mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-17 15:25:52 +00:00
Bug 1647557 - Add preffed-off code for controlling document loads directly from CanonicalBrowsingContext. r=nika,jya,necko-reviewers,dragana
Differential Revision: https://phabricator.services.mozilla.com/D70629
This commit is contained in:
parent
751fe6358b
commit
7d65de1298
@ -1584,11 +1584,15 @@ nsresult BrowsingContext::LoadURI(nsDocShellLoadState* aLoadState,
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (Canonical()->LoadInParent(aLoadState, aSetNavigating)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
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.
|
||||
uint64_t loadIdentifier = 0;
|
||||
if (Canonical()->AttemptLoadURIInParent(aLoadState)) {
|
||||
if (Canonical()->AttemptSpeculativeLoadInParent(aLoadState)) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(GetCurrentLoadIdentifier().isSome());
|
||||
loadIdentifier = GetCurrentLoadIdentifier().value();
|
||||
aLoadState->SetChannelInitialized(true);
|
||||
|
@ -942,16 +942,8 @@ MediaController* CanonicalBrowsingContext::GetMediaController() {
|
||||
return mTabMediaController;
|
||||
}
|
||||
|
||||
bool CanonicalBrowsingContext::AttemptLoadURIInParent(
|
||||
nsDocShellLoadState* aLoadState) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
bool CanonicalBrowsingContext::SupportsLoadingInParent(
|
||||
nsDocShellLoadState* aLoadState, uint64_t* aOuterWindowId) {
|
||||
// We currently don't support initiating loads in the parent when they are
|
||||
// watched by devtools. This is because devtools tracks loads using content
|
||||
// process notifications, which happens after the load is initiated in this
|
||||
@ -969,7 +961,6 @@ bool CanonicalBrowsingContext::AttemptLoadURIInParent(
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t outerWindowId = 0;
|
||||
if (WindowGlobalParent* global = GetCurrentWindowGlobal()) {
|
||||
nsCOMPtr<nsIURI> currentURI = global->GetDocumentURI();
|
||||
if (currentURI) {
|
||||
@ -990,20 +981,72 @@ bool CanonicalBrowsingContext::AttemptLoadURIInParent(
|
||||
return false;
|
||||
}
|
||||
|
||||
outerWindowId = global->OuterWindowId();
|
||||
*aOuterWindowId = global->OuterWindowId();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CanonicalBrowsingContext::LoadInParent(nsDocShellLoadState* aLoadState,
|
||||
bool aSetNavigating) {
|
||||
// We currently only support starting loads directly from the
|
||||
// CanonicalBrowsingContext for top-level BCs.
|
||||
// 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_controlled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t outerWindowId = 0;
|
||||
if (!SupportsLoadingInParent(aLoadState, &outerWindowId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Note: If successful, this will recurse into StartDocumentLoad and
|
||||
// set mCurrentLoad to the DocumentLoadListener instance created.
|
||||
// Ideally in the future we will only start loads from here, and we can
|
||||
// just set this directly instead.
|
||||
return net::DocumentLoadListener::LoadInParent(this, aLoadState,
|
||||
outerWindowId, aSetNavigating);
|
||||
}
|
||||
|
||||
bool CanonicalBrowsingContext::AttemptSpeculativeLoadInParent(
|
||||
nsDocShellLoadState* aLoadState) {
|
||||
// We currently only support starting loads directly from the
|
||||
// CanonicalBrowsingContext for top-level BCs.
|
||||
// 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() ||
|
||||
StaticPrefs::browser_tabs_documentchannel_parent_controlled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t outerWindowId = 0;
|
||||
if (!SupportsLoadingInParent(aLoadState, &outerWindowId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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);
|
||||
return net::DocumentLoadListener::SpeculativeLoadInParent(this, aLoadState,
|
||||
outerWindowId);
|
||||
}
|
||||
|
||||
void CanonicalBrowsingContext::StartDocumentLoad(
|
||||
bool CanonicalBrowsingContext::StartDocumentLoad(
|
||||
net::DocumentLoadListener* aLoad) {
|
||||
// If we're controlling loads from the parent, then starting a new load means
|
||||
// that we need to cancel any existing ones.
|
||||
if (StaticPrefs::browser_tabs_documentchannel_parent_controlled() &&
|
||||
mCurrentLoad) {
|
||||
mCurrentLoad->Cancel(NS_BINDING_ABORTED);
|
||||
}
|
||||
mCurrentLoad = aLoad;
|
||||
SetCurrentLoadIdentifier(Some(aLoad->GetLoadIdentifier()));
|
||||
return true;
|
||||
}
|
||||
|
||||
void CanonicalBrowsingContext::EndDocumentLoad(bool aForProcessSwitch) {
|
||||
|
@ -161,7 +161,17 @@ class CanonicalBrowsingContext final : public BrowsingContext {
|
||||
// if the top-level browsing context has been discarded.
|
||||
MediaController* GetMediaController();
|
||||
|
||||
bool AttemptLoadURIInParent(nsDocShellLoadState* aLoadState);
|
||||
// Attempts to start loading the given load state in this BrowsingContext,
|
||||
// without requiring any communication from a docshell. This will handle
|
||||
// computing the right process to load in, and organising handoff to
|
||||
// the right docshell when we get a response.
|
||||
bool LoadInParent(nsDocShellLoadState* aLoadState, bool aSetNavigating);
|
||||
|
||||
// Attempts to start loading the given load state in this BrowsingContext,
|
||||
// in parallel with a DocumentChannelChild being created in the docshell.
|
||||
// Requires the DocumentChannel to connect with this load for it to
|
||||
// complete successfully.
|
||||
bool AttemptSpeculativeLoadInParent(nsDocShellLoadState* aLoadState);
|
||||
|
||||
// Get or create a secure browser UI for this BrowsingContext
|
||||
nsISecureBrowserUI* GetSecureBrowserUI();
|
||||
@ -230,13 +240,17 @@ class CanonicalBrowsingContext final : public BrowsingContext {
|
||||
|
||||
friend class net::DocumentLoadListener;
|
||||
// Called when a DocumentLoadListener is created to start a load for
|
||||
// this browsing context.
|
||||
void StartDocumentLoad(net::DocumentLoadListener* aLoad);
|
||||
// this browsing context. Returns false if a higher priority load is
|
||||
// already in-progress and the new one has been rejected.
|
||||
bool StartDocumentLoad(net::DocumentLoadListener* aLoad);
|
||||
// Called once DocumentLoadListener completes handling a load, and it
|
||||
// is either complete, or handed off to the final channel to deliver
|
||||
// data to the destination docshell.
|
||||
void EndDocumentLoad(bool aForProcessSwitch);
|
||||
|
||||
bool SupportsLoadingInParent(nsDocShellLoadState* aLoadState,
|
||||
uint64_t* aOuterWindowId);
|
||||
|
||||
// XXX(farre): Store a ContentParent pointer here rather than mProcessId?
|
||||
// Indicates which process owns the docshell.
|
||||
uint64_t mProcessId;
|
||||
|
@ -1068,13 +1068,21 @@
|
||||
value: true
|
||||
mirror: always
|
||||
|
||||
# If set, use DocumentChannel to directly initiate loads from
|
||||
# parent-process BrowsingContexts
|
||||
# If set, use DocumentChannel to initiate loads from
|
||||
# parent-process BrowsingContexts in parallel with the content
|
||||
# process.
|
||||
- name: browser.tabs.documentchannel.parent-initiated
|
||||
type: bool
|
||||
value: true
|
||||
mirror: always
|
||||
|
||||
# If set, use DocumentChannel to directly initiate loads entirely
|
||||
# from parent-process BrowsingContexts
|
||||
- name: browser.tabs.documentchannel.parent-controlled
|
||||
type: bool
|
||||
value: false
|
||||
mirror: always
|
||||
|
||||
- name: browser.tabs.remote.desktopbehavior
|
||||
type: bool
|
||||
value: false
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "nsDocShell.h"
|
||||
#include "nsDocShellLoadState.h"
|
||||
#include "nsDocShellLoadTypes.h"
|
||||
#include "nsDOMNavigationTiming.h"
|
||||
#include "nsExternalHelperAppService.h"
|
||||
#include "nsHttpChannel.h"
|
||||
#include "nsIBrowser.h"
|
||||
@ -358,7 +359,7 @@ already_AddRefed<LoadInfo> DocumentLoadListener::CreateLoadInfo(
|
||||
return loadInfo.forget();
|
||||
}
|
||||
|
||||
CanonicalBrowsingContext* DocumentLoadListener::GetBrowsingContext() {
|
||||
CanonicalBrowsingContext* DocumentLoadListener::GetBrowsingContext() const {
|
||||
if (!mParentChannelListener) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -397,6 +398,8 @@ auto DocumentLoadListener::Open(
|
||||
browsingContext, aLoadState, loadInfo, mParentChannelListener,
|
||||
nullptr, attrs, loadFlags, aCacheKey, *aRv,
|
||||
getter_AddRefs(mChannel))) {
|
||||
LOG(("DocumentLoadListener::Open failed to create channel [this=%p]",
|
||||
this));
|
||||
mParentChannelListener = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
@ -457,6 +460,15 @@ auto DocumentLoadListener::Open(
|
||||
// across any serviceworker related data between channels as needed.
|
||||
AddClientChannelHelperInParent(mChannel, std::move(aInfo));
|
||||
|
||||
if (!browsingContext->StartDocumentLoad(this)) {
|
||||
LOG(("DocumentLoadListener::Open failed StartDocumentLoad [this=%p]",
|
||||
this));
|
||||
*aRv = NS_BINDING_ABORTED;
|
||||
mParentChannelListener = nullptr;
|
||||
mChannel = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Recalculate the openFlags, matching the logic in use in Content process.
|
||||
// NOTE: The only case not handled here to mirror Content process is
|
||||
// redirecting to re-use the channel.
|
||||
@ -511,6 +523,10 @@ auto DocumentLoadListener::Open(
|
||||
{
|
||||
*aRv = mChannel->AsyncOpen(openInfo);
|
||||
if (NS_FAILED(*aRv)) {
|
||||
LOG(("DocumentLoadListener::Open failed AsyncOpen [this=%p rv=%" PRIx32
|
||||
"]",
|
||||
this, static_cast<uint32_t>(*aRv)));
|
||||
browsingContext->EndDocumentLoad(false);
|
||||
mParentChannelListener = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
@ -530,8 +546,6 @@ auto DocumentLoadListener::Open(
|
||||
browsingContext->CreateSessionHistoryEntryForLoad(aLoadState, mChannel);
|
||||
}
|
||||
|
||||
browsingContext->StartDocumentLoad(this);
|
||||
|
||||
*aRv = NS_OK;
|
||||
mOpenPromise = new OpenPromise::Private(__func__);
|
||||
// We make the promise use direct task dispatch in order to reduce the number
|
||||
@ -540,18 +554,16 @@ auto DocumentLoadListener::Open(
|
||||
return mOpenPromise;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool DocumentLoadListener::OpenFromParent(
|
||||
dom::CanonicalBrowsingContext* aBrowsingContext,
|
||||
nsDocShellLoadState* aLoadState, uint64_t aOuterWindowId) {
|
||||
LOG(("DocumentLoadListener::OpenFromParent"));
|
||||
|
||||
auto DocumentLoadListener::OpenInParent(nsDocShellLoadState* aLoadState,
|
||||
uint64_t aOuterWindowId,
|
||||
bool aSupportsRedirectToRealChannel)
|
||||
-> RefPtr<OpenPromise> {
|
||||
// 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 (!GetBrowsingContext()->IsTopContent() ||
|
||||
!GetBrowsingContext()->GetContentParent()) {
|
||||
LOG(("DocumentLoadListener::OpenInParent failed because of subdoc"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (nsCOMPtr<nsIContentSecurityPolicy> csp = aLoadState->Csp()) {
|
||||
@ -563,7 +575,7 @@ bool DocumentLoadListener::OpenFromParent(
|
||||
false, /* aEnforceWhitelist */
|
||||
&allowsNavigateTo);
|
||||
if (NS_FAILED(rv) || !allowsNavigateTo) {
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@ -576,9 +588,9 @@ bool DocumentLoadListener::OpenFromParent(
|
||||
loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE ||
|
||||
loadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE) {
|
||||
LOG(
|
||||
("DocumentLoadListener::OpenFromParent failed because of history "
|
||||
("DocumentLoadListener::OpenInParent failed because of history "
|
||||
"load"));
|
||||
return false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Clone because this mutates the load flags in the load state, which
|
||||
@ -588,7 +600,7 @@ bool DocumentLoadListener::OpenFromParent(
|
||||
|
||||
RefPtr<nsDOMNavigationTiming> timing = new nsDOMNavigationTiming(nullptr);
|
||||
timing->NotifyNavigationStart(
|
||||
aBrowsingContext->GetIsActive()
|
||||
GetBrowsingContext()->GetIsActive()
|
||||
? nsDOMNavigationTiming::DocShellState::eActive
|
||||
: nsDOMNavigationTiming::DocShellState::eInactive);
|
||||
|
||||
@ -604,22 +616,110 @@ bool DocumentLoadListener::OpenFromParent(
|
||||
// not supporting yet.
|
||||
Maybe<dom::ClientInfo> initialClientInfo;
|
||||
|
||||
mSupportsRedirectToRealChannel = aSupportsRedirectToRealChannel;
|
||||
|
||||
nsresult rv;
|
||||
return Open(loadState, cacheKey, channelId, TimeStamp::Now(), timing,
|
||||
std::move(initialClientInfo), aOuterWindowId, false, Nothing(),
|
||||
Nothing(), GetBrowsingContext()->GetContentParent()->OtherPid(),
|
||||
&rv);
|
||||
}
|
||||
|
||||
static void FireStateChange(DocumentLoadListener* aLoad, uint32_t aStateFlags,
|
||||
nsresult aStatus) {
|
||||
nsCOMPtr<nsIChannel> request = aLoad->GetChannel();
|
||||
nsCOMPtr<nsIWebProgress> webProgress =
|
||||
new RemoteWebProgress(aLoad->GetLoadType(), true, true);
|
||||
|
||||
RefPtr<CanonicalBrowsingContext> ctx = aLoad->GetBrowsingContext();
|
||||
NS_DispatchToMainThread(
|
||||
NS_NewRunnableFunction("DocumentLoadListener::FireStateChange", [=]() {
|
||||
if (ctx && ctx->GetWebProgress()) {
|
||||
ctx->GetWebProgress()->OnStateChange(webProgress, request,
|
||||
aStateFlags, aStatus);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
static void SetNavigating(CanonicalBrowsingContext* aBrowsingContext,
|
||||
bool aNavigating) {
|
||||
nsCOMPtr<nsIBrowser> browser;
|
||||
if (RefPtr<Element> currentElement = aBrowsingContext->GetEmbedderElement()) {
|
||||
browser = currentElement->AsBrowser();
|
||||
}
|
||||
|
||||
if (!browser) {
|
||||
return;
|
||||
}
|
||||
|
||||
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
||||
"DocumentLoadListener::SetNavigating",
|
||||
[browser, aNavigating]() { browser->SetIsNavigating(aNavigating); }));
|
||||
}
|
||||
|
||||
/* static */ bool DocumentLoadListener::LoadInParent(
|
||||
CanonicalBrowsingContext* aBrowsingContext, nsDocShellLoadState* aLoadState,
|
||||
uint64_t aOuterWindowId, bool aSetNavigating) {
|
||||
SetNavigating(aBrowsingContext, aSetNavigating);
|
||||
|
||||
RefPtr<DocumentLoadListener> load =
|
||||
new DocumentLoadListener(aBrowsingContext);
|
||||
RefPtr<DocumentLoadListener::OpenPromise> promise = load->OpenInParent(
|
||||
aLoadState, aOuterWindowId, /* aSupportsRedirectToRealChannel */ false);
|
||||
if (!promise) {
|
||||
SetNavigating(aBrowsingContext, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// We passed false for aSupportsRedirectToRealChannel, so we should always
|
||||
// take the process switching path, and reject this promise.
|
||||
promise->Then(
|
||||
GetCurrentSerialEventTarget(), __func__,
|
||||
[load](DocumentLoadListener::OpenPromise::ResolveOrRejectValue&& aValue) {
|
||||
MOZ_ASSERT(aValue.IsReject());
|
||||
DocumentLoadListener::OpenPromiseFailedType& rejectValue =
|
||||
aValue.RejectValue();
|
||||
if (!rejectValue.mSwitchedProcess) {
|
||||
// If we're not switching the load to a new process, then it is
|
||||
// finished (and failed), and we should fire a state change to notify
|
||||
// observers. Normally the docshell would fire this, and it would get
|
||||
// filtered out by BrowserParent if needed.
|
||||
FireStateChange(load,
|
||||
nsIWebProgressListener::STATE_STOP |
|
||||
nsIWebProgressListener::STATE_IS_WINDOW |
|
||||
nsIWebProgressListener::STATE_IS_NETWORK,
|
||||
rejectValue.mStatus);
|
||||
}
|
||||
});
|
||||
|
||||
FireStateChange(load,
|
||||
nsIWebProgressListener::STATE_START |
|
||||
nsIWebProgressListener::STATE_IS_DOCUMENT |
|
||||
nsIWebProgressListener::STATE_IS_REQUEST |
|
||||
nsIWebProgressListener::STATE_IS_WINDOW |
|
||||
nsIWebProgressListener::STATE_IS_NETWORK,
|
||||
NS_OK);
|
||||
SetNavigating(aBrowsingContext, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool DocumentLoadListener::SpeculativeLoadInParent(
|
||||
dom::CanonicalBrowsingContext* aBrowsingContext,
|
||||
nsDocShellLoadState* aLoadState, uint64_t aOuterWindowId) {
|
||||
LOG(("DocumentLoadListener::OpenFromParent"));
|
||||
|
||||
RefPtr<DocumentLoadListener> listener =
|
||||
new DocumentLoadListener(aBrowsingContext);
|
||||
|
||||
nsresult rv;
|
||||
auto promise = listener->Open(
|
||||
loadState, cacheKey, channelId, TimeStamp::Now(), timing,
|
||||
std::move(initialClientInfo), aOuterWindowId, false, Nothing(), Nothing(),
|
||||
aBrowsingContext->GetContentParent()->OtherPid(), &rv);
|
||||
auto promise = listener->OpenInParent(aLoadState, aOuterWindowId, true);
|
||||
if (promise) {
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
// Create an entry in the redirect channel registrar to
|
||||
// allocate an identifier for this load.
|
||||
nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
|
||||
RedirectChannelRegistrar::GetOrCreate();
|
||||
uint64_t loadIdentifier = aLoadState->GetLoadIdentifier();
|
||||
rv = registrar->RegisterChannel(nullptr, loadIdentifier);
|
||||
nsresult rv = registrar->RegisterChannel(nullptr, loadIdentifier);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
// Register listener (as an nsIParentChannel) under our new identifier.
|
||||
rv = registrar->LinkChannels(loadIdentifier, listener, nullptr);
|
||||
@ -712,14 +812,15 @@ void DocumentLoadListener::Cancel(const nsresult& aStatusCode) {
|
||||
}
|
||||
|
||||
void DocumentLoadListener::DisconnectListeners(nsresult aStatus,
|
||||
nsresult aLoadGroupStatus) {
|
||||
nsresult aLoadGroupStatus,
|
||||
bool aSwitchedProcess) {
|
||||
LOG(
|
||||
("DocumentLoadListener DisconnectListener [this=%p, "
|
||||
"aStatus=%" PRIx32 " aLoadGroupStatus=%" PRIx32 " ]",
|
||||
this, static_cast<uint32_t>(aStatus),
|
||||
static_cast<uint32_t>(aLoadGroupStatus)));
|
||||
|
||||
RejectOpenPromise(aStatus, aLoadGroupStatus, __func__);
|
||||
RejectOpenPromise(aStatus, aLoadGroupStatus, aSwitchedProcess, __func__);
|
||||
|
||||
Disconnect();
|
||||
|
||||
@ -786,7 +887,8 @@ void DocumentLoadListener::FinishReplacementChannelSetup(nsresult aResult) {
|
||||
});
|
||||
|
||||
if (mDoingProcessSwitch) {
|
||||
DisconnectListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED);
|
||||
DisconnectListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED,
|
||||
NS_SUCCEEDED(aResult));
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
|
||||
@ -1642,6 +1744,11 @@ DocumentLoadListener::OnStartRequest(nsIRequest* aRequest) {
|
||||
// might cancel the channel.
|
||||
nsContentSecurityUtils::PerformCSPFrameAncestorAndXFOCheck(mChannel);
|
||||
|
||||
if (!GetBrowsingContext() || GetBrowsingContext()->IsDiscarded()) {
|
||||
DisconnectListeners(NS_ERROR_UNEXPECTED, NS_ERROR_UNEXPECTED);
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// Generally we want to switch to a real channel even if the request failed,
|
||||
// since the listener might want to access protocol-specific data (like http
|
||||
// response headers) in its error handling.
|
||||
@ -1681,7 +1788,34 @@ DocumentLoadListener::OnStartRequest(nsIRequest* aRequest) {
|
||||
bool willBeRemote = false;
|
||||
if (!DocShellWillDisplayContent(status) ||
|
||||
!MaybeTriggerProcessSwitch(&willBeRemote)) {
|
||||
TriggerRedirectToRealChannel();
|
||||
if (!mSupportsRedirectToRealChannel) {
|
||||
// If the existing process is right for this load, but the bridge doesn't
|
||||
// support redirects, then we need to do it manually, by faking a process
|
||||
// switch.
|
||||
mDoingProcessSwitch = true;
|
||||
|
||||
// If we're not going to process switch, then we must have an existing
|
||||
// window global, right?
|
||||
MOZ_ASSERT(GetBrowsingContext()->GetCurrentWindowGlobal());
|
||||
|
||||
RefPtr<BrowserParent> browserParent =
|
||||
GetBrowsingContext()->GetCurrentWindowGlobal()->GetBrowserParent();
|
||||
|
||||
// This load has already started, so we want to suspend the start progress
|
||||
// events from the docshell from reaching the parent.
|
||||
browserParent->SuspendProgressEventsUntilAfterNextLoadStarts();
|
||||
|
||||
// Notify the docshell that it should load using the newly connected
|
||||
// channel
|
||||
browserParent->ResumeLoad(mLoadIdentifier);
|
||||
|
||||
// Use the current process ID to run the 'process switch' path and connect
|
||||
// the channel into the current process.
|
||||
TriggerRedirectToRealChannel(
|
||||
Some(GetBrowsingContext()->OwnerProcessId()));
|
||||
} else {
|
||||
TriggerRedirectToRealChannel(Nothing());
|
||||
}
|
||||
|
||||
// If we're not switching, then check if we're currently remote.
|
||||
if (GetBrowsingContext() && GetBrowsingContext()->GetContentParent()) {
|
||||
@ -1870,7 +2004,7 @@ NS_IMETHODIMP
|
||||
DocumentLoadListener::AsyncOnChannelRedirect(
|
||||
nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
|
||||
nsIAsyncVerifyRedirectCallback* aCallback) {
|
||||
LOG(("DocumentLoadListener AsyncOnChannelRedirect [this=%p, aFlags=%" PRIx32
|
||||
LOG(("DocumentLoadListener::AsyncOnChannelRedirect [this=%p flags=%" PRIu32
|
||||
"]",
|
||||
this, aFlags));
|
||||
// We generally don't want to notify the content process about redirects,
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "mozilla/MozPromise.h"
|
||||
#include "mozilla/Variant.h"
|
||||
#include "mozilla/ipc/ProtocolUtils.h"
|
||||
#include "mozilla/dom/SessionHistoryEntry.h"
|
||||
#include "mozilla/net/NeckoCommon.h"
|
||||
#include "mozilla/net/NeckoParent.h"
|
||||
@ -107,6 +108,9 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
|
||||
struct OpenPromiseFailedType {
|
||||
nsresult mStatus;
|
||||
nsresult mLoadGroupStatus;
|
||||
// This is set to true if we're rejecting the promise because we
|
||||
// switched to load away to a new process.
|
||||
bool mSwitchedProcess = false;
|
||||
};
|
||||
|
||||
typedef MozPromise<OpenPromiseSucceededType, OpenPromiseFailedType,
|
||||
@ -130,14 +134,22 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
|
||||
Maybe<bool> aUriModified, Maybe<bool> aIsXFOError,
|
||||
base::ProcessId aPid, nsresult* aRv);
|
||||
|
||||
// Creates a DocumentLoadListener directly in the parent process without
|
||||
// an associated DocumentChannel.
|
||||
// Creates a DocumentLoadListener entirely in the parent process and opens it,
|
||||
// and never needs a DocumentChannel to connect to an existing docshell.
|
||||
// Once we get a response it takes the 'process switch' path to find the right
|
||||
// process and docshell, and delivers the response there directly.
|
||||
static bool LoadInParent(dom::CanonicalBrowsingContext* aBrowsingContext,
|
||||
nsDocShellLoadState* aLoadState,
|
||||
uint64_t aOuterWindowId, bool aSetNavigating);
|
||||
|
||||
// Creates a DocumentLoadListener directly in the parent process and opens it,
|
||||
// without needing an existing DocumentChannel.
|
||||
// If successful it registers a unique identifier (return in aOutIdent) to
|
||||
// keep it alive until a future DocumentChannel can attach to it, or we fail
|
||||
// and clean up.
|
||||
static bool OpenFromParent(dom::CanonicalBrowsingContext* aBrowsingContext,
|
||||
nsDocShellLoadState* aLoadState,
|
||||
uint64_t aOuterWindowId);
|
||||
static bool SpeculativeLoadInParent(
|
||||
dom::CanonicalBrowsingContext* aBrowsingContext,
|
||||
nsDocShellLoadState* aLoadState, uint64_t aOuterWindowId);
|
||||
|
||||
// Ensures that a load identifier allocated by OpenFromParent has
|
||||
// been deregistered if it hasn't already been claimed.
|
||||
@ -225,14 +237,22 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
|
||||
dom::ContentParent* aParent) const;
|
||||
|
||||
uint64_t GetLoadIdentifier() const { return mLoadIdentifier; }
|
||||
dom::CanonicalBrowsingContext* GetBrowsingContext() const;
|
||||
|
||||
uint32_t GetLoadType() const { return mLoadStateLoadType; }
|
||||
|
||||
protected:
|
||||
virtual ~DocumentLoadListener();
|
||||
|
||||
private:
|
||||
RefPtr<OpenPromise> OpenInParent(nsDocShellLoadState* aLoadState,
|
||||
uint64_t aOuterWindowId,
|
||||
bool aSupportsRedirectToRealChannel);
|
||||
|
||||
friend class ParentProcessDocumentOpenInfo;
|
||||
// Will reject the promise to notify the DLL consumer that we are done.
|
||||
void DisconnectListeners(nsresult aStatus, nsresult aLoadGroupStatus);
|
||||
void DisconnectListeners(nsresult aStatus, nsresult aLoadGroupStatus,
|
||||
bool aSwitchedProcess = false);
|
||||
|
||||
// Called when we were created without a document channel, and creation has
|
||||
// failed, and won't ever be attached.
|
||||
@ -241,8 +261,7 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
|
||||
// Initiates the switch from DocumentChannel to the real protocol-specific
|
||||
// channel, and ensures that RedirectToRealChannelFinished is called when
|
||||
// this is complete.
|
||||
void TriggerRedirectToRealChannel(
|
||||
const Maybe<uint64_t>& aDestinationProcess = Nothing());
|
||||
void TriggerRedirectToRealChannel(const Maybe<uint64_t>& aDestinationProcess);
|
||||
|
||||
// Called once the content-process side on setting up a replacement
|
||||
// channel is complete. May wait for the new parent channel to
|
||||
@ -281,8 +300,6 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
|
||||
dom::CanonicalBrowsingContext* aBrowsingContext,
|
||||
nsDocShellLoadState* aLoadState, uint64_t aOuterWindowId);
|
||||
|
||||
dom::CanonicalBrowsingContext* GetBrowsingContext();
|
||||
|
||||
void AddURIVisit(nsIChannel* aChannel, uint32_t aLoadFlags);
|
||||
bool HasCrossOriginOpenerPolicyMismatch() const;
|
||||
void ApplyPendingFunctions(nsIParentChannel* aChannel) const;
|
||||
@ -456,17 +473,20 @@ class DocumentLoadListener : public nsIInterfaceRequestor,
|
||||
|
||||
Maybe<nsCString> mOriginalUriString;
|
||||
|
||||
bool mSupportsRedirectToRealChannel = true;
|
||||
|
||||
// The process id of the content process that we are being called from
|
||||
// or 0 initiated from a parent process load.
|
||||
base::ProcessId mOtherPid = 0;
|
||||
|
||||
void RejectOpenPromise(nsresult aStatus, nsresult aLoadGroupStatus,
|
||||
const char* aLocation) {
|
||||
bool aSwitchedProcess, const char* aLocation) {
|
||||
// It is possible for mOpenPromise to not be set if AsyncOpen failed and
|
||||
// the DocumentChannel got canceled.
|
||||
if (!mOpenPromiseResolved && mOpenPromise) {
|
||||
mOpenPromise->Reject(OpenPromiseFailedType({aStatus, aLoadGroupStatus}),
|
||||
aLocation);
|
||||
mOpenPromise->Reject(
|
||||
OpenPromiseFailedType({aStatus, aLoadGroupStatus, aSwitchedProcess}),
|
||||
aLocation);
|
||||
mOpenPromiseResolved = true;
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ class ParentChannelListener final : public nsIInterfaceRequestor,
|
||||
// Called to set a new listener which replaces the old one after a redirect.
|
||||
void SetListenerAfterRedirect(nsIStreamListener* aListener);
|
||||
|
||||
dom::CanonicalBrowsingContext* GetBrowsingContext() {
|
||||
dom::CanonicalBrowsingContext* GetBrowsingContext() const {
|
||||
return mBrowsingContext;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user