Bug 1646892 - Allow DocumentChannel process switches into the parent process. r=jya,nika

Differential Revision: https://phabricator.services.mozilla.com/D80327
This commit is contained in:
Matt Woodrow
2020-06-27 04:10:23 +00:00
parent f2b2693886
commit b90c5208a5
15 changed files with 194 additions and 77 deletions

View File

@@ -79,6 +79,7 @@
#include "mozilla/dom/JSWindowActorChild.h"
#include "mozilla/ipc/ProtocolUtils.h"
#include "mozilla/net/DocumentChannel.h"
#include "mozilla/net/ParentChannelWrapper.h"
#include "mozilla/net/UrlClassifierFeatureFactory.h"
#include "ReferrerInfo.h"
@@ -206,6 +207,7 @@
#include "nsSHEntry.h"
#include "nsStructuredCloneContainer.h"
#include "nsSubDocumentFrame.h"
#include "nsURILoader.h"
#include "nsView.h"
#include "nsViewManager.h"
#include "nsViewSourceHandler.h"
@@ -9436,8 +9438,7 @@ nsresult nsDocShell::DoURILoad(nsDocShellLoadState* aLoadState,
outRequest.forget(aRequest);
}
return OpenInitializedChannel(channel, uriLoader,
nsIURILoader::REDIRECTED_CHANNEL);
return OpenRedirectedChannel(aLoadState);
}
// There are two cases we care about:
@@ -9723,15 +9724,10 @@ static nsresult AppendSegmentToString(nsIInputStream* aIn, void* aClosure,
return openFlags;
}
nsresult nsDocShell::OpenInitializedChannel(nsIChannel* aChannel,
nsIURILoader* aURILoader,
uint32_t aOpenFlags) {
nsresult rv = NS_OK;
void nsDocShell::UpdateMixedContentChannelForNewLoad(nsIChannel* aChannel) {
if (mLoadType == LOAD_NORMAL_ALLOW_MIXED_CONTENT ||
mLoadType == LOAD_RELOAD_ALLOW_MIXED_CONTENT) {
rv = SetMixedContentChannel(aChannel);
NS_ENSURE_SUCCESS(rv, rv);
SetMixedContentChannel(aChannel);
} else if (mMixedContentChannel) {
/*
* If the user "Disables Protection on This Page", we call
@@ -9742,11 +9738,20 @@ nsresult nsDocShell::OpenInitializedChannel(nsIChannel* aChannel,
* This way, the user does not have to click the disable protection button
* over and over for browsing the same site.
*/
rv = nsContentUtils::CheckSameOrigin(mMixedContentChannel, aChannel);
nsresult rv =
nsContentUtils::CheckSameOrigin(mMixedContentChannel, aChannel);
if (NS_FAILED(rv) || NS_FAILED(SetMixedContentChannel(aChannel))) {
SetMixedContentChannel(nullptr);
}
}
}
nsresult nsDocShell::OpenInitializedChannel(nsIChannel* aChannel,
nsIURILoader* aURILoader,
uint32_t aOpenFlags) {
nsresult rv = NS_OK;
UpdateMixedContentChannelForNewLoad(aChannel);
// If anything fails here, make sure to clear our initial ClientSource.
auto cleanupInitialClient =
@@ -9757,21 +9762,6 @@ nsresult nsDocShell::OpenInitializedChannel(nsIChannel* aChannel,
MaybeCreateInitialClientSource();
if (aOpenFlags & nsIURILoader::REDIRECTED_CHANNEL) {
nsCOMPtr<nsILoadInfo> loadInfo;
aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
LoadInfo* li = static_cast<LoadInfo*>(loadInfo.get());
if (loadInfo->GetExternalContentPolicyType() ==
nsIContentPolicy::TYPE_DOCUMENT) {
li->UpdateBrowsingContextID(mBrowsingContext->Id());
} else if (loadInfo->GetExternalContentPolicyType() ==
nsIContentPolicy::TYPE_SUBDOCUMENT) {
li->UpdateFrameBrowsingContextID(mBrowsingContext->Id());
}
}
// TODO: more attributes need to be updated on the LoadInfo (bug 1561706)
// Let the client channel helper know if we are using DocumentChannel,
// since redirects get handled in the parent process in that case.
RefPtr<net::DocumentChannel> docChannel = do_QueryObject(aChannel);
@@ -9794,12 +9784,6 @@ nsresult nsDocShell::OpenInitializedChannel(nsIChannel* aChannel,
rv = AddClientChannelHelperInChild(
aChannel, win->EventTargetFor(TaskCategory::Other));
docChannel->SetInitialClientInfo(GetInitialClientInfo());
} else if (aOpenFlags & nsIURILoader::REDIRECTED_CHANNEL) {
// If we did a process switch, then we should have an existing allocated
// ClientInfo, so we just need to allocate a corresponding ClientSource.
CreateReservedSourceIfNeeded(aChannel,
win->EventTargetFor(TaskCategory::Other));
rv = NS_OK;
} else {
rv = AddClientChannelHelper(aChannel, std::move(noReservedClient),
GetInitialClientInfo(),
@@ -9821,6 +9805,81 @@ nsresult nsDocShell::OpenInitializedChannel(nsIChannel* aChannel,
return NS_OK;
}
nsresult nsDocShell::OpenRedirectedChannel(nsDocShellLoadState* aLoadState) {
nsCOMPtr<nsIChannel> channel = aLoadState->GetPendingRedirectedChannel();
MOZ_ASSERT(channel);
UpdateMixedContentChannelForNewLoad(channel);
// If anything fails here, make sure to clear our initial ClientSource.
auto cleanupInitialClient =
MakeScopeExit([&] { mInitialClientSource.reset(); });
nsCOMPtr<nsPIDOMWindowOuter> win = GetWindow();
NS_ENSURE_TRUE(win, NS_ERROR_FAILURE);
MaybeCreateInitialClientSource();
nsCOMPtr<nsILoadInfo> loadInfo;
channel->GetLoadInfo(getter_AddRefs(loadInfo));
LoadInfo* li = static_cast<LoadInfo*>(loadInfo.get());
if (loadInfo->GetExternalContentPolicyType() ==
nsIContentPolicy::TYPE_DOCUMENT) {
li->UpdateBrowsingContextID(mBrowsingContext->Id());
} else if (loadInfo->GetExternalContentPolicyType() ==
nsIContentPolicy::TYPE_SUBDOCUMENT) {
li->UpdateFrameBrowsingContextID(mBrowsingContext->Id());
}
// TODO: more attributes need to be updated on the LoadInfo (bug 1561706)
// If we did a process switch, then we should have an existing allocated
// ClientInfo, so we just need to allocate a corresponding ClientSource.
CreateReservedSourceIfNeeded(channel,
win->EventTargetFor(TaskCategory::Other));
RefPtr<nsDocumentOpenInfo> loader =
new nsDocumentOpenInfo(this, nsIURILoader::DONT_RETARGET, nullptr);
channel->SetLoadGroup(mLoadGroup);
MOZ_ALWAYS_SUCCEEDS(loader->Prepare());
nsresult rv = NS_OK;
if (XRE_IsParentProcess()) {
// If we're in the parent, the we don't have an nsIChildChannel, just
// the original channel, which is already open in this process.
// DocumentLoadListener expects to get an nsIParentChannel, so
// we create a wrapper around the channel and nsIStreamListener
// that forwards functionality as needed, and then we register
// it under the provided identifier.
RefPtr<ParentChannelWrapper> wrapper =
new ParentChannelWrapper(channel, loader);
wrapper->Register(aLoadState->GetPendingRedirectChannelRegistrarId());
mLoadGroup->AddRequest(channel, nullptr);
} else if (nsCOMPtr<nsIChildChannel> childChannel =
do_QueryInterface(channel)) {
// Our channel was redirected from another process, so doesn't need to
// be opened again. However, it does need its listener hooked up
// correctly.
rv = childChannel->CompleteRedirectSetup(loader);
} else {
// It's possible for the redirected channel to not implement
// nsIChildChannel and be entirely local (like srcdoc). In that case we
// can just open the local instance and it will work.
rv = channel->AsyncOpen(loader);
}
if (rv == NS_ERROR_NO_CONTENT) {
return NS_OK;
}
NS_ENSURE_SUCCESS(rv, rv);
// Success. Keep the initial ClientSource if it exists.
cleanupInitialClient.release();
return NS_OK;
}
nsresult nsDocShell::ScrollToAnchor(bool aCurHasRef, bool aNewHasRef,
nsACString& aNewHash, uint32_t aLoadType) {
if (!mCurrentURI) {