Bug 1689601, try to use bfcache for top level pages, r=peterv

Differential Revision: https://phabricator.services.mozilla.com/D105238
This commit is contained in:
Olli Pettay 2021-03-02 12:13:20 +00:00
parent d9ede66ac9
commit 911d46a8b6
20 changed files with 498 additions and 38 deletions

View File

@ -2621,6 +2621,25 @@ void BrowsingContext::DidSet(FieldIndex<IDX_UserAgentOverride>) {
});
}
bool BrowsingContext::CanSet(FieldIndex<IDX_IsInBFCache>, bool,
ContentParent* aSource) {
return IsTop() && !aSource && StaticPrefs::fission_bfcacheInParent();
}
void BrowsingContext::DidSet(FieldIndex<IDX_IsInBFCache>) {
MOZ_RELEASE_ASSERT(StaticPrefs::fission_bfcacheInParent());
MOZ_DIAGNOSTIC_ASSERT(IsTop());
const bool isInBFCache = GetIsInBFCache();
PreOrderWalk([&](BrowsingContext* aContext) {
nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell();
if (shell) {
static_cast<nsDocShell*>(shell.get())
->FirePageHideShowNonRecursive(!isInBFCache);
}
});
}
void BrowsingContext::SetCustomPlatform(const nsAString& aPlatform,
ErrorResult& aRv) {
Top()->SetPlatformOverride(aPlatform, aRv);

View File

@ -197,7 +197,8 @@ enum class ExplicitActiveStatus : uint8_t {
FIELD(HasMainMediaController, bool) \
/* The number of entries added to the session history because of this \
* browsing context. */ \
FIELD(HistoryEntryCount, uint32_t)
FIELD(HistoryEntryCount, uint32_t) \
FIELD(IsInBFCache, bool)
// BrowsingContext, in this context, is the cross process replicated
// environment in which information about documents is stored. In
@ -1035,6 +1036,9 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
void DidSet(FieldIndex<IDX_TextZoom>, float aOldValue);
void DidSet(FieldIndex<IDX_AuthorStyleDisabledDefault>);
bool CanSet(FieldIndex<IDX_IsInBFCache>, bool, ContentParent* aSource);
void DidSet(FieldIndex<IDX_IsInBFCache>);
// True if the process attemping to set field is the same as the owning
// process. Deprecated. New code that might use this should generally be moved
// to WindowContext or be settable only by the parent process.

View File

@ -24,6 +24,7 @@
#include "mozilla/ipc/ProtocolUtils.h"
#include "mozilla/net/DocumentLoadListener.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/StaticPrefs_fission.h"
#include "nsIWebNavigation.h"
#include "mozilla/MozPromiseInlines.h"
#include "nsDocShell.h"
@ -158,7 +159,8 @@ void CanonicalBrowsingContext::MaybeAddAsProgressListener(
void CanonicalBrowsingContext::ReplacedBy(CanonicalBrowsingContext* aNewContext,
const RemotenessChangeState& aState) {
MOZ_ASSERT(!aNewContext->EverAttached());
MOZ_ASSERT(!aNewContext->mWebProgress);
MOZ_ASSERT(!aNewContext->mSessionHistory);
MOZ_ASSERT(IsTop() && aNewContext->IsTop());
if (mStatusFilter) {
mStatusFilter->RemoveProgressListener(mWebProgress);
@ -170,8 +172,20 @@ void CanonicalBrowsingContext::ReplacedBy(CanonicalBrowsingContext* aNewContext,
aNewContext->mFields.SetWithoutSyncing<IDX_ExplicitActive>(
GetExplicitActive());
// XXXBFCache name handling is still a bit broken in Fission in general,
// at least in case name should be cleared.
if (aState.mTryUseBFCache) {
aNewContext->mFields.SetWithoutSyncing<IDX_Name>(GetName());
aNewContext->mFields.SetWithoutSyncing<IDX_HasLoadedNonInitialDocument>(
GetHasLoadedNonInitialDocument());
}
if (mSessionHistory) {
mSessionHistory->SetBrowsingContext(aNewContext);
if (StaticPrefs::fission_bfcacheInParent()) {
// XXXBFCache Should we clear the epoch always?
mSessionHistory->SetEpoch(0, Nothing());
}
mSessionHistory.swap(aNewContext->mSessionHistory);
RefPtr<ChildSHistory> childSHistory = ForgetChildSHistory();
aNewContext->SetChildSHistory(childSHistory);
@ -307,6 +321,11 @@ SessionHistoryEntry* CanonicalBrowsingContext::GetActiveSessionHistoryEntry() {
return mActiveEntry;
}
void CanonicalBrowsingContext::SetActiveSessionHistoryEntry(
SessionHistoryEntry* aEntry) {
mActiveEntry = aEntry;
}
bool CanonicalBrowsingContext::HasHistoryEntry(nsISHEntry* aEntry) {
// XXX Should we check also loading entries?
return aEntry && mActiveEntry == aEntry;
@ -1511,6 +1530,12 @@ bool CanonicalBrowsingContext::SupportsLoadingInParent(
return false;
}
// Session-history-in-parent implementation relies currently on getting a
// round trip through a child process.
if (aLoadState->LoadIsFromSessionHistory()) {
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.
@ -1584,12 +1609,6 @@ bool CanonicalBrowsingContext::AttemptSpeculativeLoadInParent(
return false;
}
// Session-history-in-parent implementation relies currently on getting a
// round trip through a child process.
if (aLoadState->LoadIsFromSessionHistory()) {
return false;
}
// If we successfully open the DocumentChannel, then it'll register
// itself using aLoadIdentifier and be kept alive until it completes
// loading.

View File

@ -120,6 +120,7 @@ class CanonicalBrowsingContext final : public BrowsingContext {
nsISHistory* GetSessionHistory();
SessionHistoryEntry* GetActiveSessionHistoryEntry();
void SetActiveSessionHistoryEntry(SessionHistoryEntry* aEntry);
UniquePtr<LoadingSessionHistoryInfo> CreateLoadingSessionHistoryEntryForLoad(
nsDocShellLoadState* aLoadState, nsIChannel* aChannel);

View File

@ -1195,6 +1195,64 @@ void nsDocShell::FirePageHideNotificationInternal(
}
}
void nsDocShell::FirePageHideShowNonRecursive(bool aShow) {
MOZ_ASSERT(StaticPrefs::fission_bfcacheInParent());
if (!mContentViewer) {
return;
}
// Emulate what non-SHIP BFCache does too. In pageshow case
// add and remove a request and before that call SetCurrentURI to get
// the location change notification.
// For pagehide, set mFiredUnloadEvent to true, so that unload doesn't fire.
nsCOMPtr<nsIContentViewer> contentViewer(mContentViewer);
if (aShow) {
mFiredUnloadEvent = false;
RefPtr<Document> doc = contentViewer->GetDocument();
if (doc) {
if (mBrowsingContext->IsTop()) {
doc->NotifyPossibleTitleChange(false);
if (mScriptGlobal && mScriptGlobal->GetCurrentInnerWindowInternal()) {
// XXXBFCache Resume doesn't go through oop iframes.
mScriptGlobal->GetCurrentInnerWindowInternal()->Thaw();
}
}
nsCOMPtr<nsIChannel> channel = doc->GetChannel();
if (channel) {
SetCurrentURI(doc->GetDocumentURI(), channel, true, 0);
mEODForCurrentDocument = false;
mIsRestoringDocument = true;
mLoadGroup->AddRequest(channel, nullptr);
mLoadGroup->RemoveRequest(channel, nullptr, NS_OK);
mIsRestoringDocument = false;
}
RefPtr<PresShell> presShell = GetPresShell();
if (presShell) {
// XXXBFcache Thaw doesn't deal with OOP iframes.
presShell->Thaw();
}
}
} else if (!mFiredUnloadEvent) {
// XXXBFCache check again that the page can enter bfcache.
// XXXBFCache should mTiming->NotifyUnloadEventStart()/End() be called here?
mFiredUnloadEvent = true;
contentViewer->PageHide(false);
if (mBrowsingContext->IsTop()) {
if (mScriptGlobal && mScriptGlobal->GetCurrentInnerWindowInternal()) {
// XXXBFCache Resume doesn't go through oop iframes.
mScriptGlobal->GetCurrentInnerWindowInternal()->Freeze();
}
}
RefPtr<PresShell> presShell = GetPresShell();
if (presShell) {
// XXXBFcache Freeze doesn't deal with OOP iframes.
presShell->Freeze();
}
}
}
nsresult nsDocShell::Dispatch(TaskCategory aCategory,
already_AddRefed<nsIRunnable>&& aRunnable) {
nsCOMPtr<nsIRunnable> runnable(aRunnable);
@ -6898,6 +6956,7 @@ bool nsDocShell::CanSavePresentation(uint32_t aLoadType,
// Only save presentation for "normal" loads and link loads. Anything else
// probably wants to refetch the page, so caching the old presentation
// would be incorrect.
// XXXBFCache in parent needs something like this!
if (aLoadType != LOAD_NORMAL && aLoadType != LOAD_HISTORY &&
aLoadType != LOAD_LINK && aLoadType != LOAD_STOP_CONTENT &&
aLoadType != LOAD_STOP_CONTENT_AND_REPLACE &&

View File

@ -937,6 +937,8 @@ class nsDocShell final : public nsDocLoader,
void FirePageHideNotificationInternal(bool aIsUnload,
bool aSkipCheckingDynEntries);
void FirePageHideShowNonRecursive(bool aShow);
nsresult Dispatch(mozilla::TaskCategory aCategory,
already_AddRefed<nsIRunnable>&& aRunnable);

View File

@ -1349,7 +1349,8 @@ SHEntrySharedParentState* SessionHistoryEntry::SharedInfo() const {
void SessionHistoryEntry::SetFrameLoader(nsFrameLoader* aFrameLoader) {
MOZ_ASSERT_IF(aFrameLoader, !SharedInfo()->mFrameLoader);
MOZ_RELEASE_ASSERT(StaticPrefs::fission_bfcacheInParent());
// If the pref is disabled, we still allow evicting the existing entries.
MOZ_RELEASE_ASSERT(!aFrameLoader || StaticPrefs::fission_bfcacheInParent());
SharedInfo()->mFrameLoader = aFrameLoader;
if (aFrameLoader) {
// When a new frameloader is stored, try to evict some older

View File

@ -12,6 +12,7 @@
#include "nsCOMArray.h"
#include "nsComponentManagerUtils.h"
#include "nsDocShell.h"
#include "nsFrameLoaderOwner.h"
#include "nsHashKeys.h"
#include "nsIContentViewer.h"
#include "nsIDocShell.h"
@ -31,12 +32,15 @@
#include "prsystem.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/BrowsingContextGroup.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/Element.h"
#include "mozilla/LinkedList.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPrefs_fission.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "nsIWebNavigation.h"
@ -51,12 +55,16 @@ using namespace mozilla::dom;
"browser.sessionhistory.max_total_viewers"
#define CONTENT_VIEWER_TIMEOUT_SECONDS \
"browser.sessionhistory.contentViewerTimeout"
// Observe fission.bfcacheInParent so that BFCache can be enabled/disabled when
// the pref is changed.
#define PREF_FISSION_BFCACHEINPARENT "fission.bfcacheInParent"
// Default this to time out unused content viewers after 30 minutes
#define CONTENT_VIEWER_TIMEOUT_SECONDS_DEFAULT (30 * 60)
static const char* kObservedPrefs[] = {
PREF_SHISTORY_SIZE, PREF_SHISTORY_MAX_TOTAL_VIEWERS, nullptr};
static const char* kObservedPrefs[] = {PREF_SHISTORY_SIZE,
PREF_SHISTORY_MAX_TOTAL_VIEWERS,
PREF_FISSION_BFCACHEINPARENT, nullptr};
static int32_t gHistoryMaxSize = 50;
// List of all SHistory objects, used for content viewer cache eviction
@ -76,6 +84,7 @@ LazyLogModule gSHistoryLog("nsSHistory");
#define LOG(format) MOZ_LOG(gSHistoryLog, mozilla::LogLevel::Debug, format)
extern mozilla::LazyLogModule gPageCacheLog;
extern mozilla::LazyLogModule gSHIPBFCacheLog;
// This macro makes it easier to print a log message which includes a URI's
// spec. Example use:
@ -342,7 +351,8 @@ uint32_t nsSHistory::CalcMaxTotalViewers() {
// static
void nsSHistory::UpdatePrefs() {
Preferences::GetInt(PREF_SHISTORY_SIZE, &gHistoryMaxSize);
if (mozilla::SessionHistoryInParent()) {
if (mozilla::SessionHistoryInParent() &&
!StaticPrefs::fission_bfcacheInParent()) {
sHistoryMaxTotalViewers = 0;
return;
}
@ -1165,10 +1175,114 @@ nsSHistory::EvictAllContentViewers() {
return NS_OK;
}
/* static */
void nsSHistory::LoadURIOrBFCache(LoadEntryResult& aLoadEntry) {
if (mozilla::SessionHistoryInParent() &&
StaticPrefs::fission_bfcacheInParent() &&
aLoadEntry.mBrowsingContext->IsTop()) {
MOZ_ASSERT(XRE_IsParentProcess());
RefPtr<nsDocShellLoadState> loadState = aLoadEntry.mLoadState;
RefPtr<CanonicalBrowsingContext> canonicalBC =
aLoadEntry.mBrowsingContext->Canonical();
nsCOMPtr<SessionHistoryEntry> she = do_QueryInterface(loadState->SHEntry());
nsCOMPtr<SessionHistoryEntry> currentShe =
canonicalBC->GetActiveSessionHistoryEntry();
MOZ_ASSERT(she);
RefPtr<nsFrameLoader> frameLoader = she->GetFrameLoader();
if (canonicalBC->Group()->Toplevels().Length() == 1 && frameLoader &&
(!currentShe || she->SharedInfo() != currentShe->SharedInfo())) {
nsTArray<RefPtr<PContentParent::CanSavePresentationPromise>>
canSavePromises;
canonicalBC->Group()->EachParent([&](ContentParent* aParent) {
RefPtr<PContentParent::CanSavePresentationPromise> canSave =
aParent->SendCanSavePresentation(canonicalBC, Nothing());
canSavePromises.AppendElement(canSave);
});
// Check if the current page can enter bfcache.
PContentParent::CanSavePresentationPromise::All(
GetCurrentSerialEventTarget(), canSavePromises)
->Then(
GetMainThreadSerialEventTarget(), __func__,
[canonicalBC, loadState, she](const nsTArray<bool> aCanSaves) {
bool canSave = !aCanSaves.Contains(false);
MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug,
("nsSHistory::LoadURIOrBFCache "
"saving presentation=%i",
canSave));
nsCOMPtr<nsFrameLoaderOwner> frameLoaderOwner =
do_QueryInterface(canonicalBC->GetEmbedderElement());
if (frameLoaderOwner) {
RefPtr<nsFrameLoader> fl = she->GetFrameLoader();
if (fl) {
she->SetFrameLoader(nullptr);
RefPtr<BrowsingContext> loadingBC =
fl->GetMaybePendingBrowsingContext();
if (loadingBC) {
RefPtr<nsFrameLoader> currentFrameLoader =
frameLoaderOwner->GetFrameLoader();
// The current page can be bfcached, store the
// nsFrameLoader in the current SessionHistoryEntry.
if (canSave &&
canonicalBC->GetActiveSessionHistoryEntry()) {
canonicalBC->GetActiveSessionHistoryEntry()
->SetFrameLoader(currentFrameLoader);
Unused << canonicalBC->SetIsInBFCache(true);
}
// ReplacedBy will swap the entry back.
canonicalBC->SetActiveSessionHistoryEntry(she);
loadingBC->Canonical()->SetActiveSessionHistoryEntry(
nullptr);
RemotenessChangeState state;
canonicalBC->ReplacedBy(loadingBC->Canonical(), state);
frameLoaderOwner->ReplaceFrameLoader(fl);
// The old page can't be stored in the bfcache,
// destroy the nsFrameLoader.
if (!canSave && currentFrameLoader) {
currentFrameLoader->Destroy();
}
// The current active entry should not store
// nsFrameLoader.
loadingBC->Canonical()
->GetSessionHistory()
->UpdateIndex();
loadingBC->Canonical()->HistoryCommitIndexAndLength();
Unused << loadingBC->SetIsInBFCache(false);
// ResetSHEntryHasUserInteractionCache(); ?
// browser.navigation.requireUserInteraction is still
// disabled everywhere.
return;
}
}
}
// Fall back to do a normal load.
canonicalBC->LoadURI(loadState, false);
},
[canonicalBC, loadState](mozilla::ipc::ResponseRejectReason) {
MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug,
("nsSHistory::LoadURIOrBFCache "
"error in trying to save presentation"));
canonicalBC->LoadURI(loadState, false);
});
return;
}
if (frameLoader) {
she->SetFrameLoader(nullptr);
frameLoader->Destroy();
}
}
aLoadEntry.mBrowsingContext->LoadURI(aLoadEntry.mLoadState, false);
}
/* static */
void nsSHistory::LoadURIs(nsTArray<LoadEntryResult>& aLoadResults) {
for (LoadEntryResult& loadEntry : aLoadResults) {
loadEntry.mBrowsingContext->LoadURI(loadEntry.mLoadState, false);
LoadURIOrBFCache(loadEntry);
}
}

View File

@ -154,6 +154,7 @@ class nsSHistory : public mozilla::LinkedListElement<nsSHistory>,
};
static void LoadURIs(nsTArray<LoadEntryResult>& aLoadResults);
static void LoadURIOrBFCache(LoadEntryResult& aLoadEntry);
// If this doesn't return an error then either aLoadResult is set to nothing,
// in which case the caller should ignore the load, or it returns a valid

View File

@ -113,6 +113,7 @@
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/StaticPrefs_docshell.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_fission.h"
#include "mozilla/StaticPrefs_full_screen_api.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/StaticPrefs_network.h"
@ -10784,13 +10785,15 @@ bool Document::CanSavePresentation(nsIRequest* aNewRequest,
}
}
// BFCache is currently not compatible with remote subframes (bug 1609324)
if (RefPtr<BrowsingContext> browsingContext = GetBrowsingContext()) {
for (auto& child : browsingContext->Children()) {
if (!child->IsInProcess()) {
aBFCacheCombo |= BFCacheStatus::CONTAINS_REMOTE_SUBFRAMES;
ret = false;
break;
if (!StaticPrefs::fission_bfcacheInParent()) {
// BFCache is currently not compatible with remote subframes (bug 1609324)
if (RefPtr<BrowsingContext> browsingContext = GetBrowsingContext()) {
for (auto& child : browsingContext->Children()) {
if (!child->IsInProcess()) {
aBFCacheCombo |= BFCacheStatus::CONTAINS_REMOTE_SUBFRAMES;
ret = false;
break;
}
}
}
}

View File

@ -193,12 +193,17 @@ nsFrameLoader::nsFrameLoader(Element* aOwner, BrowsingContext* aBrowsingContext,
mWillChangeProcess(false),
mObservingOwnerContent(false),
mTabProcessCrashFired(false),
mNotifyingCrash(false) {}
mNotifyingCrash(false) {
nsCOMPtr<nsFrameLoaderOwner> owner = do_QueryInterface(aOwner);
owner->AttachFrameLoader(this);
}
nsFrameLoader::~nsFrameLoader() {
if (mMessageManager) {
mMessageManager->Disconnect();
}
MOZ_ASSERT(!mOwnerContent);
MOZ_RELEASE_ASSERT(mDestroyCalled);
}
@ -1870,6 +1875,9 @@ void nsFrameLoader::StartDestroy(bool aForProcessSwitch) {
!doc->InUnlinkOrDeletion();
doc->SetSubDocumentFor(mOwnerContent, nullptr);
MaybeUpdatePrimaryBrowserParent(eBrowserParentRemoved);
nsCOMPtr<nsFrameLoaderOwner> owner = do_QueryInterface(mOwnerContent);
owner->FrameLoaderDestroying(this);
SetOwnerContent(nullptr);
}
@ -2057,8 +2065,17 @@ void nsFrameLoader::SetOwnerContent(Element* aContent) {
mObservingOwnerContent = false;
mOwnerContent->RemoveMutationObserver(this);
}
if (RefPtr<nsFrameLoaderOwner> owner = do_QueryObject(mOwnerContent)) {
owner->DeattachFrameLoader(this);
}
mOwnerContent = aContent;
if (RefPtr<nsFrameLoaderOwner> owner = do_QueryObject(mOwnerContent)) {
owner->AttachFrameLoader(this);
}
if (mSessionStoreListener && mOwnerContent) {
// mOwnerContent will only be null when the frame loader is being destroyed,
// so the session store listener will be destroyed along with it.

View File

@ -19,6 +19,7 @@
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/LinkedList.h"
#include "mozilla/RefPtr.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/Nullable.h"
@ -98,7 +99,8 @@ typedef struct _GtkWidget GtkWidget;
class nsFrameLoader final : public nsStubMutationObserver,
public mozilla::dom::ipc::MessageManagerCallback,
public nsWrapperCache {
public nsWrapperCache,
public mozilla::LinkedListElement<nsFrameLoader> {
friend class AutoResetInShow;
friend class AutoResetInFrameSwap;
friend class nsFrameLoaderOwner;

View File

@ -11,6 +11,7 @@
#include "nsSubDocumentFrame.h"
#include "nsQueryObject.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/Logging.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/FrameLoaderBinding.h"
@ -24,6 +25,8 @@
#include "mozilla/StaticPrefs_fission.h"
#include "mozilla/EventStateManager.h"
extern mozilla::LazyLogModule gSHIPBFCacheLog;
using namespace mozilla;
using namespace mozilla::dom;
@ -126,15 +129,36 @@ void nsFrameLoaderOwner::ChangeRemotenessCommon(
// or want, so we use the initial (possibly pending) browsing context
// directly, instead.
bc = mFrameLoader->GetMaybePendingBrowsingContext();
if (aContextType == ChangeRemotenessContextType::PRESERVE) {
mFrameLoader->SetWillChangeProcess();
networkCreated = mFrameLoader->IsNetworkCreated();
MOZ_ASSERT_IF(aState.mTryUseBFCache, aState.mReplaceBrowsingContext);
if (aState.mTryUseBFCache) {
if (bc) {
SessionHistoryEntry* she =
bc->Canonical()->GetActiveSessionHistoryEntry();
if (she) {
MOZ_LOG(
gSHIPBFCacheLog, LogLevel::Debug,
("nsFrameLoaderOwner::ChangeRemotenessCommon: store the old "
"page in bfcache"));
Unused << bc->SetIsInBFCache(true);
she->SetFrameLoader(mFrameLoader);
// Session history owns now the frameloader.
mFrameLoader = nullptr;
}
}
}
// Preserve the networkCreated status, as nsDocShells created after a
// process swap may shouldn't change their dynamically-created status.
networkCreated = mFrameLoader->IsNetworkCreated();
mFrameLoader->Destroy(aSwitchingInProgressLoad);
mFrameLoader = nullptr;
if (mFrameLoader) {
if (aContextType == ChangeRemotenessContextType::PRESERVE) {
mFrameLoader->SetWillChangeProcess();
}
// Preserve the networkCreated status, as nsDocShells created after a
// process swap may shouldn't change their dynamically-created status.
mFrameLoader->Destroy(aSwitchingInProgressLoad);
mFrameLoader = nullptr;
}
}
mFrameLoader = nsFrameLoader::Recreate(
@ -154,34 +178,38 @@ void nsFrameLoaderOwner::ChangeRemotenessCommon(
}
}
ChangeFrameLoaderCommon(owner);
}
void nsFrameLoaderOwner::ChangeFrameLoaderCommon(Element* aOwner) {
// Now that we've got a new FrameLoader, we need to reset our
// nsSubDocumentFrame to use the new FrameLoader.
if (nsSubDocumentFrame* ourFrame = do_QueryFrame(owner->GetPrimaryFrame())) {
if (nsSubDocumentFrame* ourFrame = do_QueryFrame(aOwner->GetPrimaryFrame())) {
ourFrame->ResetFrameLoader();
}
// If the element is focused, or the current mouse over target then
// we need to update that state for the new BrowserParent too.
if (nsFocusManager* fm = nsFocusManager::GetFocusManager()) {
if (fm->GetFocusedElement() == owner) {
fm->ActivateRemoteFrameIfNeeded(*owner,
if (fm->GetFocusedElement() == aOwner) {
fm->ActivateRemoteFrameIfNeeded(*aOwner,
nsFocusManager::GenerateFocusActionId());
}
}
if (owner->GetPrimaryFrame()) {
if (aOwner->GetPrimaryFrame()) {
EventStateManager* eventManager =
owner->GetPrimaryFrame()->PresContext()->EventStateManager();
eventManager->RecomputeMouseEnterStateForRemoteFrame(*owner);
aOwner->GetPrimaryFrame()->PresContext()->EventStateManager();
eventManager->RecomputeMouseEnterStateForRemoteFrame(*aOwner);
}
if (owner->IsXULElement()) {
if (aOwner->IsXULElement()) {
// Assuming this element is a XULFrameElement, once we've reset our
// FrameLoader, fire an event to act like we've recreated ourselves, similar
// to what XULFrameElement does after rebinding to the tree.
// ChromeOnlyDispatch is turns on to make sure this isn't fired into
// content.
(new mozilla::AsyncEventDispatcher(owner, u"XULFrameLoaderCreated"_ns,
(new mozilla::AsyncEventDispatcher(aOwner, u"XULFrameLoaderCreated"_ns,
mozilla::CanBubble::eYes,
mozilla::ChromeOnlyDispatch::eYes))
->RunDOMEventWhenSafe();
@ -283,3 +311,42 @@ void nsFrameLoaderOwner::SubframeCrashed() {
/* inProgress */ false, /* isRemote */ false,
/* group */ nullptr, frameLoaderInit, IgnoreErrors());
}
void nsFrameLoaderOwner::ReplaceFrameLoader(nsFrameLoader* aNewFrameLoader) {
MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug,
("nsFrameLoaderOwner::ReplaceFrameLoader: Replace frameloader"));
mFrameLoader = aNewFrameLoader;
if (auto* browserParent = mFrameLoader->GetBrowserParent()) {
browserParent->AddWindowListeners();
browserParent->ResumeProgressEvents();
}
RefPtr<Element> owner = do_QueryObject(this);
ChangeFrameLoaderCommon(owner);
}
void nsFrameLoaderOwner::AttachFrameLoader(nsFrameLoader* aFrameLoader) {
mFrameLoaderList.insertBack(aFrameLoader);
}
void nsFrameLoaderOwner::DeattachFrameLoader(nsFrameLoader* aFrameLoader) {
if (aFrameLoader->isInList()) {
MOZ_ASSERT(mFrameLoaderList.contains(aFrameLoader));
aFrameLoader->remove();
}
}
void nsFrameLoaderOwner::FrameLoaderDestroying(nsFrameLoader* aFrameLoader) {
if (aFrameLoader == mFrameLoader) {
while (!mFrameLoaderList.isEmpty()) {
RefPtr<nsFrameLoader> loader = mFrameLoaderList.popFirst();
if (loader != mFrameLoader) {
loader->Destroy();
}
}
} else {
DeattachFrameLoader(aFrameLoader);
}
}

View File

@ -18,6 +18,7 @@ class BrowsingContext;
class BrowsingContextGroup;
class BrowserBridgeChild;
class ContentParent;
class Element;
struct RemotenessOptions;
struct RemotenessChangeState;
} // namespace dom
@ -79,6 +80,12 @@ class nsFrameLoaderOwner : public nsISupports {
void SubframeCrashed();
void ReplaceFrameLoader(nsFrameLoader* aNewFrameLoader);
void AttachFrameLoader(nsFrameLoader* aFrameLoader);
void DeattachFrameLoader(nsFrameLoader* aFrameLoader);
void FrameLoaderDestroying(nsFrameLoader* aFrameLoader);
private:
bool UseRemoteSubframes();
@ -102,9 +109,13 @@ class nsFrameLoaderOwner : public nsISupports {
std::function<void()>& aFrameLoaderInit,
mozilla::ErrorResult& aRv);
void ChangeFrameLoaderCommon(mozilla::dom::Element* aOwner);
protected:
virtual ~nsFrameLoaderOwner() = default;
RefPtr<nsFrameLoader> mFrameLoader;
mozilla::LinkedList<nsFrameLoader> mFrameLoaderList;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsFrameLoaderOwner, NS_FRAMELOADEROWNER_IID)

View File

@ -710,6 +710,7 @@ class BrowserParent final : public PBrowserParent,
// Suspend nsIWebProgressListener events. This is used to block any further
// progress events from the old process when process switching away.
void SuspendProgressEvents() { mSuspendedProgressEvents = true; }
void ResumeProgressEvents() { mSuspendedProgressEvents = false; }
bool CanCancelContentJS(nsIRemoteTab::NavigationType aNavigationType,
int32_t aNavigationIndex,

View File

@ -28,6 +28,7 @@
#include "mozilla/Components.h"
#include "mozilla/HangDetails.h"
#include "mozilla/LoadInfo.h"
#include "mozilla/Logging.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/MemoryTelemetry.h"
#include "mozilla/NullPrincipal.h"
@ -108,6 +109,7 @@
#include "mozilla/media/MediaChild.h"
#include "mozilla/net/CaptivePortalService.h"
#include "mozilla/net/CookieServiceChild.h"
#include "mozilla/net/DocumentChannelChild.h"
#include "mozilla/net/HttpChannelChild.h"
#include "mozilla/net/NeckoChild.h"
#include "mozilla/plugins/PluginInstanceParent.h"
@ -120,7 +122,9 @@
#include "nsFocusManager.h"
#include "nsIConsoleService.h"
#include "nsIInputStreamChannel.h"
#include "nsILoadGroup.h"
#include "nsIOpenWindowInfo.h"
#include "nsISimpleEnumerator.h"
#include "nsIStringBundle.h"
#include "nsIURIMutator.h"
#include "nsQueryObject.h"
@ -290,6 +294,8 @@
# include "mozilla/CodeCoverageHandler.h"
#endif
extern mozilla::LazyLogModule gSHIPBFCacheLog;
using namespace mozilla;
using namespace mozilla::docshell;
using namespace mozilla::dom::ipc;
@ -4243,6 +4249,69 @@ mozilla::ipc::IPCResult ContentChild::RecvDispatchBeforeUnloadToSubtree(
return IPC_OK();
}
mozilla::ipc::IPCResult ContentChild::RecvCanSavePresentation(
const MaybeDiscarded<BrowsingContext>& aTopLevelContext,
Maybe<uint64_t> aDocumentChannelId,
CanSavePresentationResolver&& aResolver) {
if (aTopLevelContext.IsNullOrDiscarded()) {
aResolver(false);
return IPC_OK();
}
bool canSave = true;
// XXXBFCache pass the flags to telemetry.
uint16_t flags = 0;
BrowsingContext* browsingContext = aTopLevelContext.get();
browsingContext->PreOrderWalk([&](BrowsingContext* aContext) {
nsIDocShell* docShell = aContext->GetDocShell();
if (docShell) {
Document* doc = docShell->GetDocument();
if (doc) {
nsIRequest* request = nullptr;
if (aDocumentChannelId.isSome() && aContext->IsTop()) {
nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
if (loadGroup) {
nsCOMPtr<nsISimpleEnumerator> requests;
loadGroup->GetRequests(getter_AddRefs(requests));
bool hasMore = false;
if (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
// If there is any requests, the only one we allow with bfcache
// is the DocumentChannel request.
nsCOMPtr<nsISupports> elem;
requests->GetNext(getter_AddRefs(elem));
nsCOMPtr<nsIIdentChannel> identChannel = do_QueryInterface(elem);
if (identChannel &&
identChannel->ChannelId() == aDocumentChannelId.value()) {
request = identChannel;
}
}
}
}
// Go through also the subdocuments so that flags are collected.
bool canSaveDoc = doc->CanSavePresentation(request, flags, false);
canSave = canSaveDoc && canSave;
if (MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Debug)) {
nsAutoCString uri;
if (doc->GetDocumentURI()) {
uri = doc->GetDocumentURI()->GetSpecOrDefault();
}
MOZ_LOG(
gSHIPBFCacheLog, LogLevel::Debug,
("ContentChild::RecvCanSavePresentation can save presentation "
"[%i] for [%s]",
canSaveDoc, uri.get()));
}
}
}
});
aResolver(canSave);
return IPC_OK();
}
/* static */ void ContentChild::DispatchBeforeUnloadToSubtree(
BrowsingContext* aStartingAt,
const DispatchBeforeUnloadToSubtreeResolver& aResolver) {

View File

@ -824,6 +824,11 @@ class ContentChild final : public PContentChild,
const MaybeDiscarded<BrowsingContext>& aStartingAt,
DispatchBeforeUnloadToSubtreeResolver&& aResolver);
mozilla::ipc::IPCResult RecvCanSavePresentation(
const MaybeDiscarded<BrowsingContext>& aTopLevelContext,
Maybe<uint64_t> aDocumentChannelId,
CanSavePresentationResolver&& aResolve);
public:
static void DispatchBeforeUnloadToSubtree(
BrowsingContext* aStartingAt,

View File

@ -9,6 +9,7 @@ include protocol PBrowser;
include protocol PCompositorManager;
include protocol PContentPermissionRequest;
include protocol PCycleCollectWithLogs;
include protocol PDocumentChannel;
include protocol PExternalHelperApp;
include protocol PHandlerService;
include protocol PFileDescriptorSet;
@ -945,6 +946,9 @@ child:
async DispatchBeforeUnloadToSubtree(MaybeDiscardedBrowsingContext aStartingAt)
returns (PermitUnloadResult result);
async CanSavePresentation(MaybeDiscardedBrowsingContext aTopLevelContext,
uint64_t? aDocumentChannelId) returns (bool success);
// Update the cached list of codec supported in the given process.
async UpdateMediaCodecsSupported(RemoteDecodeIn aLocation, MediaCodecsSupported aSupported);

View File

@ -11,6 +11,7 @@
#include "mozilla/net/HttpBaseChannel.h"
#include "mozilla/net/NeckoChild.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/StaticPrefs_fission.h"
#include "nsHashPropertyBag.h"
#include "nsIHttpChannelInternal.h"
#include "nsIObjectLoadingContent.h"
@ -174,7 +175,14 @@ IPCResult DocumentChannelChild::RecvDisconnectChildListeners(
// 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) {
// XXXBFCache This should be fixed in some better way.
bool disconnectChildListeners = !aSwitchedProcess;
if (!disconnectChildListeners && StaticPrefs::fission_bfcacheInParent()) {
nsDocShell* shell = GetDocShell();
disconnectChildListeners = shell && shell->GetBrowsingContext() &&
shell->GetBrowsingContext()->IsTop();
}
if (disconnectChildListeners) {
DisconnectChildListeners(aStatus, aLoadGroupStatus);
}
return IPC_OK();

View File

@ -46,6 +46,7 @@
#include "nsQueryObject.h"
#include "nsRedirectHistoryEntry.h"
#include "nsSandboxFlags.h"
#include "nsSHistory.h"
#include "nsStringStream.h"
#include "nsURILoader.h"
#include "nsWebNavigationInfo.h"
@ -64,6 +65,8 @@
mozilla::LazyLogModule gDocumentChannelLog("DocumentChannel");
#define LOG(fmt) MOZ_LOG(gDocumentChannelLog, mozilla::LogLevel::Verbose, fmt)
extern mozilla::LazyLogModule gSHIPBFCacheLog;
using namespace mozilla::dom;
namespace mozilla {
@ -1623,6 +1626,18 @@ bool DocumentLoadListener::MaybeTriggerProcessSwitch(
changeState.mReplaceBrowsingContext = true;
}
if (mozilla::SessionHistoryInParent() &&
StaticPrefs::fission_bfcacheInParent() &&
nsSHistory::GetMaxTotalViewers() > 0 && !parentWindow &&
!browsingContext->HadOriginalOpener() &&
browsingContext->Group()->Toplevels().Length() == 1 &&
!changeState.mRemoteType.IsEmpty() &&
browsingContext->GetHasLoadedNonInitialDocument() &&
mLoadStateLoadType != LOAD_ERROR_PAGE) {
changeState.mReplaceBrowsingContext = true;
changeState.mTryUseBFCache = true;
}
LOG(("GetRemoteTypeForPrincipal -> current:%s remoteType:%s",
currentRemoteType.get(), changeState.mRemoteType.get()));
@ -1644,6 +1659,44 @@ bool DocumentLoadListener::MaybeTriggerProcessSwitch(
// If we're doing a document load, we can immediately perform a process
// switch.
if (mIsDocumentLoad) {
if (changeState.mTryUseBFCache && wgp) {
if (RefPtr<BrowserParent> browserParent = wgp->GetBrowserParent()) {
nsTArray<RefPtr<PContentParent::CanSavePresentationPromise>>
canSavePromises;
browsingContext->Group()->EachParent([&](ContentParent* aParent) {
RefPtr<PContentParent::CanSavePresentationPromise> canSave =
aParent->SendCanSavePresentation(browsingContext,
mDocumentChannelId);
canSavePromises.AppendElement(canSave);
});
PContentParent::CanSavePresentationPromise::All(
GetCurrentSerialEventTarget(), canSavePromises)
->Then(
GetMainThreadSerialEventTarget(), __func__,
[self = RefPtr{this}, browsingContext,
changeState](const nsTArray<bool> aCanSaves) mutable {
bool canSave = !aCanSaves.Contains(false);
MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug,
("DocumentLoadListener::MaybeTriggerProcessSwitch "
"saving presentation=%i",
canSave));
changeState.mTryUseBFCache = canSave;
self->TriggerProcessSwitch(browsingContext, changeState);
},
[self = RefPtr{this}, browsingContext,
changeState](ipc::ResponseRejectReason) mutable {
MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug,
("DocumentLoadListener::MaybeTriggerProcessSwitch "
"error in trying to save presentation"));
changeState.mTryUseBFCache = false;
self->TriggerProcessSwitch(browsingContext, changeState);
});
return true;
}
}
changeState.mTryUseBFCache = false;
TriggerProcessSwitch(browsingContext, changeState);
return true;
}