Bug 1649349 - Don't delay rejecting DocumentLoadListener's Open promise when we switch process. r=jya

Differential Revision: https://phabricator.services.mozilla.com/D81648
This commit is contained in:
Matt Woodrow 2020-06-30 19:19:59 +00:00
parent 8ebce5b5a8
commit ff289259d3
6 changed files with 49 additions and 31 deletions

View File

@ -123,8 +123,15 @@ IPCResult DocumentChannelChild::RecvFailedAsyncOpen(
}
IPCResult DocumentChannelChild::RecvDisconnectChildListeners(
const nsresult& aStatus, const nsresult& aLoadGroupStatus) {
DisconnectChildListeners(aStatus, aLoadGroupStatus);
const nsresult& aStatus, const nsresult& aLoadGroupStatus,
bool aSwitchedProcess) {
// If this is a normal failure, then we want to disconnect our listeners and
// notify them of the failure. If this is a process switch, then we can just
// ignore it silently, and trust that the switch will shut down our docshell
// and cancel us when it's ready.
if (!aSwitchedProcess) {
DisconnectChildListeners(aStatus, aLoadGroupStatus);
}
return IPC_OK();
}

View File

@ -38,7 +38,8 @@ class DocumentChannelChild final : public DocumentChannel,
mozilla::ipc::IPCResult RecvFailedAsyncOpen(const nsresult& aStatusCode);
mozilla::ipc::IPCResult RecvDisconnectChildListeners(
const nsresult& aStatus, const nsresult& aLoadGroupStatus);
const nsresult& aStatus, const nsresult& aLoadGroupStatus,
bool aSwitchedProcess);
mozilla::ipc::IPCResult RecvDeleteSelf();

View File

@ -79,11 +79,11 @@ bool DocumentChannelParent::Init(dom::CanonicalBrowsingContext* aContext,
self->mDocumentLoadListener = nullptr;
},
[self](DocumentLoadListener::OpenPromiseFailedType&& aRejectValue) {
if (!self->CanSend()) {
return;
if (self->CanSend()) {
Unused << self->SendDisconnectChildListeners(
aRejectValue.mStatus, aRejectValue.mLoadGroupStatus,
aRejectValue.mSwitchedProcess);
}
Unused << self->SendDisconnectChildListeners(
aRejectValue.mStatus, aRejectValue.mLoadGroupStatus);
self->mDocumentLoadListener = nullptr;
});

View File

@ -14,6 +14,7 @@
#include "mozilla/StaticPrefs_extensions.h"
#include "mozilla/StaticPrefs_fission.h"
#include "mozilla/StaticPrefs_security.h"
#include "mozilla/dom/BrowserParent.h"
#include "mozilla/dom/BrowsingContextGroup.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/ChildProcessChannelListener.h"
@ -798,14 +799,6 @@ void DocumentLoadListener::Cancel(const nsresult& aStatusCode) {
"aStatusCode=%" PRIx32 " ]",
this, static_cast<uint32_t>(aStatusCode)));
mCancelled = true;
if (mDoingProcessSwitch) {
// If we've already initiated process-switching
// then we can no longer be cancelled and we'll
// disconnect the old listeners when done.
return;
}
if (mChannel) {
mChannel->Cancel(aStatusCode);
}
@ -826,12 +819,16 @@ void DocumentLoadListener::DisconnectListeners(nsresult aStatus,
Disconnect();
// If we're not going to send anything else to the content process, and
// we haven't yet consumed a stream filter promise, then we're never going
// to.
// TODO: This might be because we retargeted the stream to the download
// handler or similar. Do we need to attach a stream filter to that?
mStreamFilterRequests.Clear();
if (!aSwitchedProcess) {
// If we're not going to send anything else to the content process, and
// we haven't yet consumed a stream filter promise, then we're never going
// to. If we're disconnecting the old content process due to a proces
// switch, then we can rely on FinishReplacementChannelSetup being called
// (even if the switch failed), so we clear at that point instead.
// TODO: This might be because we retargeted the stream to the download
// handler or similar. Do we need to attach a stream filter to that?
mStreamFilterRequests.Clear();
}
}
void DocumentLoadListener::RedirectToRealChannelFinished(nsresult aRv) {
@ -887,11 +884,7 @@ void DocumentLoadListener::FinishReplacementChannelSetup(nsresult aResult) {
ctx->EndDocumentLoad(false);
}
});
if (mDoingProcessSwitch) {
DisconnectListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED,
NS_SUCCEEDED(aResult));
}
mStreamFilterRequests.Clear();
nsCOMPtr<nsIRedirectChannelRegistrar> registrar =
RedirectChannelRegistrar::GetOrCreate();
@ -1309,8 +1302,8 @@ bool DocumentLoadListener::MaybeTriggerProcessSwitch(
// Get information about the current document loaded in our BrowsingContext.
nsCOMPtr<nsIPrincipal> currentPrincipal;
if (RefPtr<WindowGlobalParent> wgp =
browsingContext->GetCurrentWindowGlobal()) {
RefPtr<WindowGlobalParent> wgp = browsingContext->GetCurrentWindowGlobal();
if (wgp) {
currentPrincipal = wgp->DocumentPrincipal();
}
RefPtr<ContentParent> contentParent = browsingContext->GetContentParent();
@ -1429,7 +1422,18 @@ bool DocumentLoadListener::MaybeTriggerProcessSwitch(
NS_ConvertUTF16toUTF8(currentRemoteType).get(),
NS_ConvertUTF16toUTF8(remoteType).get()));
// We're now committing to a process switch, so we can disconnect from
// the listeners in the old process.
mDoingProcessSwitch = true;
DisconnectListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED, true);
if (wgp) {
if (RefPtr<BrowserParent> browserParent = wgp->GetBrowserParent()) {
// This load has already started, so we want to filter out any 'stop'
// progress events coming from the old process as a result of us
// disconnecting from it.
browserParent->SuspendProgressEventsUntilAfterNextLoadStarts();
}
}
LOG(("Process Switch: Calling ChangeRemoteness"));
browsingContext

View File

@ -46,7 +46,7 @@ child:
// the loadgroup (but aStatus is passed as the parameter to RemoveRequest).
// We do this so we can remove using NS_BINDING_RETARGETED, but still have the
// channel not be in an error state.
async DisconnectChildListeners(nsresult aStatus, nsresult aLoadGroupReason);
async DisconnectChildListeners(nsresult aStatus, nsresult aLoadGroupReason, bool aSwitchedProcess);
// Triggers replacing this DocumentChannel with a 'real' channel (like PHttpChannel),
// and notifies the listener via a redirect to the new channel.

View File

@ -204,8 +204,14 @@ NS_IMETHODIMP ParentProcessDocumentChannel::AsyncOpen(
p->ChainTo(aResolveValue.mPromise.forget(), __func__);
},
[self](DocumentLoadListener::OpenPromiseFailedType&& aRejectValue) {
self->DisconnectChildListeners(aRejectValue.mStatus,
aRejectValue.mLoadGroupStatus);
// If this is a normal failure, then we want to disconnect our listeners
// and notify them of the failure. If this is a process switch, then we
// can just ignore it silently, and trust that the switch will shut down
// our docshell and cancel us when it's ready.
if (!aRejectValue.mSwitchedProcess) {
self->DisconnectChildListeners(aRejectValue.mStatus,
aRejectValue.mLoadGroupStatus);
}
self->RemoveObserver();
});
return NS_OK;