Bug 1602318 - Initiate document loads in the parent process in parallel with setting up the content process side. r=nika,jya

Differential Revision: https://phabricator.services.mozilla.com/D72112
This commit is contained in:
Matt Woodrow 2020-04-24 07:00:39 +00:00
parent 14182ed7f9
commit fc342b8877
11 changed files with 363 additions and 23 deletions

View File

@ -28,6 +28,7 @@
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/dom/WindowProxyHolder.h"
#include "mozilla/dom/SyncedContextInlines.h"
#include "mozilla/net/DocumentLoadListener.h"
#include "mozilla/Assertions.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Components.h"
@ -1464,9 +1465,30 @@ nsresult BrowsingContext::LoadURI(nsDocShellLoadState* aLoadState,
}
if (ContentParent* cp = Canonical()->GetContentParent()) {
// Attempt to initiate this load immediately in the parent, if it succeeds
// it'll return a unique identifier so that we can find it later.
uint32_t loadIdentifier = 0;
if (Canonical()->AttemptLoadURIInParent(aLoadState, &loadIdentifier)) {
aLoadState->SetLoadIdentifier(loadIdentifier);
}
cp->TransmitBlobDataIfBlobURL(aLoadState->URI(),
aLoadState->TriggeringPrincipal());
Unused << cp->SendLoadURI(this, aLoadState, aSetNavigating);
// Setup a confirmation callback once the content process receives this
// load. Normally we'd expect a PDocumentChannel actor to have been
// created to claim the load identifier by that time. If not, then it
// won't be coming, so make sure we clean up and deregister.
cp->SendLoadURI(this, aLoadState, aSetNavigating)
->Then(GetMainThreadSerialEventTarget(), __func__,
[loadIdentifier](
const PContentParent::LoadURIPromise::ResolveOrRejectValue&
aValue) {
if (loadIdentifier) {
net::DocumentLoadListener::CleanupParentLoadAttempt(
loadIdentifier);
}
});
}
}
return NS_OK;

View File

@ -16,6 +16,7 @@
#include "mozilla/ipc/ProtocolUtils.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/net/DocumentLoadListener.h"
#include "nsNetUtil.h"
#include "nsGlobalWindowOuter.h"
@ -525,6 +526,55 @@ MediaController* CanonicalBrowsingContext::GetMediaController() {
return mTabMediaController;
}
bool CanonicalBrowsingContext::AttemptLoadURIInParent(
nsDocShellLoadState* aLoadState, uint32_t* aLoadIdentifier) {
// We currently only support starting loads directly from the
// CanonicalBrowsingContext for top-level BCs.
if (!IsTopContent() || !GetContentParent() ||
!StaticPrefs::browser_tabs_documentchannel() ||
!StaticPrefs::browser_tabs_documentchannel_parent_initiated()) {
return false;
}
// DocumentChannel currently only supports connecting channels into the
// content process, so we can only support schemes that will always be loaded
// there for now. Restrict to just http(s) for simplicity.
if (net::SchemeIsHTTP(aLoadState->URI()) ||
net::SchemeIsHTTPS(aLoadState->URI())) {
return false;
}
uint64_t outerWindowId = 0;
if (WindowGlobalParent* global = GetCurrentWindowGlobal()) {
nsCOMPtr<nsIURI> currentURI = global->GetDocumentURI();
if (currentURI) {
bool newURIHasRef = false;
aLoadState->URI()->GetHasRef(&newURIHasRef);
bool equalsExceptRef = false;
aLoadState->URI()->EqualsExceptRef(currentURI, &equalsExceptRef);
if (equalsExceptRef && newURIHasRef) {
// This navigation is same-doc WRT the current one, we should pass it
// down to the docshell to be handled.
return false;
}
}
// If the current document has a beforeunload listener, then we need to
// start the load in that process after we fire the event.
if (global->HasBeforeUnload()) {
return false;
}
outerWindowId = global->OuterWindowId();
}
// If we successfully open the DocumentChannel, then it'll register
// itself using aLoadIdentifier and be kept alive until it completes
// loading.
return net::DocumentLoadListener::OpenFromParent(
this, aLoadState, outerWindowId, aLoadIdentifier);
}
void CanonicalBrowsingContext::StartDocumentLoad(
net::DocumentLoadListener* aLoad) {
mCurrentLoad = aLoad;

View File

@ -114,6 +114,9 @@ class CanonicalBrowsingContext final : public BrowsingContext {
// if the top-level browsing context has been discarded.
MediaController* GetMediaController();
bool AttemptLoadURIInParent(nsDocShellLoadState* aLoadState,
uint32_t* aLoadIdentifier);
bool HasHistoryEntry(nsISHEntry* aEntry) const {
return aEntry && (aEntry == mOSHE || aEntry == mLSHE);
}

View File

@ -4089,7 +4089,10 @@ mozilla::ipc::IPCResult ContentChild::RecvScriptError(
mozilla::ipc::IPCResult ContentChild::RecvLoadURI(
const MaybeDiscarded<BrowsingContext>& aContext,
nsDocShellLoadState* aLoadState, bool aSetNavigating) {
nsDocShellLoadState* aLoadState, bool aSetNavigating,
LoadURIResolver&& aResolve) {
auto resolveOnExit = MakeScopeExit([&] { aResolve(true); });
if (aContext.IsNullOrDiscarded()) {
return IPC_OK();
}

View File

@ -792,7 +792,8 @@ class ContentChild final
mozilla::ipc::IPCResult RecvLoadURI(
const MaybeDiscarded<BrowsingContext>& aContext,
nsDocShellLoadState* aLoadState, bool aSetNavigating);
nsDocShellLoadState* aLoadState, bool aSetNavigating,
LoadURIResolver&& aResolve);
mozilla::ipc::IPCResult RecvInternalLoad(
const MaybeDiscarded<BrowsingContext>& aContext,

View File

@ -841,7 +841,8 @@ child:
async EvictContentViewers(uint64_t[] aToEvictSharedStateIDs);
async LoadURI(MaybeDiscardedBrowsingContext aContext, nsDocShellLoadState aLoadState, bool aSetNavigating);
async LoadURI(MaybeDiscardedBrowsingContext aContext, nsDocShellLoadState aLoadState, bool aSetNavigating)
returns (bool aSuccess);
async InternalLoad(MaybeDiscardedBrowsingContext aContext, nsDocShellLoadState aLoadState, bool aTakeFocus);

View File

@ -984,6 +984,13 @@
value: false
mirror: always
# If set, use DocumentChannel to directly initiate loads from
# parent-process BrowsingContexts
- name: browser.tabs.documentchannel.parent-initiated
type: bool
value: false
mirror: always
- name: browser.tabs.remote.desktopbehavior
type: bool
value: false

View File

@ -33,6 +33,12 @@ bool DocumentChannelParent::Init(dom::CanonicalBrowsingContext* aContext,
LOG(("DocumentChannelParent Init [this=%p, uri=%s]", this,
loadState->URI()->GetSpecOrDefault().get()));
if (loadState->GetLoadIdentifier()) {
mParent = DocumentLoadListener::ClaimParentLoad(
loadState->GetLoadIdentifier(), this);
return !!mParent;
}
mParent = new DocumentLoadListener(aContext, this);
Maybe<ClientInfo> clientInfo;
@ -42,7 +48,7 @@ bool DocumentChannelParent::Init(dom::CanonicalBrowsingContext* aContext,
nsresult rv = NS_ERROR_UNEXPECTED;
if (!mParent->Open(loadState, aArgs.loadFlags(), aArgs.cacheKey(),
aArgs.channelId(), aArgs.asyncOpenTime(),
Some(aArgs.channelId()), aArgs.asyncOpenTime(),
aArgs.timing().refOr(nullptr), std::move(clientInfo),
aArgs.outerWindowId(), aArgs.hasValidTransientUserAction(),
&rv)) {
@ -57,7 +63,7 @@ DocumentChannelParent::RedirectToRealChannel(
nsTArray<ipc::Endpoint<extensions::PStreamFilterParent>>&&
aStreamFilterEndpoints,
uint32_t aRedirectFlags, uint32_t aLoadFlags) {
if (!CanSend()) {
if (!CanSend() || !mParent) {
return PDocumentChannelParent::RedirectToRealChannelPromise::
CreateAndReject(ResponseRejectReason::ChannelClosed, __func__);
}

View File

@ -38,6 +38,7 @@
#include "nsURILoader.h"
#include "nsWebNavigationInfo.h"
#include "nsDocShellLoadTypes.h"
#include "nsSandboxFlags.h"
#ifdef ANDROID
# include "mozilla/widget/nsWindow.h"
@ -242,11 +243,21 @@ NS_INTERFACE_MAP_END
DocumentLoadListener::DocumentLoadListener(
CanonicalBrowsingContext* aBrowsingContext,
ADocumentChannelBridge* aBridge) {
MOZ_ASSERT(aBridge);
LOG(("DocumentLoadListener ctor [this=%p]", this));
mParentChannelListener = new ParentChannelListener(
this, aBrowsingContext, aBrowsingContext->UsePrivateBrowsing());
}
DocumentLoadListener::DocumentLoadListener(
CanonicalBrowsingContext* aBrowsingContext,
base::ProcessId aPendingBridgeProcess) {
LOG(("DocumentLoadListener ctor [this=%p]", this));
mParentChannelListener = new ParentChannelListener(
this, aBrowsingContext, aBrowsingContext->UsePrivateBrowsing());
mPendingDocumentChannelBridgeProcess = Some(aPendingBridgeProcess);
}
DocumentLoadListener::~DocumentLoadListener() {
LOG(("DocumentLoadListener dtor [this=%p]", this));
}
@ -350,7 +361,7 @@ CanonicalBrowsingContext* DocumentLoadListener::GetBrowsingContext() {
bool DocumentLoadListener::Open(
nsDocShellLoadState* aLoadState, nsLoadFlags aLoadFlags, uint32_t aCacheKey,
const uint64_t& aChannelId, const TimeStamp& aAsyncOpenTime,
const Maybe<uint64_t>& aChannelId, const TimeStamp& aAsyncOpenTime,
nsDOMNavigationTiming* aTiming, Maybe<ClientInfo>&& aInfo,
uint64_t aOuterWindowId, bool aHasGesture, nsresult* aRv) {
LOG(("DocumentLoadListener Open [this=%p, uri=%s]", this,
@ -421,8 +432,8 @@ bool DocumentLoadListener::Open(
AntiTrackingUtils::HasStoragePermissionInParent(mChannel));
nsCOMPtr<nsIIdentChannel> identChannel = do_QueryInterface(mChannel);
if (identChannel) {
Unused << identChannel->SetChannelId(aChannelId);
if (identChannel && aChannelId) {
Unused << identChannel->SetChannelId(*aChannelId);
}
RefPtr<nsHttpChannel> httpChannelImpl = do_QueryObject(mChannel);
@ -521,6 +532,160 @@ bool DocumentLoadListener::Open(
return true;
}
bool DocumentLoadListener::OpenFromParent(
dom::CanonicalBrowsingContext* aBrowsingContext,
nsDocShellLoadState* aLoadState, uint64_t aOuterWindowId,
uint32_t* aOutIdent) {
LOG(("DocumentLoadListener::OpenFromParent"));
// We currently only support passing nullptr for aLoadInfo for
// top level browsing contexts.
if (!aBrowsingContext->IsTopContent() ||
!aBrowsingContext->GetContentParent()) {
LOG(("DocumentLoadListener::OpenFromParent failed because of subdoc"));
return false;
}
if (nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadState->Csp()) {
// Check CSP navigate-to
bool allowsNavigateTo = false;
nsresult rv = csp->GetAllowsNavigateTo(aLoadState->URI(),
aLoadState->IsFormSubmission(),
false, /* aWasRedirected */
false, /* aEnforceWhitelist */
&allowsNavigateTo);
if (NS_FAILED(rv) || !allowsNavigateTo) {
return false;
}
}
// Any sort of reload/history load would need the cacheKey, and session
// history data for load flags. We don't have those available in the parent
// yet, so don't support these load types.
auto loadType = aLoadState->LoadType();
if (loadType == LOAD_HISTORY || loadType == LOAD_RELOAD_NORMAL ||
loadType == LOAD_RELOAD_CHARSET_CHANGE ||
loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE ||
loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE) {
LOG(
("DocumentLoadListener::OpenFromParent failed because of history "
"load"));
return false;
}
// Clone because this mutates the load flags in the load state, which
// breaks nsDocShells expectations of being able to do it.
RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(*aLoadState);
loadState->CalculateLoadURIFlags();
nsLoadFlags loadFlags = loadState->LoadFlags() |
nsIChannel::LOAD_DOCUMENT_URI |
nsIChannel::LOAD_CALL_CONTENT_SNIFFERS;
uint32_t sandboxFlags = aBrowsingContext->GetSandboxFlags();
if ((sandboxFlags & (SANDBOXED_ORIGIN | SANDBOXED_SCRIPTS)) == 0) {
loadFlags |= nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE;
}
if (loadState->FirstParty()) {
// tag first party URL loads
loadFlags |= nsIChannel::LOAD_INITIAL_DOCUMENT_URI;
}
RefPtr<nsDOMNavigationTiming> timing = new nsDOMNavigationTiming(nullptr);
timing->NotifyNavigationStart(
aBrowsingContext->GetIsActive()
? nsDOMNavigationTiming::DocShellState::eActive
: nsDOMNavigationTiming::DocShellState::eInactive);
// We're not a history load or a reload, so we don't need this.
uint32_t cacheKey = 0;
// Loads start in the content process might have exposed a channel id to
// observers, so we need to preserve the value in the parent. That can't have
// happened here, so Nothing() is fine.
Maybe<uint64_t> channelId = Nothing();
// Initial client info is only relevant for subdocument loads, which we're
// not supporting yet.
Maybe<dom::ClientInfo> initialClientInfo;
RefPtr<DocumentLoadListener> listener = new DocumentLoadListener(
aBrowsingContext, aBrowsingContext->GetContentParent()->OtherPid());
nsresult rv;
bool result = listener->Open(
loadState, loadFlags, cacheKey, channelId, TimeStamp::Now(), timing,
std::move(initialClientInfo), aOuterWindowId, false, &rv);
if (result) {
// Create an entry in the redirect channel registrar to
// allocate an identifier for this load.
nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
RedirectChannelRegistrar::GetOrCreate();
rv = registrar->RegisterChannel(nullptr, aOutIdent);
MOZ_ASSERT(NS_SUCCEEDED(rv));
// Register listener (as an nsIParentChannel) under our new identifier.
rv = registrar->LinkChannels(*aOutIdent, listener, nullptr);
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
return result;
}
void DocumentLoadListener::CleanupParentLoadAttempt(uint32_t aLoadIdent) {
nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
RedirectChannelRegistrar::GetOrCreate();
nsCOMPtr<nsIParentChannel> parentChannel;
registrar->GetParentChannel(aLoadIdent, getter_AddRefs(parentChannel));
RefPtr<DocumentLoadListener> loadListener = do_QueryObject(parentChannel);
if (loadListener) {
// If the load listener is still registered, then we must have failed
// to connect DocumentChannel into it. Better cancel it!
loadListener->NotifyBridgeFailed();
}
registrar->DeregisterChannels(aLoadIdent);
}
already_AddRefed<DocumentLoadListener> DocumentLoadListener::ClaimParentLoad(
uint32_t aLoadIdent, ADocumentChannelBridge* aBridge) {
nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
RedirectChannelRegistrar::GetOrCreate();
nsCOMPtr<nsIParentChannel> parentChannel;
registrar->GetParentChannel(aLoadIdent, getter_AddRefs(parentChannel));
RefPtr<DocumentLoadListener> loadListener = do_QueryObject(parentChannel);
registrar->DeregisterChannels(aLoadIdent);
MOZ_ASSERT(loadListener);
if (loadListener) {
loadListener->NotifyBridgeConnected(aBridge);
}
return loadListener.forget();
}
void DocumentLoadListener::NotifyBridgeConnected(
ADocumentChannelBridge* aBridge) {
LOG(("DocumentLoadListener NotifyBridgeConnected [this=%p]", this));
MOZ_ASSERT(!mDocumentChannelBridge);
MOZ_ASSERT(mPendingDocumentChannelBridgeProcess);
MOZ_ASSERT(aBridge->OtherPid() == *mPendingDocumentChannelBridgeProcess);
mDocumentChannelBridge = aBridge;
mPendingDocumentChannelBridgeProcess.reset();
mBridgePromise.ResolveIfExists(aBridge, __func__);
}
void DocumentLoadListener::NotifyBridgeFailed() {
LOG(("DocumentLoadListener NotifyBridgeFailed [this=%p]", this));
MOZ_ASSERT(!mDocumentChannelBridge);
MOZ_ASSERT(mPendingDocumentChannelBridgeProcess);
mPendingDocumentChannelBridgeProcess.reset();
Cancel(NS_BINDING_ABORTED);
mBridgePromise.RejectIfExists(false, __func__);
}
void DocumentLoadListener::DocumentChannelBridgeDisconnected() {
LOG(("DocumentLoadListener DocumentChannelBridgeDisconnected [this=%p]",
this));
@ -568,6 +733,15 @@ void DocumentLoadListener::DisconnectChildListeners(nsresult aStatus,
// This will drop the bridge's reference to us, so we use keepAlive to
// make sure we don't get deleted until we exit the function.
mDocumentChannelBridge->DisconnectChildListeners(aStatus, aLoadGroupStatus);
} else if (mPendingDocumentChannelBridgeProcess) {
EnsureBridge()->Then(
GetCurrentThreadSerialEventTarget(), __func__,
[keepAlive, aStatus,
aLoadGroupStatus](ADocumentChannelBridge* aBridge) {
aBridge->DisconnectChildListeners(aStatus, aLoadGroupStatus);
keepAlive->mDocumentChannelBridge = nullptr;
},
[](bool aDummy) {});
}
DocumentChannelBridgeDisconnected();
@ -891,8 +1065,17 @@ void DocumentLoadListener::SerializeRedirectData(
channelLoadInfo->GetPrincipalToInherit(getter_AddRefs(principalToInherit));
const RefPtr<nsHttpChannel> baseChannel = do_QueryObject(mChannel.get());
nsCOMPtr<nsILoadContext> loadContext;
NS_QueryNotificationCallbacks(mChannel, loadContext);
nsCOMPtr<nsILoadInfo> redirectLoadInfo;
if (baseChannel) {
// Only use CloneLoadInfoForRedirect if we have a load context,
// since it internally tries to pull OriginAttributes from the
// the load context and asserts if they don't match the load info.
// We can end up without a load context if the channel has been aborted
// and the callbacks have been cleared.
if (baseChannel && loadContext) {
redirectLoadInfo = baseChannel->CloneLoadInfoForRedirect(
aArgs.uri(), nsIChannelEventSink::REDIRECT_INTERNAL);
redirectLoadInfo->SetResultPrincipalURI(aArgs.uri());
@ -1168,6 +1351,17 @@ bool DocumentLoadListener::MaybeTriggerProcessSwitch() {
return true;
}
auto DocumentLoadListener::EnsureBridge() -> RefPtr<EnsureBridgePromise> {
MOZ_ASSERT(mDocumentChannelBridge || mPendingDocumentChannelBridgeProcess);
if (mDocumentChannelBridge) {
MOZ_ASSERT(mBridgePromise.IsEmpty());
return EnsureBridgePromise::CreateAndResolve(mDocumentChannelBridge,
__func__);
}
return mBridgePromise.Ensure(__func__);
}
RefPtr<PDocumentChannelParent::RedirectToRealChannelPromise>
DocumentLoadListener::RedirectToRealChannel(
uint32_t aRedirectFlags, uint32_t aLoadFlags,
@ -1219,9 +1413,19 @@ DocumentLoadListener::RedirectToRealChannel(
return cp->SendCrossProcessRedirect(args,
std::move(aStreamFilterEndpoints));
}
MOZ_ASSERT(mDocumentChannelBridge);
return mDocumentChannelBridge->RedirectToRealChannel(
std::move(aStreamFilterEndpoints), aRedirectFlags, aLoadFlags);
return EnsureBridge()->Then(
GetCurrentThreadSerialEventTarget(), __func__,
[endpoints = std::move(aStreamFilterEndpoints), aRedirectFlags,
aLoadFlags](ADocumentChannelBridge* aBridge) mutable {
return aBridge->RedirectToRealChannel(std::move(endpoints),
aRedirectFlags, aLoadFlags);
},
[](bool aDummy) {
return PDocumentChannelParent::RedirectToRealChannelPromise::
CreateAndReject(ipc::ResponseRejectReason::ActorDestroyed,
__func__);
});
}
void DocumentLoadListener::TriggerRedirectToRealChannel(
@ -1326,7 +1530,7 @@ DocumentLoadListener::OnStartRequest(nsIRequest* aRequest) {
MOZ_DIAGNOSTIC_ASSERT(mChannel);
RefPtr<nsHttpChannel> httpChannel = do_QueryObject(mChannel);
if (!mDocumentChannelBridge) {
if (!mDocumentChannelBridge && !mPendingDocumentChannelBridgeProcess) {
return NS_ERROR_UNEXPECTED;
}
@ -1580,10 +1784,6 @@ DocumentLoadListener::AsyncOnChannelRedirect(
mLoadStateLoadType, nsIWebNavigation::LOAD_FLAGS_ALLOW_MIXED_CONTENT));
}
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

View File

@ -95,11 +95,30 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
// Creates the channel, and then calls AsyncOpen on it.
bool Open(nsDocShellLoadState* aLoadState, nsLoadFlags aLoadFlags,
uint32_t aCacheKey, const uint64_t& aChannelId,
uint32_t aCacheKey, const Maybe<uint64_t>& aChannelId,
const TimeStamp& aAsyncOpenTime, nsDOMNavigationTiming* aTiming,
Maybe<dom::ClientInfo>&& aInfo, uint64_t aOuterWindowId,
bool aHasGesture, nsresult* aRv);
// Creates a DocumentLoadListener directly in the parent process without
// an associated DocumentChannelBridge.
// If successful it registers a unique identifier (return in aOutIdent) to
// keep it alive until a future bridge can attach to it, or we fail and clean
// up.
static bool OpenFromParent(dom::CanonicalBrowsingContext* aBrowsingContext,
nsDocShellLoadState* aLoadState,
uint64_t aOuterWindowId, uint32_t* aOutIdent);
// Ensures that a load identifier allocated by OpenFromParent has
// been deregistered if it hasn't already been claimed.
// This also cancels the load.
static void CleanupParentLoadAttempt(uint32_t aLoadIdent);
// Looks up aLoadIdent to find the associated, cleans up the registration
// and attaches aBridge as the listener.
static already_AddRefed<DocumentLoadListener> ClaimParentLoad(
uint32_t aLoadIdent, ADocumentChannelBridge* aBridge);
NS_DECL_ISUPPORTS
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
@ -166,6 +185,9 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
if (mDocumentChannelBridge) {
return mDocumentChannelBridge->OtherPid();
}
if (mPendingDocumentChannelBridgeProcess) {
return *mPendingDocumentChannelBridgeProcess;
}
return 0;
}
@ -187,8 +209,26 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
net::LastVisitInfo LastVisitInfo() const;
protected:
DocumentLoadListener(dom::CanonicalBrowsingContext* aBrowsingContext,
base::ProcessId aPendingBridgeProcess);
virtual ~DocumentLoadListener();
// Called when we were created without a document channel bridge,
// and now it has been created and attached.
void NotifyBridgeConnected(ADocumentChannelBridge* aBridge);
// Called when we were created without a document channel bridge,
// and creation has failed, and won't ever be attached.
void NotifyBridgeFailed();
// Returns a promise that resolves with the document channel bridge,
// waiting for a pending one if necessary.
// If we've failed to create a bridge, or a bridge has already been
// detached then rejects.
typedef MozPromise<RefPtr<ADocumentChannelBridge>, bool, false>
EnsureBridgePromise;
RefPtr<EnsureBridgePromise> EnsureBridge();
// Initiates the switch from DocumentChannel to the real protocol-specific
// channel, and ensures that RedirectToRealChannelFinished is called when
// this is complete.
@ -333,6 +373,15 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
// shuts down to break this.
RefPtr<ADocumentChannelBridge> mDocumentChannelBridge;
// If we were created without a bridge, then this is set
// to Some() with the process id of the content process
// that will be creating our bridge soon.
Maybe<base::ProcessId> mPendingDocumentChannelBridgeProcess;
// Holds a promise for callers that want to wait on the document
// channel bridge becoming available.
MozPromiseHolder<EnsureBridgePromise> mBridgePromise;
// The original URI of the current channel. If there are redirects,
// then the value on the channel gets overwritten with the original
// URI of the first channel in the redirect chain, so we cache the

View File

@ -107,11 +107,9 @@ ParentProcessDocumentChannel::OnRedirectVerifyCallback(nsresult aResult) {
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);
GetDocShell()->GetBrowsingContext()->Canonical(), this);
LOG(("Created PPDocumentChannel with listener=%p",
mDocumentLoadListener.get()));
@ -128,7 +126,7 @@ NS_IMETHODIMP ParentProcessDocumentChannel::AsyncOpen(
nsresult rv = NS_OK;
Maybe<dom::ClientInfo> initialClientInfo = mInitialClientInfo;
if (!mDocumentLoadListener->Open(
mLoadState, mLoadFlags, mCacheKey, mChannelId, mAsyncOpenTime,
mLoadState, mLoadFlags, mCacheKey, Some(mChannelId), mAsyncOpenTime,
mTiming, std::move(initialClientInfo),
GetDocShell()->GetOuterWindowID(),
GetDocShell()