Bug 1696175 - Call DisconnectChildListeners at a later time for BFCache in the parent. r=necko-reviewers,nika

Differential Revision: https://phabricator.services.mozilla.com/D112981
This commit is contained in:
Peter Van der Beken 2021-05-13 15:38:01 +00:00
Родитель bd3999c92b
Коммит 3e785c9c12
5 изменённых файлов: 60 добавлений и 14 удалений

Просмотреть файл

@ -2745,6 +2745,10 @@ void BrowsingContext::DidSet(FieldIndex<IDX_IsInBFCache>) {
[&](BrowsingContext* aContext) { aContext->mIsInBFCache = false; });
}
if (isInBFCache && XRE_IsContentProcess() && mDocShell) {
nsDocShell::Cast(mDocShell)->MaybeDisconnectChildListenersOnPageHide();
}
PreOrderWalk([&](BrowsingContext* aContext) {
nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell();
if (shell) {

Просмотреть файл

@ -88,6 +88,7 @@
#include "mozilla/dom/JSWindowActorChild.h"
#include "mozilla/ipc/ProtocolUtils.h"
#include "mozilla/net/DocumentChannel.h"
#include "mozilla/net/DocumentChannelChild.h"
#include "mozilla/net/ParentChannelWrapper.h"
#include "mozilla/net/UrlClassifierFeatureFactory.h"
#include "ReferrerInfo.h"
@ -383,6 +384,7 @@ nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext,
mFailedLoadType(0),
mJSRunToCompletionDepth(0),
mMetaViewportOverride(nsIDocShell::META_VIEWPORT_OVERRIDE_NONE),
mChannelToDisconnectOnPageHide(0),
mCreatingDocument(false),
#ifdef DEBUG
mInEnsureScriptEnv(false),
@ -4369,6 +4371,11 @@ nsDocShell::Stop(uint32_t aStopFlags) {
// just call Stop() on us as an nsIDocumentLoader... We need fewer
// redundant apis!
Stop();
// Clear out mChannelToDisconnectOnPageHide. This page won't go in the
// BFCache now, and the Stop above will have removed the DocumentChannel
// from the loadgroup.
mChannelToDisconnectOnPageHide = 0;
}
for (auto* child : mChildList.ForwardRange()) {
@ -13625,3 +13632,20 @@ nsDocShell::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
RecordSingleChannelId();
return nsDocLoader::OnStopRequest(aRequest, aStatusCode);
}
void nsDocShell::MaybeDisconnectChildListenersOnPageHide() {
MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
if (mChannelToDisconnectOnPageHide != 0 && mLoadGroup) {
nsCOMPtr<nsISimpleEnumerator> requests;
mLoadGroup->GetRequests(getter_AddRefs(requests));
for (const auto& request : SimpleEnumerator<nsIRequest>(requests)) {
RefPtr<DocumentChannel> channel = do_QueryObject(request);
if (channel && channel->ChannelId() == mChannelToDisconnectOnPageHide) {
static_cast<DocumentChannelChild*>(channel.get())
->DisconnectChildListeners(NS_BINDING_ABORTED, NS_BINDING_ABORTED);
}
}
mChannelToDisconnectOnPageHide = 0;
}
}

Просмотреть файл

@ -1088,6 +1088,12 @@ class nsDocShell final : public nsDocLoader,
void RecordSingleChannelId();
void SetChannelToDisconnectOnPageHide(uint64_t aChannelId) {
MOZ_ASSERT(mChannelToDisconnectOnPageHide == 0);
mChannelToDisconnectOnPageHide = aChannelId;
}
void MaybeDisconnectChildListenersOnPageHide();
private: // data members
nsString mTitle;
nsCString mOriginalUriString;
@ -1227,6 +1233,8 @@ class nsDocShell final : public nsDocLoader,
// See WindowGlobalParent::mSingleChannelId.
mozilla::Maybe<uint64_t> mSingleChannelId;
uint64_t mChannelToDisconnectOnPageHide;
// The following two fields cannot be declared as bit fields
// because of uses with AutoRestore.
bool mCreatingDocument; // (should be) debugging only

Просмотреть файл

@ -55,6 +55,9 @@ class DocumentChannel : public nsIIdentChannel {
mInitialClientInfo = aInfo;
}
void DisconnectChildListeners(const nsresult& aStatus,
const nsresult& aLoadGroupStatus);
/**
* Will create the appropriate document channel:
* Either a DocumentChannelChild if called from the content process or
@ -77,8 +80,6 @@ class DocumentChannel : public nsIIdentChannel {
bool aIsXFOError);
void ShutdownListeners(nsresult aStatusCode);
void DisconnectChildListeners(const nsresult& aStatus,
const nsresult& aLoadGroupStatus);
virtual void DeleteIPDL() {}
nsDocShell* GetDocShell();

Просмотреть файл

@ -192,20 +192,29 @@ IPCResult DocumentChannelChild::RecvFailedAsyncOpen(
IPCResult DocumentChannelChild::RecvDisconnectChildListeners(
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.
// XXXBFCache This should be fixed in some better way.
bool disconnectChildListeners = !aSwitchedProcess;
if (!disconnectChildListeners && mozilla::BFCacheInParent()) {
nsDocShell* shell = GetDocShell();
disconnectChildListeners = shell && shell->GetBrowsingContext() &&
shell->GetBrowsingContext()->IsTop();
}
if (disconnectChildListeners) {
// If this disconnect is not due to a process switch, perform the disconnect
// immediately.
if (!aSwitchedProcess) {
DisconnectChildListeners(aStatus, aLoadGroupStatus);
return IPC_OK();
}
// Otherwise, the disconnect will occur later using some other mechanism,
// depending on what's happening to the loading DocShell. If this is a
// toplevel navigation, and this BrowsingContext enters the BFCache, we will
// cancel this channel when the PageHide event is firing, whereas if it does
// not enter BFCache (e.g. due to being an object, subframe or non-bfcached
// toplevel navigation), we will cancel this channel when the DocShell is
// destroyed.
nsDocShell* shell = GetDocShell();
if (mLoadInfo->GetExternalContentPolicyType() ==
ExtContentPolicy::TYPE_DOCUMENT &&
shell) {
MOZ_ASSERT(shell->GetBrowsingContext()->IsTop());
// Tell the DocShell which channel to cancel if it enters the BFCache.
shell->SetChannelToDisconnectOnPageHide(mChannelId);
}
return IPC_OK();
}