diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index c753834957b4..6db159c11f73 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -142,6 +142,7 @@ #include "nsIMutable.h" #include "nsINSSU2FToken.h" #include "nsIObserverService.h" +#include "nsIParentChannel.h" #include "nsIPresShell.h" #include "nsIRemoteWindowContext.h" #include "nsIScriptError.h" @@ -4419,6 +4420,32 @@ ContentParent::RecvLoadURIExternal(const URIParams& uri, return true; } +bool +ContentParent::RecvExtProtocolChannelConnectParent(const uint32_t& registrarId) +{ + nsresult rv; + + // First get the real channel created before redirect on the parent. + nsCOMPtr channel; + rv = NS_LinkRedirectChannels(registrarId, nullptr, getter_AddRefs(channel)); + NS_ENSURE_SUCCESS(rv, true); + + nsCOMPtr parent = do_QueryInterface(channel, &rv); + NS_ENSURE_SUCCESS(rv, true); + + // The channel itself is its own (faked) parent, link it. + rv = NS_LinkRedirectChannels(registrarId, parent, getter_AddRefs(channel)); + NS_ENSURE_SUCCESS(rv, true); + + // Signal the parent channel that it's a redirect-to parent. This will + // make AsyncOpen on it do nothing (what we want). + // Yes, this is a bit of a hack, but I don't think it's necessary to invent + // a new interface just to set this flag on the channel. + parent->SetParentListener(nullptr); + + return true; +} + bool ContentParent::HasNotificationPermission(const IPC::Principal& aPrincipal) { diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index f6d7d7bea333..ba7881f4f34c 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -1004,6 +1004,7 @@ private: virtual bool RecvLoadURIExternal(const URIParams& uri, PBrowserParent* windowContext) override; + virtual bool RecvExtProtocolChannelConnectParent(const uint32_t& registrarId) override; virtual bool RecvSyncMessage(const nsString& aMsg, const ClonedMessageData& aData, diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index ebcbdad0e77a..64c33a1e17c3 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -833,6 +833,7 @@ parent: async SetURITitle(URIParams uri, nsString title); async LoadURIExternal(URIParams uri, PBrowser windowContext); + async ExtProtocolChannelConnectParent(uint32_t registrarId); // PrefService message sync ReadPrefsArray() returns (PrefSetting[] prefs) verify; diff --git a/uriloader/exthandler/nsExternalProtocolHandler.cpp b/uriloader/exthandler/nsExternalProtocolHandler.cpp index e9b397d14ff0..c570e2340f90 100644 --- a/uriloader/exthandler/nsExternalProtocolHandler.cpp +++ b/uriloader/exthandler/nsExternalProtocolHandler.cpp @@ -26,6 +26,8 @@ // used to dispatch urls to default protocol handlers #include "nsCExternalHandlerService.h" #include "nsIExternalProtocolService.h" +#include "nsIChildChannel.h" +#include "nsIParentChannel.h" class nsILoadInfo; @@ -34,12 +36,18 @@ class nsILoadInfo; // to calls in the OS for loading the url. //////////////////////////////////////////////////////////////////////// -class nsExtProtocolChannel : public nsIChannel +class nsExtProtocolChannel : public nsIChannel, + public nsIChildChannel, + public nsIParentChannel { public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSICHANNEL + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER NS_DECL_NSIREQUEST + NS_DECL_NSICHILDCHANNEL + NS_DECL_NSIPARENTCHANNEL nsExtProtocolChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo); @@ -54,6 +62,11 @@ private: nsresult mStatus; nsLoadFlags mLoadFlags; bool mWasOpened; + // Set true (as a result of ConnectParent invoked from child process) + // when this channel is on the parent process and is being used as + // a redirect target channel. It turns AsyncOpen into a no-op since + // we do it on the child. + bool mConnectedParent; nsCOMPtr mCallbacks; nsCOMPtr mLoadGroup; @@ -67,6 +80,10 @@ NS_INTERFACE_MAP_BEGIN(nsExtProtocolChannel) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChannel) NS_INTERFACE_MAP_ENTRY(nsIChannel) NS_INTERFACE_MAP_ENTRY(nsIRequest) + NS_INTERFACE_MAP_ENTRY(nsIChildChannel) + NS_INTERFACE_MAP_ENTRY(nsIParentChannel) + NS_INTERFACE_MAP_ENTRY(nsIStreamListener) + NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) NS_INTERFACE_MAP_END_THREADSAFE nsExtProtocolChannel::nsExtProtocolChannel(nsIURI* aURI, @@ -75,6 +92,7 @@ nsExtProtocolChannel::nsExtProtocolChannel(nsIURI* aURI, , mOriginalURI(aURI) , mStatus(NS_OK) , mWasOpened(false) + , mConnectedParent(false) , mLoadInfo(aLoadInfo) { } @@ -184,6 +202,10 @@ NS_IMETHODIMP nsExtProtocolChannel::Open2(nsIInputStream** aStream) NS_IMETHODIMP nsExtProtocolChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctxt) { + if (mConnectedParent) { + return NS_OK; + } + MOZ_ASSERT(!mLoadInfo || mLoadInfo->GetSecurityMode() == 0 || mLoadInfo->GetInitialSecurityCheckDone() || @@ -338,6 +360,81 @@ NS_IMETHODIMP nsExtProtocolChannel::Resume() return NS_ERROR_NOT_IMPLEMENTED; } +/////////////////////////////////////////////////////////////////////// +// From nsIChildChannel +////////////////////////////////////////////////////////////////////// + +NS_IMETHODIMP nsExtProtocolChannel::ConnectParent(uint32_t registrarId) +{ + mozilla::dom::ContentChild::GetSingleton()-> + SendExtProtocolChannelConnectParent(registrarId); + return NS_OK; +} + +NS_IMETHODIMP nsExtProtocolChannel::CompleteRedirectSetup(nsIStreamListener *listener, + nsISupports *context) +{ + // For redirects to external protocols we AsyncOpen on the child + // (not the parent) because child channel has the right docshell + // (which is needed for the select dialog). + return AsyncOpen(listener, context); +} + +/////////////////////////////////////////////////////////////////////// +// From nsIParentChannel (derives from nsIStreamListener) +////////////////////////////////////////////////////////////////////// + +NS_IMETHODIMP nsExtProtocolChannel::SetParentListener(HttpChannelParentListener* aListener) +{ + // This is called as part of the connect parent operation from + // ContentParent::RecvExtProtocolChannelConnectParent. Setting + // this flag tells this channel to not proceed and makes AsyncOpen + // just no-op. Actual operation will happen from the child process + // via CompleteRedirectSetup call on the child channel. + mConnectedParent = true; + return NS_OK; +} + +NS_IMETHODIMP nsExtProtocolChannel::NotifyTrackingProtectionDisabled() +{ + // nothing to do + return NS_OK; +} + +NS_IMETHODIMP nsExtProtocolChannel::Delete() +{ + // nothing to do + return NS_OK; +} + +NS_IMETHODIMP nsExtProtocolChannel::OnStartRequest(nsIRequest *aRequest, + nsISupports *aContext) +{ + // no data is expected + MOZ_CRASH("No data expected from external protocol channel"); + return NS_ERROR_UNEXPECTED; +} + +NS_IMETHODIMP nsExtProtocolChannel::OnStopRequest(nsIRequest *aRequest, + nsISupports *aContext, + nsresult aStatusCode) +{ + // no data is expected + MOZ_CRASH("No data expected from external protocol channel"); + return NS_ERROR_UNEXPECTED; +} + +NS_IMETHODIMP nsExtProtocolChannel::OnDataAvailable(nsIRequest *aRequest, + nsISupports *aContext, + nsIInputStream *aInputStream, + uint64_t aOffset, + uint32_t aCount) +{ + // no data is expected + MOZ_CRASH("No data expected from external protocol channel"); + return NS_ERROR_UNEXPECTED; +} + /////////////////////////////////////////////////////////////////////// // the default protocol handler implementation //////////////////////////////////////////////////////////////////////