Bug 1607984 - P12. Start parent load via DocumentChannel. r=mayhemer,nika,mattwoodrow,necko-reviewers

Add ParentProcessDocumentChannel object. This object is a DocumentChannel that will start a channel load from the parent process via a DocumentChannel.

The aim of this task is two-fold.
1- Be consistent on how we handle redirects before continuing the load on the final channel.
2- Prepare to initiate a process switch when needed without having to go via an intermediary content process, saving a process switch. This task will be done in a follow-up task.

The behaviour of the ParentProcessDocumentChannel is similar in logic to the DocumentChannelChild/DocumentChannelParent pair.
The ParentProcessDocumentChannel sets up a DocumentLoadListener, have it handle the redirects and upon completion continue the load on the final channel.

Differential Revision: https://phabricator.services.mozilla.com/D70009
This commit is contained in:
Jean-Yves Avenard 2020-04-22 10:27:11 +00:00
Родитель 46e166596d
Коммит fce4dd9e1f
13 изменённых файлов: 527 добавлений и 175 удалений

Просмотреть файл

@ -73,9 +73,8 @@
#include "mozilla/dom/nsCSPContext.h"
#include "mozilla/dom/LoadURIOptionsBinding.h"
#include "mozilla/dom/JSWindowActorChild.h"
#include "mozilla/net/DocumentChannel.h"
#include "nsSHEntry.h"
#include "mozilla/net/DocumentChannelChild.h"
#include "mozilla/net/DocumentChannel.h"
#include "mozilla/net/UrlClassifierFeatureFactory.h"
#include "ReferrerInfo.h"
@ -5713,7 +5712,8 @@ void nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
// of redirects handled in the parent process.
// Query the full redirect chain directly, so that we can add history
// entries for them.
if (RefPtr<DocumentChannel> docChannel = do_QueryObject(aOldChannel)) {
RefPtr<DocumentChannel> docChannel = do_QueryObject(aOldChannel);
if (docChannel) {
nsCOMPtr<nsIURI> previousURI;
uint32_t previousFlags = 0;
docChannel->GetLastVisit(getter_AddRefs(previousURI), &previousFlags);
@ -5761,7 +5761,7 @@ void nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
// check if the new load should go through the application cache.
nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel =
do_QueryInterface(aNewChannel);
if (appCacheChannel) {
if (appCacheChannel && !docChannel) {
if (GeckoProcessType_Default != XRE_GetProcessType()) {
// Permission will be checked in the parent process.
appCacheChannel->SetChooseApplicationCache(true);
@ -9188,23 +9188,6 @@ static bool IsConsideredSameOriginForUIR(nsIPrincipal* aTriggeringPrincipal,
return NS_OK;
}
// Changes here should also be made in
// E10SUtils.documentChannelPermittedForURI().
static bool URIUsesDocChannel(nsIURI* aURI) {
if (SchemeIsJavascript(aURI) || NS_IsAboutBlank(aURI)) {
return false;
}
nsCString spec = aURI->GetSpecOrDefault();
if (spec.EqualsLiteral("about:printpreview") ||
spec.EqualsLiteral("about:crashcontent")) {
return false;
}
return true;
}
/* static */ bool nsDocShell::CreateAndConfigureRealChannelForLoadState(
BrowsingContext* aBrowsingContext, nsDocShellLoadState* aLoadState,
LoadInfo* aLoadInfo, nsIInterfaceRequestor* aCallbacks,
@ -9605,8 +9588,9 @@ nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
// If we have a pending channel, use the channel we've already created here.
// We don't need to set up load flags for our channel, as it has already been
// created.
nsCOMPtr<nsIChannel> channel = aLoadState->GetPendingRedirectedChannel();
if (channel) {
if (nsCOMPtr<nsIChannel> channel =
aLoadState->GetPendingRedirectedChannel()) {
MOZ_ASSERT(!aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC),
"pending channel for srcdoc load?");
@ -9775,20 +9759,11 @@ nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
cacheKey = mOSHE->GetCacheKey();
}
// We want to use DocumentChannel if we're using a supported scheme. Sandboxed
// srcdoc loads break due to failing assertions after changing processes, and
// non-sandboxed srcdoc loads need to share the same principal object as their
// outer document (and must load in the same process), which breaks if we
// serialize to the parent process.
bool canUseDocumentChannel =
!aLoadState->HasLoadFlags(INTERNAL_LOAD_FLAGS_IS_SRCDOC) &&
URIUsesDocChannel(aLoadState->URI());
if (StaticPrefs::browser_tabs_documentchannel() && XRE_IsContentProcess() &&
canUseDocumentChannel) {
channel =
new DocumentChannelChild(aLoadState, loadInfo, loadFlags, cacheKey);
channel->SetNotificationCallbacks(this);
nsCOMPtr<nsIChannel> channel;
if (DocumentChannel::CanUseDocumentChannel(aLoadState)) {
channel = DocumentChannel::CreateDocumentChannel(aLoadState, loadInfo,
loadFlags, this, cacheKey);
MOZ_ASSERT(channel);
} else if (!CreateAndConfigureRealChannelForLoadState(
mBrowsingContext, aLoadState, loadInfo, this, this,
GetOriginAttributes(), loadFlags, cacheKey, rv,

Просмотреть файл

@ -95,7 +95,7 @@ var RequestWatcher = {
// We may also see, eg, chrome://global/skin/icons/resizer.svg, so
// skip chrome:// URLs too.
if (req.name.startsWith("about:") || req.name.startsWith("resource:") ||
req.name.startsWith("chrome:")) {
req.name.startsWith("chrome:") || req.name.startsWith("documentchannel:")) {
return;
}
is(req.loadFlags & TEST_FLAGS, TEST_FLAGS, "request " + req.name + " has the expected flags");

Просмотреть файл

@ -47,6 +47,7 @@ class ADocumentChannelBridge {
uint32_t aRedirectFlags, uint32_t aLoadFlags) = 0;
// Returns the process id that this bridge is connected to.
// If 0 indicates that the load is started from the parent process.
virtual base::ProcessId OtherPid() const = 0;
protected:

Просмотреть файл

@ -10,20 +10,24 @@
#include "SerializedLoadContext.h"
#include "mozIThirdPartyUtil.h"
#include "mozilla/LoadInfo.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/nsCSPContext.h"
#include "mozilla/extensions/StreamFilterParent.h"
#include "mozilla/ipc/IPCStreamUtils.h"
#include "mozilla/ipc/URIUtils.h"
#include "mozilla/net/DocumentChannelChild.h"
#include "mozilla/net/HttpChannelChild.h"
#include "mozilla/net/NeckoChild.h"
#include "mozilla/net/ParentProcessDocumentChannel.h"
#include "mozilla/net/UrlClassifierCommon.h"
#include "nsContentSecurityManager.h"
#include "nsDocShell.h"
#include "nsDocShellLoadState.h"
#include "nsHttpHandler.h"
#include "nsIInputStreamChannel.h"
#include "nsNetUtil.h"
#include "nsQueryObject.h"
#include "nsSerializationHelper.h"
#include "nsStreamListenerWrapper.h"
@ -77,6 +81,48 @@ DocumentChannel::AsyncOpen(nsIStreamListener* aListener) {
return NS_ERROR_NOT_IMPLEMENTED;
}
void DocumentChannel::ShutdownListeners(nsresult aStatusCode) {
LOG(("DocumentChannel ShutdownListeners [this=%p, status=%" PRIx32 "]", this,
static_cast<uint32_t>(aStatusCode)));
mStatus = aStatusCode;
nsCOMPtr<nsIStreamListener> listener = mListener;
if (listener) {
listener->OnStartRequest(this);
}
mIsPending = false;
listener = mListener; // it might have changed!
if (listener) {
listener->OnStopRequest(this, aStatusCode);
}
mListener = nullptr;
mCallbacks = nullptr;
if (mLoadGroup) {
mLoadGroup->RemoveRequest(this, nullptr, aStatusCode);
mLoadGroup = nullptr;
}
DeleteIPDL();
}
void DocumentChannel::DisconnectChildListeners(
const nsresult& aStatus, const nsresult& aLoadGroupStatus) {
MOZ_ASSERT(NS_FAILED(aStatus));
mStatus = aLoadGroupStatus;
// Make sure we remove from the load group before
// setting mStatus, as existing tests expect the
// status to be successful when we disconnect.
if (mLoadGroup) {
mLoadGroup->RemoveRequest(this, nullptr, aStatus);
mLoadGroup = nullptr;
}
ShutdownListeners(aStatus);
}
nsDocShell* DocumentChannel::GetDocShell() {
nsCOMPtr<nsILoadContext> loadContext;
NS_QueryNotificationCallbacks(this, loadContext);
@ -93,6 +139,47 @@ nsDocShell* DocumentChannel::GetDocShell() {
return nsDocShell::Cast(docshell);
}
// Changes here should also be made in
// E10SUtils.documentChannelPermittedForURI().
static bool URIUsesDocChannel(nsIURI* aURI) {
if (SchemeIsJavascript(aURI) || NS_IsAboutBlank(aURI)) {
return false;
}
nsCString spec = aURI->GetSpecOrDefault();
return !spec.EqualsLiteral("about:printpreview") &&
!spec.EqualsLiteral("about:crashcontent");
}
/* static */
already_AddRefed<DocumentChannel> DocumentChannel::CreateDocumentChannel(
nsDocShellLoadState* aLoadState, class LoadInfo* aLoadInfo,
nsLoadFlags aLoadFlags, nsIInterfaceRequestor* aNotificationCallbacks,
uint32_t aCacheKey) {
RefPtr<DocumentChannel> channel;
if (XRE_IsContentProcess()) {
channel =
new DocumentChannelChild(aLoadState, aLoadInfo, aLoadFlags, aCacheKey);
} else {
channel = new ParentProcessDocumentChannel(aLoadState, aLoadInfo,
aLoadFlags, aCacheKey);
}
channel->SetNotificationCallbacks(aNotificationCallbacks);
return channel.forget();
}
bool DocumentChannel::CanUseDocumentChannel(nsDocShellLoadState* aLoadState) {
MOZ_ASSERT(aLoadState);
// We want to use DocumentChannel if we're using a supported scheme. Sandboxed
// srcdoc loads break due to failing assertions after changing processes, and
// non-sandboxed srcdoc loads need to share the same principal object as their
// outer document (and must load in the same process), which breaks if we
// serialize to the parent process.
return StaticPrefs::browser_tabs_documentchannel() &&
!aLoadState->HasLoadFlags(nsDocShell::INTERNAL_LOAD_FLAGS_IS_SRCDOC) &&
URIUsesDocChannel(aLoadState->URI());
}
//-----------------------------------------------------------------------------
// DocumentChannel::nsITraceableChannel
//-----------------------------------------------------------------------------
@ -100,6 +187,8 @@ nsDocShell* DocumentChannel::GetDocShell() {
NS_IMETHODIMP
DocumentChannel::SetNewListener(nsIStreamListener* aListener,
nsIStreamListener** _retval) {
LOG(("DocumentChannel SetNewListener [this=%p, aListener=%p]", this,
aListener));
NS_ENSURE_ARG_POINTER(aListener);
nsCOMPtr<nsIStreamListener> wrapper = new nsStreamListenerWrapper(mListener);

Просмотреть файл

@ -8,12 +8,12 @@
#ifndef mozilla_net_DocumentChannel_h
#define mozilla_net_DocumentChannel_h
#include "mozilla/net/PDocumentChannelChild.h"
#include "mozilla/dom/ClientInfo.h"
#include "mozilla/net/NeckoChannelParams.h"
#include "nsDOMNavigationTiming.h"
#include "nsIChannel.h"
#include "nsIChildChannel.h"
#include "nsITraceableChannel.h"
#include "mozilla/dom/ClientInfo.h"
#define DOCUMENT_CHANNEL_IID \
{ \
@ -45,9 +45,6 @@ class DocumentChannel : public nsIIdentChannel, public nsITraceableChannel {
NS_DECLARE_STATIC_IID_ACCESSOR(DOCUMENT_CHANNEL_IID)
DocumentChannel(nsDocShellLoadState* aLoadState, class LoadInfo* aLoadInfo,
nsLoadFlags aLoadFlags, uint32_t aCacheKey);
const nsTArray<DocumentChannelRedirect>& GetRedirectChain() const {
return mRedirects;
}
@ -65,7 +62,28 @@ class DocumentChannel : public nsIIdentChannel, public nsITraceableChannel {
mInitialClientInfo = aInfo;
}
/**
* Will create the appropriate document channel:
* Either a DocumentChannelChild if called from the content process or
* a ParentProcessDocumentChannel if called from the parent process.
* This operation is infallible.
*/
static already_AddRefed<DocumentChannel> CreateDocumentChannel(
nsDocShellLoadState* aLoadState, class LoadInfo* aLoadInfo,
nsLoadFlags aLoadFlags, nsIInterfaceRequestor* aNotificationCallbacks,
uint32_t aCacheKey);
static bool CanUseDocumentChannel(nsDocShellLoadState* aLoadState);
protected:
DocumentChannel(nsDocShellLoadState* aLoadState, class LoadInfo* aLoadInfo,
nsLoadFlags aLoadFlags, uint32_t aCacheKey);
void ShutdownListeners(nsresult aStatusCode);
void DisconnectChildListeners(const nsresult& aStatus,
const nsresult& aLoadGroupStatus);
virtual void DeleteIPDL() {}
nsDocShell* GetDocShell();
virtual ~DocumentChannel() = default;

Просмотреть файл

@ -128,48 +128,9 @@ IPCResult DocumentChannelChild::RecvFailedAsyncOpen(
return IPC_OK();
}
void DocumentChannelChild::ShutdownListeners(nsresult aStatusCode) {
LOG(("DocumentChannelChild ShutdownListeners [this=%p, status=%" PRIx32 "]",
this, static_cast<uint32_t>(aStatusCode)));
mStatus = aStatusCode;
nsCOMPtr<nsIStreamListener> l = mListener;
if (l) {
l->OnStartRequest(this);
}
mIsPending = false;
l = mListener; // it might have changed!
if (l) {
l->OnStopRequest(this, aStatusCode);
}
mListener = nullptr;
mCallbacks = nullptr;
if (mLoadGroup) {
mLoadGroup->RemoveRequest(this, nullptr, aStatusCode);
mLoadGroup = nullptr;
}
if (CanSend()) {
Send__delete__(this);
}
}
IPCResult DocumentChannelChild::RecvDisconnectChildListeners(
const nsresult& aStatus, const nsresult& aLoadGroupStatus) {
MOZ_ASSERT(NS_FAILED(aStatus));
mStatus = aLoadGroupStatus;
// Make sure we remove from the load group before
// setting mStatus, as existing tests expect the
// status to be successful when we disconnect.
if (mLoadGroup) {
mLoadGroup->RemoveRequest(this, nullptr, aStatus);
mLoadGroup = nullptr;
}
ShutdownListeners(aStatus);
DisconnectChildListeners(aStatus, aLoadGroupStatus);
return IPC_OK();
}

Просмотреть файл

@ -48,7 +48,11 @@ class DocumentChannelChild final : public DocumentChannel,
RedirectToRealChannelResolver&& aResolve);
private:
void ShutdownListeners(nsresult aStatusCode);
void DeleteIPDL() override {
if (CanSend()) {
Send__delete__(this);
}
}
~DocumentChannelChild();

Просмотреть файл

@ -13,7 +13,8 @@
namespace mozilla {
namespace dom {
class CanonicalBrowsingContext;
}
class BrowserParent;
} // namespace dom
namespace net {
/**
@ -61,7 +62,7 @@ class DocumentChannelParent final : public ADocumentChannelBridge,
}
}
virtual ProcessId OtherPid() const override { return IProtocol::OtherPid(); }
ProcessId OtherPid() const override { return IProtocol::OtherPid(); }
RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
RedirectToRealChannel(
@ -69,7 +70,7 @@ class DocumentChannelParent final : public ADocumentChannelBridge,
aStreamFilterEndpoints,
uint32_t aRedirectFlags, uint32_t aLoadFlags) override;
~DocumentChannelParent();
virtual ~DocumentChannelParent();
RefPtr<DocumentLoadListener> mParent;
};

Просмотреть файл

@ -6,44 +6,36 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DocumentLoadListener.h"
#include "mozilla/AntiTrackingUtils.h"
#include "mozilla/ContentBlockingAllowList.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/LoadInfo.h"
#include "mozilla/MozPromiseInlines.h" // For MozPromise::FromDomPromise
#include "mozilla/StaticPrefs_security.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/ClientChannelHelper.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/ContentProcessManager.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/dom/ipc/IdType.h"
#include "mozilla/dom/ServiceWorkerManager.h"
#include "mozilla/net/CookieJarSettings.h"
#include "mozilla/net/HttpChannelParent.h"
#include "mozilla/net/RedirectChannelRegistrar.h"
#include "mozilla/net/UrlClassifierCommon.h"
#include "nsContentSecurityUtils.h"
#include "nsDocShell.h"
#include "nsDocShellLoadState.h"
#include "nsContentSecurityUtils.h"
#include "nsHttpChannel.h"
#include "nsISecureBrowserUI.h"
#include "nsRedirectHistoryEntry.h"
#include "nsSerializationHelper.h"
#include "nsIPrompt.h"
#include "nsIWindowWatcher.h"
#include "nsIURIContentListener.h"
#include "nsWebNavigationInfo.h"
#include "nsURILoader.h"
#include "nsIStreamConverterService.h"
#include "nsExternalHelperAppService.h"
#include "nsCExternalHandlerService.h"
#include "nsMimeTypes.h"
#include "nsIViewSourceChannel.h"
#include "nsIOService.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/StaticPrefs_security.h"
#include "nsICookieService.h"
#include "nsHttpChannel.h"
#include "nsIBrowser.h"
#include "nsIE10SUtils.h"
#include "nsIStreamConverterService.h"
#include "nsIViewSourceChannel.h"
#include "nsImportModule.h"
#include "nsMimeTypes.h"
#include "nsRedirectHistoryEntry.h"
#include "nsURILoader.h"
#include "nsWebNavigationInfo.h"
#ifdef ANDROID
# include "mozilla/widget/nsWindow.h"
@ -248,11 +240,10 @@ NS_INTERFACE_MAP_END
DocumentLoadListener::DocumentLoadListener(
CanonicalBrowsingContext* aBrowsingContext, nsILoadContext* aLoadContext,
ADocumentChannelBridge* aBridge)
: mLoadContext(aLoadContext) {
: mDocumentChannelBridge(aBridge), mLoadContext(aLoadContext) {
LOG(("DocumentLoadListener ctor [this=%p]", this));
mParentChannelListener = new ParentChannelListener(
this, aBrowsingContext, aLoadContext->UsePrivateBrowsing());
mDocumentChannelBridge = aBridge;
}
DocumentLoadListener::~DocumentLoadListener() {
@ -440,8 +431,8 @@ bool DocumentLoadListener::Open(
// OnStart/StopRequest with itself. We don't need this, and instead
// we want the original request so that we get different ones for
// each part of a multipart channel.
if (nsCOMPtr<nsIViewSourceChannel> viewSourceChannel =
do_QueryInterface(mChannel)) {
nsCOMPtr<nsIViewSourceChannel> viewSourceChannel;
if (OtherPid() && (viewSourceChannel = do_QueryInterface(mChannel))) {
viewSourceChannel->SetReplaceRequest(false);
}
@ -570,6 +561,11 @@ void DocumentLoadListener::RedirectToRealChannelFinished(nsresult aRv) {
return;
}
if (!mRedirectChannelId) {
FinishReplacementChannelSetup(true);
return;
}
// Wait for background channel ready on target channel
nsCOMPtr<nsIRedirectChannelRegistrar> redirectReg =
RedirectChannelRegistrar::GetOrCreate();
@ -608,42 +604,48 @@ void DocumentLoadListener::FinishReplacementChannelSetup(bool aSucceeded) {
("DocumentLoadListener FinishReplacementChannelSetup [this=%p, "
"aSucceeded=%d]",
this, aSucceeded));
nsresult rv;
if (mDoingProcessSwitch) {
DisconnectChildListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED);
}
nsCOMPtr<nsIParentChannel> redirectChannel;
if (mRedirectChannelId) {
nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
RedirectChannelRegistrar::GetOrCreate();
MOZ_ASSERT(registrar);
rv = registrar->GetParentChannel(mRedirectChannelId,
getter_AddRefs(redirectChannel));
if (NS_FAILED(rv) || !redirectChannel) {
// Redirect might get canceled before we got AsyncOnChannelRedirect
nsCOMPtr<nsIChannel> newChannel;
rv = registrar->GetRegisteredChannel(mRedirectChannelId,
getter_AddRefs(newChannel));
MOZ_ASSERT(newChannel, "Already registered channel not found");
if (NS_SUCCEEDED(rv)) {
newChannel->Cancel(NS_BINDING_ABORTED);
}
if (!mRedirectChannelId) {
if (!aSucceeded) {
mChannel->Resume();
return;
}
// Release all previously registered channels, they are no longer needed to
// be kept in the registrar from this moment.
registrar->DeregisterChannels(mRedirectChannelId);
mRedirectChannelId = 0;
ApplyPendingFunctions(mChannel);
// ResumeSuspendedChannel will be called later as RedirectToRealChannel
// continues, so we can return early.
return;
}
if (!redirectChannel) {
aSucceeded = false;
nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
RedirectChannelRegistrar::GetOrCreate();
MOZ_ASSERT(registrar);
nsCOMPtr<nsIParentChannel> redirectChannel;
nsresult rv = registrar->GetParentChannel(mRedirectChannelId,
getter_AddRefs(redirectChannel));
if (NS_FAILED(rv) || !redirectChannel) {
// Redirect might get canceled before we got AsyncOnChannelRedirect
nsCOMPtr<nsIChannel> newChannel;
rv = registrar->GetRegisteredChannel(mRedirectChannelId,
getter_AddRefs(newChannel));
MOZ_ASSERT(newChannel, "Already registered channel not found");
if (NS_SUCCEEDED(rv)) {
newChannel->Cancel(NS_BINDING_ABORTED);
}
if (!redirectChannel) {
aSucceeded = false;
}
}
// Release all previously registered channels, they are no longer needed to
// be kept in the registrar from this moment.
registrar->DeregisterChannels(mRedirectChannelId);
mRedirectChannelId = 0;
if (!aSucceeded) {
if (redirectChannel) {
redirectChannel->Delete();
@ -656,36 +658,85 @@ void DocumentLoadListener::FinishReplacementChannelSetup(bool aSucceeded) {
!SameCOMIdentity(redirectChannel, static_cast<nsIParentChannel*>(this)));
Delete();
if (!mIsFinished) {
mParentChannelListener->SetListenerAfterRedirect(redirectChannel);
}
redirectChannel->SetParentListener(mParentChannelListener);
ApplyPendingFunctions(redirectChannel);
ResumeSuspendedChannel(redirectChannel);
}
void DocumentLoadListener::ApplyPendingFunctions(nsISupports* aChannel) const {
// We stored the values from all nsIParentChannel functions called since we
// couldn't handle them. Copy them across to the real channel since it should
// know what to do.
for (auto& variant : mIParentChannelFunctions) {
variant.match(
[redirectChannel](const nsIHttpChannel::FlashPluginState& aState) {
redirectChannel->NotifyFlashPluginStateChanged(aState);
},
[redirectChannel](const ClassifierMatchedInfoParams& aParams) {
redirectChannel->SetClassifierMatchedInfo(
aParams.mList, aParams.mProvider, aParams.mFullHash);
},
[redirectChannel](const ClassifierMatchedTrackingInfoParams& aParams) {
redirectChannel->SetClassifierMatchedTrackingInfo(
aParams.mLists, aParams.mFullHashes);
},
[redirectChannel](const ClassificationFlagsParams& aParams) {
redirectChannel->NotifyClassificationFlags(
aParams.mClassificationFlags, aParams.mIsThirdParty);
});
// couldn't handle them. Copy them across to the real channel since it
// should know what to do.
nsCOMPtr<nsIParentChannel> parentChannel = do_QueryInterface(aChannel);
if (parentChannel) {
for (auto& variant : mIParentChannelFunctions) {
variant.match(
[parentChannel](const nsIHttpChannel::FlashPluginState& aState) {
parentChannel->NotifyFlashPluginStateChanged(aState);
},
[parentChannel](const ClassifierMatchedInfoParams& aParams) {
parentChannel->SetClassifierMatchedInfo(
aParams.mList, aParams.mProvider, aParams.mFullHash);
},
[parentChannel](const ClassifierMatchedTrackingInfoParams& aParams) {
parentChannel->SetClassifierMatchedTrackingInfo(
aParams.mLists, aParams.mFullHashes);
},
[parentChannel](const ClassificationFlagsParams& aParams) {
parentChannel->NotifyClassificationFlags(
aParams.mClassificationFlags, aParams.mIsThirdParty);
});
}
} else {
for (auto& variant : mIParentChannelFunctions) {
variant.match(
[&](const nsIHttpChannel::FlashPluginState& aState) {
// For now, only HttpChannel use this attribute.
RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(aChannel);
if (httpChannel) {
httpChannel->SetFlashPluginState(aState);
}
},
[&](const ClassifierMatchedInfoParams& aParams) {
nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
do_QueryInterface(aChannel);
if (classifiedChannel) {
classifiedChannel->SetMatchedInfo(
aParams.mList, aParams.mProvider, aParams.mFullHash);
}
},
[&](const ClassifierMatchedTrackingInfoParams& aParams) {
nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
do_QueryInterface(aChannel);
if (classifiedChannel) {
nsTArray<nsCString> lists, fullhashes;
for (const nsACString& token : aParams.mLists.Split(',')) {
lists.AppendElement(token);
}
for (const nsACString& token : aParams.mFullHashes.Split(',')) {
fullhashes.AppendElement(token);
}
classifiedChannel->SetMatchedTrackingInfo(lists, fullhashes);
}
},
[&](const ClassificationFlagsParams& aParams) {
UrlClassifierCommon::SetClassificationFlagsHelper(
static_cast<nsIChannel*>(aChannel),
aParams.mClassificationFlags, aParams.mIsThirdParty);
});
}
}
RefPtr<HttpChannelParent> httpParent = do_QueryObject(redirectChannel);
if (httpParent) {
RefPtr<HttpChannelSecurityWarningReporter> reporter = httpParent;
RefPtr<HttpChannelSecurityWarningReporter> reporter;
if (RefPtr<HttpChannelParent> httpParent = do_QueryObject(aChannel)) {
reporter = httpParent;
} else if (RefPtr<nsHttpChannel> httpChannel = do_QueryObject(aChannel)) {
reporter = httpChannel->GetWarningReporter();
}
if (reporter) {
for (auto& variant : mSecurityWarningFunctions) {
variant.match(
[reporter](const ReportSecurityMessageParams& aParams) {
@ -703,11 +754,9 @@ void DocumentLoadListener::FinishReplacementChannelSetup(bool aSucceeded) {
});
}
}
ResumeSuspendedChannel(redirectChannel);
}
void DocumentLoadListener::ResumeSuspendedChannel(
bool DocumentLoadListener::ResumeSuspendedChannel(
nsIStreamListener* aListener) {
LOG(("DocumentLoadListener ResumeSuspendedChannel [this=%p]", this));
RefPtr<nsHttpChannel> httpChannel = do_QueryObject(mChannel);
@ -715,11 +764,18 @@ void DocumentLoadListener::ResumeSuspendedChannel(
httpChannel->SetApplyConversion(mOldApplyConversion);
}
if (!mIsFinished) {
mParentChannelListener->SetListenerAfterRedirect(aListener);
}
// If we failed to suspend the channel, then we might have received
// some messages while the redirected was being handled.
// Manually send them on now.
nsTArray<StreamListenerFunction> streamListenerFunctions =
std::move(mStreamListenerFunctions);
if (!aListener) {
streamListenerFunctions.Clear();
}
nsresult rv = NS_OK;
for (auto& variant : streamListenerFunctions) {
variant.match(
@ -775,6 +831,7 @@ void DocumentLoadListener::ResumeSuspendedChannel(
"Should not have added new stream listener function!");
mChannel->Resume();
return !mIsFinished;
}
void DocumentLoadListener::SerializeRedirectData(
@ -1089,6 +1146,15 @@ DocumentLoadListener::RedirectToRealChannel(
MOZ_ALWAYS_SUCCEEDS(
registrar->RegisterChannel(mChannel, &mRedirectChannelId));
if (aDestinationProcess || OtherPid()) {
// Register the new channel and obtain id for it
nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
RedirectChannelRegistrar::GetOrCreate();
MOZ_ASSERT(registrar);
MOZ_ALWAYS_SUCCEEDS(
registrar->RegisterChannel(mChannel, &mRedirectChannelId));
}
if (aDestinationProcess) {
dom::ContentParent* cp =
dom::ContentProcessManager::GetSingleton()->GetContentProcessById(
@ -1426,16 +1492,13 @@ NS_IMETHODIMP
DocumentLoadListener::AsyncOnChannelRedirect(
nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
nsIAsyncVerifyRedirectCallback* aCallback) {
LOG(("DocumentLoadListener AsyncOnChannelRedirect [this=%p, aFlags=%" PRIx32
"]",
this, aFlags));
// We generally don't want to notify the content process about redirects,
// so just update our channel and tell the callback that we're good to go.
mChannel = aNewChannel;
// We need the original URI of the current channel to use to open the real
// channel in the content process. Unfortunately we overwrite the original
// uri of the new channel with the original pre-redirect URI, so grab
// a copy of it now.
aNewChannel->GetOriginalURI(getter_AddRefs(mChannelCreationURI));
// Since we're redirecting away from aOldChannel, we should check if it
// had a COOP mismatch, since we want the final result for this to
// include the state of all channels we redirected through.
@ -1448,6 +1511,10 @@ DocumentLoadListener::AsyncOnChannelRedirect(
// We don't need to confirm internal redirects or record any
// history for them, so just immediately verify and return.
if (aFlags & nsIChannelEventSink::REDIRECT_INTERNAL) {
LOG(
("DocumentLoadListener AsyncOnChannelRedirect [this=%p] "
"flags=REDIRECT_INTERNAL",
this));
aCallback->OnRedirectVerifyCallback(NS_OK);
return NS_OK;
} else {
@ -1460,11 +1527,21 @@ DocumentLoadListener::AsyncOnChannelRedirect(
mRedirects.AppendElement(DocumentChannelRedirect{
oldURI, aFlags, responseStatus, net::ChannelIsPost(aOldChannel)});
}
LOG(
("DocumentLoadListener AsyncOnChannelRedirect [this=%p] "
"mRedirects=%" PRIx32,
this, uint32_t(mRedirects.Length())));
if (!mDocumentChannelBridge) {
return NS_BINDING_ABORTED;
}
// We need the original URI of the current channel to use to open the real
// channel in the content process. Unfortunately we overwrite the original
// uri of the new channel with the original pre-redirect URI, so grab
// a copy of it now.
aNewChannel->GetOriginalURI(getter_AddRefs(mChannelCreationURI));
// Clear out our nsIParentChannel functions, since a normal parent
// channel would actually redirect and not have those values on the new one.
// We expect the URI classifier to run on the redirected channel with
@ -1535,14 +1612,14 @@ DocumentLoadListener::AsyncOnChannelRedirect(
// This method returns the cached result of running the Cross-Origin-Opener
// policy compare algorithm by calling ComputeCrossOriginOpenerPolicyMismatch
bool DocumentLoadListener::HasCrossOriginOpenerPolicyMismatch() {
bool DocumentLoadListener::HasCrossOriginOpenerPolicyMismatch() const {
// If we found a COOP mismatch on an earlier channel and then
// redirected away from that, we should use that result.
if (mHasCrossOriginOpenerPolicyMismatch) {
return true;
}
RefPtr<nsHttpChannel> httpChannel = do_QueryObject(mChannel);
RefPtr<nsHttpChannel> httpChannel = do_QueryObject(mChannel.get());
if (!httpChannel) {
// Not an nsHttpChannel assume it's okay to switch.
return false;

Просмотреть файл

@ -114,12 +114,15 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
// This helper resumes the underlying channel again, and manually
// forwards any nsIStreamListener messages that arrived while we
// were suspended (which might have failed).
void ResumeSuspendedChannel(nsIStreamListener* aListener);
// Returns true if the channel was finished before we could resume it.
bool ResumeSuspendedChannel(nsIStreamListener* aListener);
NS_DECLARE_STATIC_IID_ACCESSOR(DOCUMENT_LOAD_LISTENER_IID)
void Cancel(const nsresult& status);
nsIChannel* GetChannel() const { return mChannel; }
nsresult ReportSecurityMessage(const nsAString& aMessageTag,
const nsAString& aMessageCategory) override {
ReportSecurityMessageParams params;
@ -221,7 +224,8 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
dom::CanonicalBrowsingContext* aBrowsingContext,
nsDocShellLoadState* aLoadState, uint64_t aOuterWindowId);
bool HasCrossOriginOpenerPolicyMismatch();
bool HasCrossOriginOpenerPolicyMismatch() const;
void ApplyPendingFunctions(nsISupports* aChannel) const;
// This defines a variant that describes all the attribute setters (and their
// parameters) from nsIParentChannel

Просмотреть файл

@ -0,0 +1,158 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ParentProcessDocumentChannel.h"
extern mozilla::LazyLogModule gDocumentChannelLog;
#define LOG(fmt) MOZ_LOG(gDocumentChannelLog, mozilla::LogLevel::Verbose, fmt)
namespace mozilla {
namespace net {
NS_IMPL_ISUPPORTS_INHERITED(ParentProcessDocumentChannel, DocumentChannel,
nsIAsyncVerifyRedirectCallback)
ParentProcessDocumentChannel::ParentProcessDocumentChannel(
nsDocShellLoadState* aLoadState, class LoadInfo* aLoadInfo,
nsLoadFlags aLoadFlags, uint32_t aCacheKey)
: DocumentChannel(aLoadState, aLoadInfo, aLoadFlags, aCacheKey) {
LOG(("ParentProcessDocumentChannel ctor [this=%p]", this));
}
ParentProcessDocumentChannel::~ParentProcessDocumentChannel() {
LOG(("ParentProcessDocumentChannel dtor [this=%p]", this));
}
RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
ParentProcessDocumentChannel::RedirectToRealChannel(
nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&&
aStreamFilterEndpoints,
uint32_t aRedirectFlags, uint32_t aLoadFlags) {
LOG(("ParentProcessDocumentChannel RedirectToRealChannel [this=%p]", this));
nsCOMPtr<nsIChannel> channel = mDocumentLoadListener->GetChannel();
channel->SetLoadFlags(aLoadFlags);
channel->SetNotificationCallbacks(mCallbacks);
if (mLoadGroup) {
channel->SetLoadGroup(mLoadGroup);
}
mLastVisitInfo = mDocumentLoadListener->LastVisitInfo();
mRedirects = mDocumentLoadListener->Redirects();
mStreamFilterEndpoints = std::move(aStreamFilterEndpoints);
RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise> p =
mPromise.Ensure(__func__);
nsresult rv =
gHttpHandler->AsyncOnChannelRedirect(this, channel, aRedirectFlags);
if (NS_FAILED(rv)) {
OnRedirectVerifyCallback(rv);
}
return p;
}
NS_IMETHODIMP
ParentProcessDocumentChannel::OnRedirectVerifyCallback(nsresult aResult) {
LOG(
("ParentProcessDocumentChannel OnRedirectVerifyCallback [this=%p "
"aResult=%d]",
this, int(aResult)));
MOZ_ASSERT(mDocumentLoadListener);
if (NS_FAILED(aResult)) {
Cancel(aResult);
} else if (mCanceled && NS_SUCCEEDED(aResult)) {
aResult = NS_BINDING_ABORTED;
} else {
const nsCOMPtr<nsIChannel> channel = mDocumentLoadListener->GetChannel();
mLoadGroup->AddRequest(channel, nullptr);
// Adding the channel to the loadgroup could have triggered a status
// change with an observer being called destroying the docShell, resulting
// in the PPDC to be canceled.
if (!mCanceled) {
mLoadGroup->RemoveRequest(this, nullptr, NS_BINDING_REDIRECTED);
for (auto& endpoint : mStreamFilterEndpoints) {
extensions::StreamFilterParent::Attach(channel, std::move(endpoint));
}
if (!mDocumentLoadListener->ResumeSuspendedChannel(mListener)) {
// We added ourselves to the load group, but attempting
// to resume has notified us that the channel is already
// finished. Better remove ourselves from the loadgroup
// again.
nsresult status = NS_OK;
channel->GetStatus(&status);
mLoadGroup->RemoveRequest(channel, nullptr, status);
}
}
}
mLoadGroup = nullptr;
mListener = nullptr;
mCallbacks = nullptr;
mDocumentLoadListener->DocumentChannelBridgeDisconnected();
mDocumentLoadListener = nullptr;
mPromise.ResolveIfExists(aResult, __func__);
return NS_OK;
}
NS_IMETHODIMP ParentProcessDocumentChannel::AsyncOpen(
nsIStreamListener* aListener) {
LOG(("ParentProcessDocumentChannel AsyncOpen [this=%p]", this));
nsCOMPtr<nsILoadContext> loadContext;
NS_QueryNotificationCallbacks(this, loadContext);
mDocumentLoadListener = new DocumentLoadListener(
GetDocShell()->GetBrowsingContext()->Canonical(), loadContext, this);
LOG(("Created PPDocumentChannel with listener=%p",
mDocumentLoadListener.get()));
gHttpHandler->OnOpeningDocumentRequest(this);
nsresult rv = NS_OK;
Maybe<dom::ClientInfo> initialClientInfo = mInitialClientInfo;
if (!mDocumentLoadListener->Open(
mLoadState, mLoadFlags, mCacheKey, mChannelId, mAsyncOpenTime,
mTiming, std::move(initialClientInfo), mLoadInfo->GetOuterWindowID(),
GetDocShell()
->GetBrowsingContext()
->HasValidTransientUserGestureActivation(),
&rv)) {
MOZ_ASSERT(NS_FAILED(rv));
mDocumentLoadListener->DocumentChannelBridgeDisconnected();
mDocumentLoadListener = nullptr;
return rv;
}
mListener = aListener;
if (mLoadGroup) {
mLoadGroup->AddRequest(this, nullptr);
}
return NS_OK;
}
NS_IMETHODIMP ParentProcessDocumentChannel::Cancel(nsresult aStatus) {
LOG(("ParentProcessDocumentChannel Cancel [this=%p]", this));
if (mCanceled) {
return NS_OK;
}
mCanceled = true;
mDocumentLoadListener->Cancel(aStatus);
ShutdownListeners(aStatus);
return NS_OK;
}
} // namespace net
} // namespace mozilla
#undef LOG

Просмотреть файл

@ -0,0 +1,62 @@
/* vim: set sw=2 ts=8 et tw=80 : */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_net_ParentProcessDocumentChannel_h
#define mozilla_net_ParentProcessDocumentChannel_h
#include "ProtocolUtils.h"
#include "mozilla/net/ADocumentChannelBridge.h"
#include "mozilla/net/DocumentChannel.h"
#include "mozilla/net/DocumentLoadListener.h"
namespace mozilla {
namespace net {
class ParentProcessDocumentChannel : public DocumentChannel,
public nsIAsyncVerifyRedirectCallback,
public ADocumentChannelBridge {
public:
ParentProcessDocumentChannel(nsDocShellLoadState* aLoadState,
class LoadInfo* aLoadInfo,
nsLoadFlags aLoadFlags, uint32_t aCacheKey);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
NS_IMETHOD AsyncOpen(nsIStreamListener* aListener) override;
NS_IMETHOD Cancel(nsresult aStatusCode) override;
RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
RedirectToRealChannel(
nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&&
aStreamFilterEndpoints,
uint32_t aRedirectFlags, uint32_t aLoadFlags) override;
void DisconnectChildListeners(nsresult aStatus,
nsresult aLoadGroupStatus) override {
DocumentChannel::DisconnectChildListeners(aStatus, aLoadGroupStatus);
mDocumentLoadListener = nullptr;
}
void Delete() override {}
void DeleteIPDL() override {
mPromise.ResolveIfExists(NS_BINDING_ABORTED, __func__);
}
base::ProcessId OtherPid() const override { return 0; }
private:
virtual ~ParentProcessDocumentChannel();
RefPtr<DocumentLoadListener> mDocumentLoadListener;
nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>
mStreamFilterEndpoints;
MozPromiseHolder<PDocumentChannelParent::RedirectToRealChannelPromise>
mPromise;
};
} // namespace net
} // namespace mozilla
#endif // mozilla_net_ParentProcessDocumentChannel_h

Просмотреть файл

@ -18,6 +18,7 @@ EXPORTS.mozilla.net += [
'NeckoMessageUtils.h',
'NeckoParent.h',
'NeckoTargetHolder.h',
'ParentProcessDocumentChannel.h',
'SocketProcessBridgeChild.h',
'SocketProcessBridgeParent.h',
'SocketProcessChild.h',
@ -38,6 +39,7 @@ UNIFIED_SOURCES += [
'NeckoCommon.cpp',
'NeckoParent.cpp',
'NeckoTargetHolder.cpp',
'ParentProcessDocumentChannel.cpp',
'SocketProcessBridgeChild.cpp',
'SocketProcessBridgeParent.cpp',
'SocketProcessChild.cpp',