michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * vim:set ts=2 sts=2 sw=2 et cin: michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsIURI.h" michael@0: #include "nsIURL.h" michael@0: #include "nsExternalProtocolHandler.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsIInterfaceRequestor.h" michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: #include "nsIStringBundle.h" michael@0: #include "nsIPrefService.h" michael@0: #include "nsIPrompt.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsExternalHelperAppService.h" michael@0: michael@0: // used to dispatch urls to default protocol handlers michael@0: #include "nsCExternalHandlerService.h" michael@0: #include "nsIExternalProtocolService.h" michael@0: michael@0: //////////////////////////////////////////////////////////////////////// michael@0: // a stub channel implemenation which will map calls to AsyncRead and OpenInputStream michael@0: // to calls in the OS for loading the url. michael@0: //////////////////////////////////////////////////////////////////////// michael@0: michael@0: class nsExtProtocolChannel : public nsIChannel michael@0: { michael@0: public: michael@0: NS_DECL_THREADSAFE_ISUPPORTS michael@0: NS_DECL_NSICHANNEL michael@0: NS_DECL_NSIREQUEST michael@0: michael@0: nsExtProtocolChannel(); michael@0: virtual ~nsExtProtocolChannel(); michael@0: michael@0: nsresult SetURI(nsIURI*); michael@0: michael@0: private: michael@0: nsresult OpenURL(); michael@0: void Finish(nsresult aResult); michael@0: michael@0: nsCOMPtr mUrl; michael@0: nsCOMPtr mOriginalURI; michael@0: nsresult mStatus; michael@0: nsLoadFlags mLoadFlags; michael@0: bool mWasOpened; michael@0: michael@0: nsCOMPtr mCallbacks; michael@0: nsCOMPtr mLoadGroup; michael@0: }; michael@0: michael@0: NS_IMPL_ADDREF(nsExtProtocolChannel) michael@0: NS_IMPL_RELEASE(nsExtProtocolChannel) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(nsExtProtocolChannel) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChannel) michael@0: NS_INTERFACE_MAP_ENTRY(nsIChannel) michael@0: NS_INTERFACE_MAP_ENTRY(nsIRequest) michael@0: NS_INTERFACE_MAP_END_THREADSAFE michael@0: michael@0: nsExtProtocolChannel::nsExtProtocolChannel() : mStatus(NS_OK), michael@0: mWasOpened(false) michael@0: { michael@0: } michael@0: michael@0: nsExtProtocolChannel::~nsExtProtocolChannel() michael@0: {} michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::GetLoadGroup(nsILoadGroup * *aLoadGroup) michael@0: { michael@0: NS_IF_ADDREF(*aLoadGroup = mLoadGroup); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::SetLoadGroup(nsILoadGroup * aLoadGroup) michael@0: { michael@0: mLoadGroup = aLoadGroup; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aCallbacks) michael@0: { michael@0: NS_IF_ADDREF(*aCallbacks = mCallbacks); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) michael@0: { michael@0: mCallbacks = aCallbacks; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsExtProtocolChannel::GetSecurityInfo(nsISupports * *aSecurityInfo) michael@0: { michael@0: *aSecurityInfo = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::GetOriginalURI(nsIURI* *aURI) michael@0: { michael@0: NS_ADDREF(*aURI = mOriginalURI); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::SetOriginalURI(nsIURI* aURI) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aURI); michael@0: mOriginalURI = aURI; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::GetURI(nsIURI* *aURI) michael@0: { michael@0: *aURI = mUrl; michael@0: NS_IF_ADDREF(*aURI); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsExtProtocolChannel::SetURI(nsIURI* aURI) michael@0: { michael@0: mUrl = aURI; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsExtProtocolChannel::OpenURL() michael@0: { michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: nsCOMPtr extProtService (do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID)); michael@0: michael@0: if (extProtService) michael@0: { michael@0: #ifdef DEBUG michael@0: nsAutoCString urlScheme; michael@0: mUrl->GetScheme(urlScheme); michael@0: bool haveHandler = false; michael@0: extProtService->ExternalProtocolHandlerExists(urlScheme.get(), &haveHandler); michael@0: NS_ASSERTION(haveHandler, "Why do we have a channel for this url if we don't support the protocol?"); michael@0: #endif michael@0: michael@0: nsCOMPtr aggCallbacks; michael@0: rv = NS_NewNotificationCallbacksAggregation(mCallbacks, mLoadGroup, michael@0: getter_AddRefs(aggCallbacks)); michael@0: if (NS_FAILED(rv)) { michael@0: goto finish; michael@0: } michael@0: michael@0: rv = extProtService->LoadURI(mUrl, aggCallbacks); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // despite success, we need to abort this channel, at the very least michael@0: // to make it clear to the caller that no on{Start,Stop}Request michael@0: // should be expected. michael@0: rv = NS_ERROR_NO_CONTENT; michael@0: } michael@0: } michael@0: michael@0: finish: michael@0: mCallbacks = 0; michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::Open(nsIInputStream **_retval) michael@0: { michael@0: return OpenURL(); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(listener); michael@0: NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); michael@0: michael@0: mWasOpened = true; michael@0: michael@0: return OpenURL(); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::GetLoadFlags(nsLoadFlags *aLoadFlags) michael@0: { michael@0: *aLoadFlags = mLoadFlags; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::SetLoadFlags(nsLoadFlags aLoadFlags) michael@0: { michael@0: mLoadFlags = aLoadFlags; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::GetContentType(nsACString &aContentType) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::SetContentType(const nsACString &aContentType) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::GetContentCharset(nsACString &aContentCharset) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::SetContentCharset(const nsACString &aContentCharset) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::GetContentDisposition(uint32_t *aContentDisposition) michael@0: { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::SetContentDisposition(uint32_t aContentDisposition) michael@0: { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename) michael@0: { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename) michael@0: { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader) michael@0: { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::GetContentLength(int64_t * aContentLength) michael@0: { michael@0: *aContentLength = -1; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsExtProtocolChannel::SetContentLength(int64_t aContentLength) michael@0: { michael@0: NS_NOTREACHED("SetContentLength"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::GetOwner(nsISupports * *aPrincipal) michael@0: { michael@0: NS_NOTREACHED("GetOwner"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::SetOwner(nsISupports * aPrincipal) michael@0: { michael@0: NS_NOTREACHED("SetOwner"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // From nsIRequest michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::GetName(nsACString &result) michael@0: { michael@0: return mUrl->GetSpec(result); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::IsPending(bool *result) michael@0: { michael@0: *result = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::GetStatus(nsresult *status) michael@0: { michael@0: *status = mStatus; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::Cancel(nsresult status) michael@0: { michael@0: mStatus = status; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::Suspend() michael@0: { michael@0: NS_NOTREACHED("Suspend"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExtProtocolChannel::Resume() michael@0: { michael@0: NS_NOTREACHED("Resume"); michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////// michael@0: // the default protocol handler implementation michael@0: ////////////////////////////////////////////////////////////////////// michael@0: michael@0: nsExternalProtocolHandler::nsExternalProtocolHandler() michael@0: { michael@0: m_schemeName = "default"; michael@0: } michael@0: michael@0: michael@0: nsExternalProtocolHandler::~nsExternalProtocolHandler() michael@0: {} michael@0: michael@0: NS_IMPL_ADDREF(nsExternalProtocolHandler) michael@0: NS_IMPL_RELEASE(nsExternalProtocolHandler) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(nsExternalProtocolHandler) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIProtocolHandler) michael@0: NS_INTERFACE_MAP_ENTRY(nsIProtocolHandler) michael@0: NS_INTERFACE_MAP_ENTRY(nsIExternalProtocolHandler) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) michael@0: NS_INTERFACE_MAP_END_THREADSAFE michael@0: michael@0: NS_IMETHODIMP nsExternalProtocolHandler::GetScheme(nsACString &aScheme) michael@0: { michael@0: aScheme = m_schemeName; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExternalProtocolHandler::GetDefaultPort(int32_t *aDefaultPort) michael@0: { michael@0: *aDefaultPort = 0; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsExternalProtocolHandler::AllowPort(int32_t port, const char *scheme, bool *_retval) michael@0: { michael@0: // don't override anything. michael@0: *_retval = false; michael@0: return NS_OK; michael@0: } michael@0: // returns TRUE if the OS can handle this protocol scheme and false otherwise. michael@0: bool nsExternalProtocolHandler::HaveExternalProtocolHandler(nsIURI * aURI) michael@0: { michael@0: bool haveHandler = false; michael@0: if (aURI) michael@0: { michael@0: nsAutoCString scheme; michael@0: aURI->GetScheme(scheme); michael@0: nsCOMPtr extProtSvc(do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID)); michael@0: if (extProtSvc) michael@0: extProtSvc->ExternalProtocolHandlerExists(scheme.get(), &haveHandler); michael@0: } michael@0: michael@0: return haveHandler; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExternalProtocolHandler::GetProtocolFlags(uint32_t *aUritype) michael@0: { michael@0: // Make it norelative since it is a simple uri michael@0: *aUritype = URI_NORELATIVE | URI_NOAUTH | URI_LOADABLE_BY_ANYONE | michael@0: URI_NON_PERSISTABLE | URI_DOES_NOT_RETURN_DATA; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExternalProtocolHandler::NewURI(const nsACString &aSpec, michael@0: const char *aCharset, // ignore charset info michael@0: nsIURI *aBaseURI, michael@0: nsIURI **_retval) michael@0: { michael@0: nsresult rv; michael@0: nsCOMPtr uri = do_CreateInstance(NS_SIMPLEURI_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = uri->SetSpec(aSpec); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: NS_ADDREF(*_retval = uri); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsExternalProtocolHandler::NewChannel(nsIURI *aURI, nsIChannel **_retval) michael@0: { michael@0: // Only try to return a channel if we have a protocol handler for the url. michael@0: // nsOSHelperAppService::LoadUriInternal relies on this to check trustedness michael@0: // for some platforms at least. (win uses ::ShellExecute and unix uses michael@0: // gnome_url_show.) michael@0: bool haveExternalHandler = HaveExternalProtocolHandler(aURI); michael@0: if (haveExternalHandler) michael@0: { michael@0: nsCOMPtr channel = new nsExtProtocolChannel(); michael@0: if (!channel) return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: ((nsExtProtocolChannel*) channel.get())->SetURI(aURI); michael@0: channel->SetOriginalURI(aURI); michael@0: michael@0: if (_retval) michael@0: { michael@0: *_retval = channel; michael@0: NS_IF_ADDREF(*_retval); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: return NS_ERROR_UNKNOWN_PROTOCOL; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////// michael@0: // External protocol handler interface implementation michael@0: ////////////////////////////////////////////////////////////////////// michael@0: NS_IMETHODIMP nsExternalProtocolHandler::ExternalAppExistsForScheme(const nsACString& aScheme, bool *_retval) michael@0: { michael@0: nsCOMPtr extProtSvc(do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID)); michael@0: if (extProtSvc) michael@0: return extProtSvc->ExternalProtocolHandlerExists( michael@0: PromiseFlatCString(aScheme).get(), _retval); michael@0: michael@0: // In case we don't have external protocol service. michael@0: *_retval = false; michael@0: return NS_OK; michael@0: }