mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-20 16:55:40 +00:00
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:
parent
14182ed7f9
commit
fc342b8877
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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__);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
Loading…
Reference in New Issue
Block a user