tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 28759ec172192638508c6a0bc478e39404c9c4b4
parent 47014eccd519b247a4f45418ba66d44f5a26bc0c
Author: Andreas Farre <farre@mozilla.com>
Date:   Wed,  3 Dec 2025 17:00:25 +0000

Bug 1986879 - Implement #update-the-navigation-api-entries-for-reactivation. r=smaug

Differential Revision: https://phabricator.services.mozilla.com/D263743

Diffstat:
Mdocshell/base/BrowsingContext.cpp | 177+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Mdocshell/base/BrowsingContext.h | 20+++++++++++++++-----
Mdocshell/base/CanonicalBrowsingContext.cpp | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdocshell/base/CanonicalBrowsingContext.h | 6++++++
Mdocshell/base/nsDocShell.cpp | 25+++++++++++++++++++------
Mdocshell/base/nsDocShell.h | 1+
Mdocshell/base/nsDocShellLoadState.cpp | 8++++++++
Mdocshell/base/nsDocShellLoadState.h | 2++
Mdocshell/shistory/SessionHistoryEntry.h | 2+-
Mdocshell/shistory/nsSHistory.cpp | 29++++++++++++++++++-----------
Mdocshell/shistory/nsSHistory.h | 2+-
Mdom/base/Document.h | 1+
Mdom/base/nsFocusManager.cpp | 5+++--
Mdom/base/nsFrameLoaderOwner.cpp | 2+-
Mdom/ipc/ContentChild.cpp | 33+++++++++++++++++++++++++++++++++
Mdom/ipc/ContentChild.h | 9+++++++++
Mdom/ipc/PContent.ipdl | 8++++++++
Mdom/navigation/Navigation.cpp | 176++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Mdom/navigation/Navigation.h | 12++++++++----
Mdom/navigation/NavigationActivation.cpp | 12++++++++++++
Mdom/navigation/NavigationActivation.h | 5+++++
Mdom/navigation/NavigationHistoryEntry.cpp | 19+++++++++++++++++++
Mdom/navigation/NavigationHistoryEntry.h | 4++++
Dtesting/web-platform/meta/navigation-api/navigation-activation/activation-after-bfcache-cross-origin.html.ini | 4----
Dtesting/web-platform/meta/navigation-api/navigation-activation/activation-after-bfcache.html.ini | 4----
Mtesting/web-platform/meta/navigation-api/navigation-history-entry/entries-after-bfcache.html.ini | 2+-
Mtesting/web-platform/meta/navigation-api/per-entry-events/dispose-after-bfcache.html.ini | 2+-
27 files changed, 503 insertions(+), 137 deletions(-)

diff --git a/docshell/base/BrowsingContext.cpp b/docshell/base/BrowsingContext.cpp @@ -1240,6 +1240,116 @@ bool BrowsingContext::IsInBFCache() const { mParentWindow->TopWindowContext()->GetWindowStateSaved(); } +void BrowsingContext::SetIsInBFCache(bool aIsInBFCache) { + MOZ_DIAGNOSTIC_ASSERT(mozilla::SessionHistoryInParent()); + mIsInBFCache = aIsInBFCache; +} + +void BrowsingContext::DeactivateDocuments() { + MOZ_RELEASE_ASSERT(mozilla::BFCacheInParent()); + MOZ_DIAGNOSTIC_ASSERT(IsTop()); + + if (XRE_IsContentProcess() && mDocShell) { + nsDocShell::Cast(mDocShell)->MaybeDisconnectChildListenersOnPageHide(); + } + + PreOrderWalk([&](BrowsingContext* aContext) { + nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell(); + if (shell) { + nsDocShell::Cast(shell)->FirePageHideShowNonRecursive(false); + } + }); + + PreOrderWalk([&](BrowsingContext* aContext) { + nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell(); + if (shell) { + nsDocShell::Cast(shell)->ThawFreezeNonRecursive(false); + if (nsPresContext* pc = shell->GetPresContext()) { + pc->EventStateManager()->ResetHoverState(); + } + } + aContext->mIsInBFCache = true; + Document* doc = aContext->GetDocument(); + if (doc) { + // Notifying needs to happen after mIsInBFCache is set to true. + doc->NotifyActivityChanged(); + } + }); +} + +static void GetSubframeReactivationData( + BrowsingContext* aBrowsingContext, + Maybe<SessionHistoryInfo>& aReactivatedEntry, + nsTArray<SessionHistoryInfo>& aNewSHEs, + Maybe<SessionHistoryInfo>& aPreviousEntryForActivation) { + // See bug 1991680 + MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug, + "We currently don't know how to reactivate subframes"); +} + +void BrowsingContext::ReactivateDocuments( + const Maybe<SessionHistoryInfo>& aReactivatedEntry, + const nsTArray<SessionHistoryInfo>& aNewSHEs, + const Maybe<SessionHistoryInfo>& aPreviousEntryForActivation) { + UpdateCurrentTopByBrowserId(this); + PreOrderWalk( + [&](BrowsingContext* aContext) MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA { + aContext->mIsInBFCache = false; + nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell(); + // Before doing anything to reactivate, we need to thaw our suspended + // docshells. If we don't, updating the navigation entries can't + // succeed, since we don't have an active document. + if (shell) { + nsDocShell::Cast(shell)->ThawFreezeNonRecursive(true); + } + + // We need to restore our navigation object state before calling + // pageshow. This is ok, since we will fire any + // NavigationHistoryEntry.dispose events async. + if (aContext->IsTop()) { + aContext->UpdateForReactivation(aReactivatedEntry, aNewSHEs, + aPreviousEntryForActivation); + } else { + Maybe<SessionHistoryInfo> reactivatedEntry; + nsTArray<SessionHistoryInfo> newSHEs; + Maybe<SessionHistoryInfo> previousEntryForActivation; + GetSubframeReactivationData(aContext, reactivatedEntry, newSHEs, + previousEntryForActivation); + aContext->UpdateForReactivation(reactivatedEntry, newSHEs, + previousEntryForActivation); + } + }); + + PostOrderWalk([&](BrowsingContext* aContext) { + nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell(); + if (shell) { + nsDocShell::Cast(shell)->FirePageHideShowNonRecursive(true); + } + }); +} + +// https://html.spec.whatwg.org/#update-document-for-history-step-application +void BrowsingContext::UpdateForReactivation( + const Maybe<SessionHistoryInfo>& aReactivatedEntry, + const nsTArray<SessionHistoryInfo>& aNewSHEs, + const Maybe<SessionHistoryInfo>& aPreviousEntryForActivation) { + if (RefPtr docShell = nsDocShell::Cast(GetDocShell()); + docShell && aReactivatedEntry) { + if (RefPtr window = docShell->GetActiveWindow()) { + if (RefPtr navigation = window->Navigation()) { + // The spec actually performs these to steps in reverse order, but we + // can't. We need to reactivate to be able to have the navigation + // history entries available to set the correct navigation activation. + navigation->UpdateForReactivation(aNewSHEs, aReactivatedEntry.ptr()); + + navigation->CreateNavigationActivationFrom( + aPreviousEntryForActivation.ptrOr(nullptr), + Some(NavigationType::Traverse)); + } + } + } +} + Span<RefPtr<BrowsingContext>> BrowsingContext::Children() const { if (WindowContext* current = mCurrentWindowContext) { return current->Children(); @@ -3434,73 +3544,6 @@ void BrowsingContext::DidSet(FieldIndex<IDX_UserAgentOverride>) { }); } -bool BrowsingContext::CanSet(FieldIndex<IDX_IsInBFCache>, bool, - ContentParent* aSource) { - return IsTop() && !aSource && mozilla::BFCacheInParent(); -} - -void BrowsingContext::DidSet(FieldIndex<IDX_IsInBFCache>) { - MOZ_RELEASE_ASSERT(mozilla::BFCacheInParent()); - MOZ_DIAGNOSTIC_ASSERT(IsTop()); - - const bool isInBFCache = GetIsInBFCache(); - if (!isInBFCache) { - UpdateCurrentTopByBrowserId(this); - PreOrderWalk([&](BrowsingContext* aContext) { - aContext->mIsInBFCache = false; - nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell(); - if (shell) { - nsDocShell::Cast(shell)->ThawFreezeNonRecursive(true); - } - }); - } - - if (isInBFCache && XRE_IsContentProcess() && mDocShell) { - nsDocShell::Cast(mDocShell)->MaybeDisconnectChildListenersOnPageHide(); - } - - if (isInBFCache) { - PreOrderWalk([&](BrowsingContext* aContext) { - nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell(); - if (shell) { - nsDocShell::Cast(shell)->FirePageHideShowNonRecursive(false); - } - }); - } else { - PostOrderWalk([&](BrowsingContext* aContext) { - nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell(); - if (shell) { - nsDocShell::Cast(shell)->FirePageHideShowNonRecursive(true); - } - }); - } - - if (isInBFCache) { - PreOrderWalk([&](BrowsingContext* aContext) { - nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell(); - if (shell) { - nsDocShell::Cast(shell)->ThawFreezeNonRecursive(false); - if (nsPresContext* pc = shell->GetPresContext()) { - pc->EventStateManager()->ResetHoverState(); - } - } - aContext->mIsInBFCache = true; - Document* doc = aContext->GetDocument(); - if (doc) { - // Notifying needs to happen after mIsInBFCache is set to true. - doc->NotifyActivityChanged(); - } - }); - - if (XRE_IsParentProcess()) { - if (mCurrentWindowContext && - mCurrentWindowContext->Canonical()->Fullscreen()) { - mCurrentWindowContext->Canonical()->ExitTopChromeDocumentFullscreen(); - } - } - } -} - void BrowsingContext::DidSet(FieldIndex<IDX_IsSyntheticDocumentContainer>) { if (WindowContext* parentWindowContext = GetParentWindowContext()) { parentWindowContext->UpdateChildSynthetic( diff --git a/docshell/base/BrowsingContext.h b/docshell/base/BrowsingContext.h @@ -257,8 +257,6 @@ struct EmbedderColorSchemes { /* The number of entries added to the session history because of this \ * browsing context. */ \ FIELD(HistoryEntryCount, uint32_t) \ - /* Don't use the getter of the field, but IsInBFCache() method */ \ - FIELD(IsInBFCache, bool) \ FIELD(HasRestoreData, bool) \ FIELD(SessionStoreEpoch, uint32_t) \ /* Whether we can execute scripts in this BrowsingContext. Has no effect \ @@ -1052,6 +1050,19 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { } bool IsInBFCache() const; + void DeactivateDocuments(); + + MOZ_CAN_RUN_SCRIPT + void ReactivateDocuments( + const Maybe<SessionHistoryInfo>& aReactivatedEntry, + const nsTArray<SessionHistoryInfo>& aNewSHEs, + const Maybe<SessionHistoryInfo>& aPreviousEntryForActivation); + + MOZ_CAN_RUN_SCRIPT + void UpdateForReactivation( + const Maybe<SessionHistoryInfo>& aReactivatedEntry, + const nsTArray<SessionHistoryInfo>& aNewSHEs, + const Maybe<SessionHistoryInfo>& aPreviousEntryForActivation); bool AllowJavascript() const { return GetAllowJavascript(); } bool CanExecuteScripts() const { return mCanExecuteScripts; } @@ -1097,6 +1108,8 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { static bool ShouldAddEntryForRefresh(nsIURI* aCurrentURI, nsIURI* aNewURI, bool aHasPostData); + void SetIsInBFCache(bool aIsInBFCache); + private: // Check whether it's OK to load the given url with the given subject // principal, and if so construct the right nsDocShellLoadInfo for the load @@ -1451,9 +1464,6 @@ 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>); - void DidSet(FieldIndex<IDX_IsSyntheticDocumentContainer>); void DidSet(FieldIndex<IDX_IsUnderHiddenEmbedderElement>, bool aOldValue); diff --git a/docshell/base/CanonicalBrowsingContext.cpp b/docshell/base/CanonicalBrowsingContext.cpp @@ -2802,6 +2802,7 @@ void CanonicalBrowsingContext::HistoryCommitIndexAndLength() { CallerWillNotifyHistoryIndexAndLengthChanges caller(nullptr); HistoryCommitIndexAndLength(changeID, caller); } + void CanonicalBrowsingContext::HistoryCommitIndexAndLength( const nsID& aChangeID, const CallerWillNotifyHistoryIndexAndLengthChanges& aProofOfCaller) { @@ -2828,6 +2829,75 @@ void CanonicalBrowsingContext::HistoryCommitIndexAndLength( }); } +void CanonicalBrowsingContext::DeactivateDocuments() { + MOZ_DIAGNOSTIC_ASSERT(IsTop() && mozilla::BFCacheInParent() && + GetContentParent()); + if (IsInProcess()) { + BrowsingContext::DeactivateDocuments(); + } else { + Group()->EachParent([&](ContentParent* aContentParent) { + (void)aContentParent->SendDeactivateDocuments(this); + }); + + PreOrderWalk([&](BrowsingContext* aContext) { + aContext->Canonical()->SetIsInBFCache(true); + }); + } + + if (GetCurrentWindowGlobal() && GetCurrentWindowGlobal()->Fullscreen()) { + GetCurrentWindowGlobal()->ExitTopChromeDocumentFullscreen(); + } +} + +void CanonicalBrowsingContext::ReactivateDocuments( + SessionHistoryEntry* aEntry, + SessionHistoryEntry* aPreviousEntryForActivation) { + nsTArray<SessionHistoryInfo> topNewSHIs; + + if (Navigation::IsAPIEnabled()) { + nsSHistory::WalkContiguousEntriesInOrder( + aEntry, [&topNewSHIs](auto* aContiguousEntry) { + if (nsCOMPtr<SessionHistoryEntry> she = + do_QueryInterface(aContiguousEntry)) { + topNewSHIs.AppendElement(she->Info()); + } + return true; + }); + } + + if (IsInProcess()) { + BrowsingContext::ReactivateDocuments( + Some(mActiveEntry->Info()), topNewSHIs, + ToMaybeRef(aPreviousEntryForActivation).map([](auto& entry) { + return entry.Info(); + })); + + } else { + Group()->EachParent([&](ContentParent* aContentParent) { + nsTArray<SessionHistoryInfo> newSHIs; + Maybe<SessionHistoryInfo> reactivatedEntry; + if (GetContentParent() == aContentParent && Navigation::IsAPIEnabled()) { + newSHIs.AppendElements(std::move(topNewSHIs)); + reactivatedEntry.emplace(mActiveEntry->Info()); + } + (void)aContentParent->SendReactivateDocuments( + this, reactivatedEntry, newSHIs, + ToMaybeRef(aPreviousEntryForActivation).map([](auto& entry) { + return entry.Info(); + })); + }); + + UpdateCurrentTopByBrowserId(this); + PreOrderWalk([&](BrowsingContext* aContext) { + aContext->Canonical()->SetIsInBFCache(false); + }); + } + + if (GetCurrentWindowGlobal() && GetCurrentWindowGlobal()->Fullscreen()) { + GetCurrentWindowGlobal()->ExitTopChromeDocumentFullscreen(); + } +} + void CanonicalBrowsingContext::SynchronizeLayoutHistoryState() { if (mActiveEntry) { if (IsInProcess()) { diff --git a/docshell/base/CanonicalBrowsingContext.h b/docshell/base/CanonicalBrowsingContext.h @@ -330,6 +330,12 @@ class CanonicalBrowsingContext final : public BrowsingContext { void HistoryCommitIndexAndLength(); + void DeactivateDocuments(); + + MOZ_CAN_RUN_SCRIPT + void ReactivateDocuments(SessionHistoryEntry* aEntry, + SessionHistoryEntry* aPreviousEntryForActivation); + void SynchronizeLayoutHistoryState(); void SynchronizeNavigationAPIState(nsIStructuredCloneContainer* aState); diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp @@ -9958,6 +9958,19 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState, // If we have a saved content viewer in history, restore and show it now. if (aLoadState->LoadIsFromSessionHistory() && (mLoadType & LOAD_CMD_HISTORY)) { + if (RefPtr window = GetActiveWindow()) { + if (RefPtr navigation = window->Navigation()) { + if (const LoadingSessionHistoryInfo* loadingInfo = + GetLoadingSessionHistoryInfo()) { + const SessionHistoryInfo* previousEntryForActivation = + loadingInfo->mTriggeringEntry.ptrOr(nullptr); + navigation->CreateNavigationActivationFrom( + previousEntryForActivation, + NavigationUtils::NavigationTypeFromLoadType(mLoadType)); + } + } + } + // https://html.spec.whatwg.org/#history-traversal: // To traverse the history // "If entry has a different Document object than the current entry, then @@ -12130,6 +12143,9 @@ nsresult nsDocShell::UpdateURLAndHistory( Document* aDocument, nsIURI* aNewURI, nsIStructuredCloneContainer* aData, NavigationHistoryBehavior aHistoryHandling, nsIURI* aCurrentURI, bool aEqualURIs) { + MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug, "UpdateURLAndHistory {}", + aHistoryHandling); + // Implements // https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps MOZ_ASSERT(aHistoryHandling != NavigationHistoryBehavior::Auto); @@ -14617,12 +14633,9 @@ void nsDocShell::MoveLoadingToActiveEntry(bool aExpired, uint32_t aCacheKey, return fmt::format(FMT_STRING("{}"), type); }) .valueOr("none")); - if (loadingEntry->mTriggeringEntry && - loadingEntry->mTriggeringNavigationType) { - navigation->CreateNavigationActivationFrom( - &*loadingEntry->mTriggeringEntry, - *loadingEntry->mTriggeringNavigationType); - } + navigation->CreateNavigationActivationFrom( + loadingEntry->mTriggeringEntry.ptrOr(nullptr), + loadingEntry->mTriggeringNavigationType); } } } diff --git a/docshell/base/nsDocShell.h b/docshell/base/nsDocShell.h @@ -1196,6 +1196,7 @@ class nsDocShell final : public nsDocLoader, * @param aCurrentURI the current URI we're working with. Might be null. * @param aEqualURIs whether the two URIs involved are equal. */ + MOZ_CAN_RUN_SCRIPT nsresult UpdateURLAndHistory( mozilla::dom::Document* aDocument, nsIURI* aNewURI, nsIStructuredCloneContainer* aData, diff --git a/docshell/base/nsDocShellLoadState.cpp b/docshell/base/nsDocShellLoadState.cpp @@ -787,6 +787,14 @@ void nsDocShellLoadState::SetSHEntry(nsISHEntry* aSHEntry) { } } +void nsDocShellLoadState::SetPreviousEntryForActivation(nsISHEntry* aSHEntry) { + MOZ_DIAGNOSTIC_ASSERT(mSHEntry); + nsCOMPtr<SessionHistoryEntry> she = do_QueryInterface(aSHEntry); + if (mLoadingSessionHistoryInfo) { + mLoadingSessionHistoryInfo->mTriggeringEntry = Some(she->Info()); + } +} + void nsDocShellLoadState::SetLoadingSessionHistoryInfo( const mozilla::dom::LoadingSessionHistoryInfo& aLoadingInfo) { SetLoadingSessionHistoryInfo( diff --git a/docshell/base/nsDocShellLoadState.h b/docshell/base/nsDocShellLoadState.h @@ -200,6 +200,8 @@ class nsDocShellLoadState final { void SetSHEntry(nsISHEntry* aSHEntry); + void SetPreviousEntryForActivation(nsISHEntry* aSHEntry); + const mozilla::dom::LoadingSessionHistoryInfo* GetLoadingSessionHistoryInfo() const; diff --git a/docshell/shistory/SessionHistoryEntry.h b/docshell/shistory/SessionHistoryEntry.h @@ -162,7 +162,7 @@ class SessionHistoryInfo { void SetSaveLayoutStateFlag(bool aSaveLayoutStateFlag); - bool IsTransient() { return mTransient; } + bool IsTransient() const { return mTransient; } void SetTransient() { mTransient = true; } nsID& NavigationKey() { return mNavigationKey; } diff --git a/docshell/shistory/nsSHistory.cpp b/docshell/shistory/nsSHistory.cpp @@ -1258,7 +1258,9 @@ static void FinishRestore(CanonicalBrowsingContext* aBrowsingContext, aEntry->SetFrameLoader(nullptr); - nsCOMPtr<nsISHistory> shistory = aEntry->GetShistory(); + nsCOMPtr<nsISHistory> nsihistory = aEntry->GetShistory(); + RefPtr<nsSHistory> shistory = static_cast<nsSHistory*>(nsihistory.get()); + int32_t indexOfHistoryLoad = shistory ? shistory->GetIndexOfEntry(aEntry) : -1; @@ -1303,7 +1305,7 @@ static void FinishRestore(CanonicalBrowsingContext* aBrowsingContext, if (aCanSave) { currentSHEntry->SetFrameLoader(currentFrameLoader); - (void)aBrowsingContext->SetIsInBFCache(true); + aBrowsingContext->DeactivateDocuments(); } } @@ -1349,10 +1351,10 @@ static void FinishRestore(CanonicalBrowsingContext* aBrowsingContext, currentFrameLoader->Destroy(); } - (void)loadingBC->SetIsInBFCache(false); + loadingBC->ReactivateDocuments(aEntry, currentSHEntry); // We need to call this after we've restored the page from BFCache (see - // SetIsInBFCache(false) above), so that the page is not frozen anymore and + // ReactivateDocuments above), so that the page is not frozen anymore and // the right focus events are fired. frameLoaderOwner->UpdateFocusAndMouseEnterStateAfterFrameLoaderChange(); @@ -2408,7 +2410,7 @@ nsresult nsSHistory::LoadEntry(BrowsingContext* aSourceBrowsingContext, // Possibly a reload case InitiateLoad(aSourceBrowsingContext, nextEntry, rootBC, aLoadType, aLoadResults, aLoadCurrentEntry, aUserActivation, - requestedOffset); + requestedOffset, prevEntry); return NS_OK; } @@ -2417,13 +2419,13 @@ nsresult nsSHistory::LoadEntry(BrowsingContext* aSourceBrowsingContext, prevEntry, nextEntry, rootBC, [self = RefPtr{this}, sourceBrowsingContext = RefPtr{aSourceBrowsingContext}, aLoadType, - &aLoadResults, aLoadCurrentEntry, aUserActivation, - requestedOffset](nsISHEntry* aEntry, BrowsingContext* aParent) { + &aLoadResults, aLoadCurrentEntry, aUserActivation, requestedOffset, + prevEntry](nsISHEntry* aEntry, BrowsingContext* aParent) { // Set the Subframe flag if not navigating the root docshell. aEntry->SetIsSubFrame(aParent->Id() != self->mRootBC); self->InitiateLoad(sourceBrowsingContext, aEntry, aParent, aLoadType, aLoadResults, aLoadCurrentEntry, aUserActivation, - requestedOffset); + requestedOffset, prevEntry); }); if (!differenceFound) { // LoadNextPossibleEntry will change the offset by one, and in order @@ -2625,7 +2627,8 @@ void nsSHistory::InitiateLoad(BrowsingContext* aSourceBrowsingContext, BrowsingContext* aFrameBC, long aLoadType, nsTArray<LoadEntryResult>& aLoadResults, bool aLoadCurrentEntry, bool aUserActivation, - int32_t aOffset) { + int32_t aOffset, + nsISHEntry* aPreviousEntryForActivation) { MOZ_ASSERT(aFrameBC && aFrameEntry); LoadEntryResult* loadResult = aLoadResults.AppendElement(); @@ -2653,6 +2656,8 @@ void nsSHistory::InitiateLoad(BrowsingContext* aSourceBrowsingContext, loadState->SetSHEntry(aFrameEntry); + loadState->SetPreviousEntryForActivation(aPreviousEntryForActivation); + // If we're loading the current entry we want to treat it as not a // same-document navigation (see nsDocShell::IsSameDocumentNavigation), so // record that here in the LoadingSessionHistoryEntry. @@ -2668,8 +2673,10 @@ void nsSHistory::InitiateLoad(BrowsingContext* aSourceBrowsingContext, if (mozilla::SessionHistoryInParent()) { nsCOMPtr<SessionHistoryEntry> she = do_QueryInterface(aFrameEntry); - aFrameBC->Canonical()->AddLoadingSessionHistoryEntry( - loadState->GetLoadingSessionHistoryInfo()->mLoadId, she); + const LoadingSessionHistoryInfo* loadingInfo = + loadState->GetLoadingSessionHistoryInfo(); + aFrameBC->Canonical()->AddLoadingSessionHistoryEntry(loadingInfo->mLoadId, + she); } nsCOMPtr<nsIURI> originalURI = aFrameEntry->GetOriginalURI(); diff --git a/docshell/shistory/nsSHistory.h b/docshell/shistory/nsSHistory.h @@ -250,7 +250,7 @@ class nsSHistory : public mozilla::LinkedListElement<nsSHistory>, mozilla::dom::BrowsingContext* aFrameBC, long aLoadType, nsTArray<LoadEntryResult>& aLoadResult, bool aLoadCurrentEntry, bool aUserActivation, - int32_t aOffset); + int32_t aOffset, nsISHEntry* aPreviousEntryForActivation); nsresult LoadEntry(mozilla::dom::BrowsingContext* aSourceBrowsingContext, int32_t aIndex, long aLoadType, uint32_t aHistCmd, diff --git a/dom/base/Document.h b/dom/base/Document.h @@ -3509,6 +3509,7 @@ class Document : public nsINode, return GetFuncStringContentList<nsCachableElementsByNameNodeList>( this, MatchNameAttribute, nullptr, UseExistingNameString, aName); } + MOZ_CAN_RUN_SCRIPT Document* Open(const mozilla::dom::Optional<nsAString>& /* unused */, const mozilla::dom::Optional<nsAString>& /* unused */, mozilla::ErrorResult& aError); diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp @@ -1355,8 +1355,9 @@ void nsFocusManager::WindowHidden(mozIDOMWindowProxy* aWindow, if (!XRE_IsParentProcess() && mActiveBrowsingContextInContent == docShellBeingHidden->GetBrowsingContext() && - mActiveBrowsingContextInContent->GetIsInBFCache()) { - SetActiveBrowsingContextInContent(nullptr, aActionId, aIsEnteringBFCache); + aIsEnteringBFCache) { + SetActiveBrowsingContextInContent(nullptr, aActionId, + /* aIsEnteringBFCache */ true); } // if the window being hidden is an ancestor of the focused window, adjust diff --git a/dom/base/nsFrameLoaderOwner.cpp b/dom/base/nsFrameLoaderOwner.cpp @@ -146,7 +146,7 @@ void nsFrameLoaderOwner::ChangeRemotenessCommon( MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug, ("nsFrameLoaderOwner::ChangeRemotenessCommon: store the old " "page in bfcache")); - (void)bc->SetIsInBFCache(true); + bc->Canonical()->DeactivateDocuments(); bfcacheEntry->SetFrameLoader(mFrameLoader); // Session history owns now the frameloader. mFrameLoader = nullptr; diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp @@ -75,6 +75,7 @@ #include "mozilla/dom/LSObject.h" #include "mozilla/dom/MemoryReportRequest.h" #include "mozilla/dom/Navigation.h" +#include "mozilla/dom/NavigationHistoryEntry.h" #include "mozilla/dom/PSessionStorageObserverChild.h" #include "mozilla/dom/PostMessageEvent.h" #include "mozilla/dom/PushNotifier.h" @@ -4617,6 +4618,38 @@ mozilla::ipc::IPCResult ContentChild::RecvStopLoad( return IPC_OK(); } +mozilla::ipc::IPCResult ContentChild::RecvDeactivateDocuments( + const MaybeDiscarded<BrowsingContext>& aContext) { + if (aContext.IsNullOrDiscarded()) { + return IPC_OK(); + } + BrowsingContext* browsingContext = aContext.get(); + MOZ_DIAGNOSTIC_ASSERT(browsingContext->IsTopContent()); + + browsingContext->DeactivateDocuments(); + + return IPC_OK(); +} + +// https://html.spec.whatwg.org/#update-document-for-history-step-application +// Perform steps 7 and 9 for the BFCache case. +mozilla::ipc::IPCResult ContentChild::RecvReactivateDocuments( + const MaybeDiscarded<BrowsingContext>& aContext, + const Maybe<SessionHistoryInfo>& aReactivatedEntry, + const nsTArray<SessionHistoryInfo>& aNewSHEs, + const Maybe<SessionHistoryInfo>& aPreviousEntryForActivation) { + if (aContext.IsNullOrDiscarded()) { + return IPC_OK(); + } + RefPtr browsingContext = aContext.get(); + MOZ_DIAGNOSTIC_ASSERT(browsingContext->IsTopContent()); + + browsingContext->ReactivateDocuments(aReactivatedEntry, aNewSHEs, + aPreviousEntryForActivation); + + return IPC_OK(); +} + #if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS) mozilla::ipc::IPCResult ContentChild::RecvInitSandboxTesting( Endpoint<PSandboxTestingChild>&& aEndpoint) { diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h @@ -766,6 +766,15 @@ class ContentChild final : public PContentChild, const MaybeDiscarded<BrowsingContext>& aContext, const uint32_t aStopFlags); + mozilla::ipc::IPCResult RecvDeactivateDocuments( + const MaybeDiscarded<BrowsingContext>& aContext); + MOZ_CAN_RUN_SCRIPT_BOUNDARY + mozilla::ipc::IPCResult RecvReactivateDocuments( + const MaybeDiscarded<BrowsingContext>& aContext, + const Maybe<SessionHistoryInfo>& aReactivatedEntry, + const nsTArray<SessionHistoryInfo>& aNewSHEs, + const Maybe<SessionHistoryInfo>& aPreviousEntryForActivation); + mozilla::ipc::IPCResult RecvRawMessage( const JSActorMessageMeta& aMeta, JSIPCValue&& aData, const UniquePtr<ClonedMessageData>& aStack); diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl @@ -82,6 +82,7 @@ include "mozilla/dom/DocShellMessageUtils.h"; include "mozilla/dom/FeaturePolicyUtils.h"; include "mozilla/dom/GeolocationIPCUtils.h"; include "mozilla/dom/MediaSessionIPCUtils.h"; +include "mozilla/dom/NavigationAPIIPCUtils.h"; include "mozilla/dom/PageLoadEventUtils.h"; include "mozilla/dom/ReferrerInfoUtils.h"; include "mozilla/glean/DomMetrics.h"; @@ -106,6 +107,7 @@ using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h"; using gfxPlatform::GlobalReflowFlags from "gfxPlatform.h"; using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h"; using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h"; +using mozilla::dom::NavigationType from "mozilla/dom/NavigationAPIIPCUtils.h"; using mozilla::EventMessage from "mozilla/EventForwards.h"; using mozilla::LayoutDeviceIntPoint from "Units.h"; using mozilla::ImagePoint from "Units.h"; @@ -1030,6 +1032,12 @@ child: async Reload(MaybeDiscardedBrowsingContext aContext, uint32_t aReloadFlags); async StopLoad(MaybeDiscardedBrowsingContext aContext, uint32_t aStopFlags); + async DeactivateDocuments(MaybeDiscardedBrowsingContext aContext); + async ReactivateDocuments(MaybeDiscardedBrowsingContext aContext, + SessionHistoryInfo? aReactivatedEntry, + SessionHistoryInfo[] aNewSHEs, + SessionHistoryInfo? aPreviousEntryForActivation); + async OnAllowAccessFor(MaybeDiscardedBrowsingContext aParentContext, nsCString aTrackingOrigin, uint32_t aCookieBehavior, diff --git a/dom/navigation/Navigation.cpp b/dom/navigation/Navigation.cpp @@ -400,6 +400,7 @@ void Navigation::UpdateEntriesForSameDocumentNavigation( mOngoingAPIMethodTracker->NotifyAboutCommittedToEntry(currentEntry); } + // Reset cached index for entries. for (auto& entry : disposedEntries) { entry->ResetIndexForDisposal(); } @@ -418,19 +419,93 @@ void Navigation::UpdateEntriesForSameDocumentNavigation( event->SetTrusted(true); DispatchEvent(*event); - for (const auto& entry : disposedEntries) { - RefPtr<Event> event = NS_NewDOMEvent(entry, nullptr, nullptr); - event->InitEvent(u"dispose"_ns, false, false); - event->SetTrusted(true); - event->SetTarget(entry); - entry->DispatchEvent(*event); + for (RefPtr<NavigationHistoryEntry>& entry : disposedEntries) { + MOZ_KnownLive(entry)->FireDisposeEvent(); } } } // https://html.spec.whatwg.org/#update-the-navigation-api-entries-for-reactivation -void Navigation::UpdateForReactivation(SessionHistoryInfo* aReactivatedEntry) { - // NAV-TODO +void Navigation::UpdateForReactivation( + Span<const SessionHistoryInfo> aNewSHEs, + const SessionHistoryInfo* aReactivatedEntry) { + // Step 1 + if (HasEntriesAndEventsDisabled()) { + return; + } + + LOG_FMTD( + "Reactivate {} {}", fmt::ptr(aReactivatedEntry), + fmt::join( + [currentEntry = RefPtr{GetCurrentEntry()}](auto& aEntries) { + nsTArray<nsCString> entries; + (void)TransformIfAbortOnErr( + aEntries, MakeBackInserter(entries), [](auto) { return true; }, + [currentEntry](auto& entry) -> Result<nsCString, nsresult> { + return nsPrintfCString( + "%s%s", entry.NavigationKey().ToString().get(), + currentEntry && + currentEntry->Key() == entry.NavigationKey() + ? "*" + : ""); + }); + return std::move(entries); + }(aNewSHEs), + ", ")); + + // Step 2 + nsTArray<RefPtr<NavigationHistoryEntry>> newNHEs; + + // Step 3 + nsTArray<RefPtr<NavigationHistoryEntry>> oldNHEs = mEntries.Clone(); + + // Step 4 + for (const auto& newSHE : aNewSHEs) { + // Step 4.1 + RefPtr<NavigationHistoryEntry> newNHE; + if (ArrayIterator matchingOldNHE = std::find_if( + oldNHEs.begin(), oldNHEs.end(), + [newSHE](const auto& aNHE) { return aNHE->IsSameEntry(&newSHE); }); + matchingOldNHE != oldNHEs.end()) { + // Step 4.2.1 + newNHE = *matchingOldNHE; + // Since we cache indices we need to update newNHE here. Also, narrowing. + // Yay. + CheckedInt<int64_t> newIndex(newNHEs.Length()); + newNHE->SetIndex(newIndex.value()); + + // Step 4.2.2 + oldNHEs.RemoveElementAt(matchingOldNHE); + } else { + // Step 4.3.1 and 4.3.2 + newNHE = MakeRefPtr<NavigationHistoryEntry>(GetOwnerGlobal(), &newSHE, + newNHEs.Length()); + } + // Step 4.4 + newNHEs.AppendElement(newNHE); + } + + // Step 5 + mEntries = std::move(newNHEs); + + // Step 6 + mCurrentEntryIndex = GetNavigationEntryIndex(*aReactivatedEntry); + + // Reset cached index for entries. + for (const auto& oldEntry : oldNHEs) { + oldEntry->ResetIndexForDisposal(); + } + + // Step 7 + NS_DispatchToMainThread(NS_NewRunnableFunction( + "UpdateForReactivation", + [oldEntries = std::move(oldNHEs)]() MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA { + // Step 7.1 + for (const RefPtr<NavigationHistoryEntry>& disposedNHE : oldEntries) { + // Step 7.1.1 + MOZ_KnownLive(disposedNHE)->FireDisposeEvent(); + } + })); } // https://html.spec.whatwg.org/#navigation-api-early-error-result @@ -1273,7 +1348,7 @@ struct NavigationWaitForAllScope final : public nsISupports, return; } // 2. Let navigable be event's relevant global object's navigable. - nsDocShell* docShell = nsDocShell::Cast(document->GetDocShell()); + RefPtr<nsDocShell> docShell = nsDocShell::Cast(document->GetDocShell()); Maybe<BrowsingContext&> navigable = ToMaybeRef(mNavigation->GetOwnerWindow()).andThen([](auto& aWindow) { return ToMaybeRef(aWindow.GetBrowsingContext()); @@ -1318,11 +1393,13 @@ struct NavigationWaitForAllScope final : public nsISupports, // URL, with serializedData set to event's classic history API // state and historyHandling set to event's navigationType. if (docShell) { + nsCOMPtr newURL = mDestination->GetURL(); + nsCOMPtr currentURL = document->GetDocumentURI(); + nsCOMPtr serializedData = mEvent->ClassicHistoryAPIState(); docShell->UpdateURLAndHistory( - document, mDestination->GetURL(), - mEvent->ClassicHistoryAPIState(), + document, newURL, serializedData, *NavigationUtils::NavigationHistoryBehavior(aNavigationType), - document->GetDocumentURI(), + currentURL, Equals(mDestination->GetURL(), document->GetDocumentURI())); } break; @@ -1331,8 +1408,9 @@ struct NavigationWaitForAllScope final : public nsISupports, // given navigation, navigable's active session history entry, and // "reload". if (docShell) { - mNavigation->UpdateEntriesForSameDocumentNavigation( - docShell->GetActiveSessionHistoryInfo(), aNavigationType); + MOZ_KnownLive(mNavigation) + ->UpdateEntriesForSameDocumentNavigation( + docShell->GetActiveSessionHistoryInfo(), aNavigationType); } break; case NavigationType::Traverse: @@ -1793,6 +1871,21 @@ NavigationHistoryEntry* Navigation::FindNavigationHistoryEntry( return nullptr; } +// https://html.spec.whatwg.org/#getting-the-navigation-api-entry-index +Maybe<size_t> Navigation::GetNavigationEntryIndex( + const SessionHistoryInfo& aSessionHistoryInfo) const { + size_t index = 0; + for (const auto& navigationHistoryEntry : mEntries) { + if (navigationHistoryEntry->IsSameEntry(&aSessionHistoryInfo)) { + return Some(index); + } + + index++; + } + + return Nothing(); +} + // https://html.spec.whatwg.org/#navigation-api-method-tracker-clean-up /* static */ void Navigation::CleanUp( NavigationAPIMethodTracker* aNavigationAPIMethodTracker) { @@ -2068,19 +2161,31 @@ Navigation::AddUpcomingTraverseAPIMethodTracker(const nsID& aKey, // https://html.spec.whatwg.org/#update-document-for-history-step-application void Navigation::CreateNavigationActivationFrom( - SessionHistoryInfo* aPreviousEntryForActivation, - NavigationType aNavigationType) { - // Note: we do Step 7.1 at the end of method so we can both create and - // initialize the activation at once. - MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug, - "Creating NavigationActivation for from={}, type={}", - fmt::ptr(aPreviousEntryForActivation), aNavigationType); - RefPtr currentEntry = GetCurrentEntry(); - if (!currentEntry) { + const SessionHistoryInfo* aPreviousEntryForActivation, + Maybe<NavigationType> aNavigationType) { + // Step 7 If all the following are true: + // * previousEntryForActivation is given; + // * navigationType is non-null; and + // * navigationType is "reload" or previousEntryForActivation's document is + // not document, + RefPtr<NavigationHistoryEntry> currentEntry = GetCurrentEntry(); + if (!aPreviousEntryForActivation || !aNavigationType || !currentEntry || + (*aNavigationType != NavigationType::Reload && + currentEntry->SessionHistoryInfo()->SharesDocumentWith( + *aPreviousEntryForActivation))) { return; } - // Step 7.2. Let previousEntryIndex be the result of getting the navigation + NavigationType navigationType = *aNavigationType; + + MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug, + "Creating NavigationActivation for from={}, type={}", + fmt::ptr(aPreviousEntryForActivation), navigationType); + + // Note: we do Step 7.1 at the end of method so we can both create and + // initialize the activation at once. + + // Step 7.2 Let previousEntryIndex be the result of getting the navigation // API entry index of previousEntryForActivation within navigation. auto possiblePreviousEntry = std::find_if(mEntries.begin(), mEntries.end(), @@ -2088,7 +2193,7 @@ void Navigation::CreateNavigationActivationFrom( return entry->IsSameEntry(aPreviousEntryForActivation); }); - // 3. If previousEntryIndex is non-negative, then set activation's old entry + // 7.3 If previousEntryIndex is non-negative, then set activation's old entry // to navigation's entry list[previousEntryIndex]. RefPtr<NavigationHistoryEntry> oldEntry; if (possiblePreviousEntry != mEntries.end()) { @@ -2096,9 +2201,9 @@ void Navigation::CreateNavigationActivationFrom( "Found previous entry at {}", fmt::ptr(possiblePreviousEntry->get())); oldEntry = *possiblePreviousEntry; - } else if (aNavigationType == NavigationType::Replace && + } else if (navigationType == NavigationType::Replace && !aPreviousEntryForActivation->IsTransient()) { - // 4. Otherwise, if all the following are true: + // 7.4 Otherwise, if all the following are true: // navigationType is "replace"; // previousEntryForActivation's document state's origin is same origin // with document's origin; and previousEntryForActivation's document's @@ -2118,6 +2223,11 @@ void Navigation::CreateNavigationActivationFrom( MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug, "Created a new entry at {}", fmt::ptr(oldEntry.get())); } + } else { + LOG_FMTV("Didn't find previous entry id={}", + aPreviousEntryForActivation + ? aPreviousEntryForActivation->NavigationId().ToString().get() + : ""); } // 1. If navigation's activation is null, then set navigation's @@ -2125,8 +2235,16 @@ void Navigation::CreateNavigationActivationFrom( // realm. // 5. Set activation's new entry to navigation's current entry. // 6. Set activation's navigation type to navigationType. - mActivation = MakeRefPtr<NavigationActivation>(GetOwnerGlobal(), currentEntry, - oldEntry, aNavigationType); + if (!mActivation) { + mActivation = MakeRefPtr<NavigationActivation>( + GetOwnerGlobal(), currentEntry, oldEntry, navigationType); + } else { + mActivation->SetNewEntry(currentEntry); + mActivation->SetNavigationType(navigationType); + if (oldEntry) { + mActivation->SetOldEntry(oldEntry); + } + } } // https://html.spec.whatwg.org/#dom-navigationprecommitcontroller-redirect diff --git a/dom/navigation/Navigation.h b/dom/navigation/Navigation.h @@ -136,9 +136,10 @@ class Navigation final : public DOMEventTargetHelper { // https://html.spec.whatwg.org/multipage/nav-history-apis.html#update-the-navigation-api-entries-for-reactivation MOZ_CAN_RUN_SCRIPT - void UpdateForReactivation(SessionHistoryInfo* aReactivatedEntry); + void UpdateForReactivation(Span<const SessionHistoryInfo> aNewSHEs, + const SessionHistoryInfo* aReactivatedEntry); - // https://html.spec.whatwg.org/multipage/nav-history-apis.html#update-the-navigation-api-entries-for-a-same-document-navigation + MOZ_CAN_RUN_SCRIPT void UpdateEntriesForSameDocumentNavigation( SessionHistoryInfo* aDestinationSHE, NavigationType aNavigationType); @@ -192,8 +193,8 @@ class Navigation final : public DOMEventTargetHelper { void InformAboutChildNavigableDestruction(JSContext* aCx); void CreateNavigationActivationFrom( - SessionHistoryInfo* aPreviousEntryForActivation, - NavigationType aNavigationType); + const SessionHistoryInfo* aPreviousEntryForActivation, + Maybe<NavigationType> aNavigationType); void SetSerializedStateIntoOngoingAPIMethodTracker( nsIStructuredCloneContainer* aSerializedState); @@ -229,6 +230,9 @@ class Navigation final : public DOMEventTargetHelper { NavigationHistoryEntry* FindNavigationHistoryEntry( const SessionHistoryInfo& aSessionHistoryInfo) const; + Maybe<size_t> GetNavigationEntryIndex( + const SessionHistoryInfo& aSessionHistoryInfo) const; + RefPtr<NavigationAPIMethodTracker> SetUpNavigateReloadAPIMethodTracker( JS::Handle<JS::Value> aInfo, nsIStructuredCloneContainer* aSerializedState); diff --git a/dom/navigation/NavigationActivation.cpp b/dom/navigation/NavigationActivation.cpp @@ -44,4 +44,16 @@ already_AddRefed<NavigationHistoryEntry> NavigationActivation::Entry() const { return do_AddRef(mNewEntry); } +void NavigationActivation::SetNewEntry(NavigationHistoryEntry* aEntry) { + mNewEntry = aEntry; +} + +void NavigationActivation::SetOldEntry(NavigationHistoryEntry* aEntry) { + mOldEntry = aEntry; +} + +void NavigationActivation::SetNavigationType(enum NavigationType aType) { + mType = aType; +} + } // namespace mozilla::dom diff --git a/dom/navigation/NavigationActivation.h b/dom/navigation/NavigationActivation.h @@ -29,9 +29,14 @@ class NavigationActivation final : public nsISupports, public nsWrapperCache { already_AddRefed<NavigationHistoryEntry> GetFrom() const; already_AddRefed<NavigationHistoryEntry> Entry() const; + // https://html.spec.whatwg.org/#dom-navigationactivation-navigationtype enum NavigationType NavigationType() const { return mType; } + void SetNewEntry(NavigationHistoryEntry* aEntry); + void SetOldEntry(NavigationHistoryEntry* aEntry); + void SetNavigationType(enum NavigationType aType); + JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; nsIGlobalObject* GetParentObject() const { return mGlobal; } diff --git a/dom/navigation/NavigationHistoryEntry.cpp b/dom/navigation/NavigationHistoryEntry.cpp @@ -8,10 +8,14 @@ #include "mozilla/dom/Document.h" #include "mozilla/dom/NavigationHistoryEntryBinding.h" +#include "mozilla/dom/NavigationUtils.h" #include "mozilla/dom/SessionHistoryEntry.h" #include "nsDocShell.h" #include "nsGlobalWindowInner.h" +#define LOG_FMTD(format, ...) \ + MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug, format, ##__VA_ARGS__); + extern mozilla::LazyLogModule gNavigationAPILog; namespace mozilla::dom { @@ -177,6 +181,21 @@ nsIStructuredCloneContainer* NavigationHistoryEntry::GetNavigationAPIState() return mSHInfo->GetNavigationAPIState(); } +// Since we don't compute the index of the entry every time we use it, but +// instead cache it, we need to make sure to invalidate the cache when the +// entry is disposed. void NavigationHistoryEntry::ResetIndexForDisposal() { mIndex = -1; } +MOZ_CAN_RUN_SCRIPT +void NavigationHistoryEntry::FireDisposeEvent() { + RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr); + // it doesn't bubble, and it isn't cancelable + event->InitEvent(u"dispose"_ns, false, false); + event->SetTrusted(true); + LOG_FMTD("Fire dispose"); + DispatchEvent(*event, IgnoreErrors()); +} + } // namespace mozilla::dom + +#undef LOG_FMTD diff --git a/dom/navigation/NavigationHistoryEntry.h b/dom/navigation/NavigationHistoryEntry.h @@ -30,6 +30,7 @@ class NavigationHistoryEntry final : public DOMEventTargetHelper { void GetKey(nsAString& aResult) const; void GetId(nsAString& aResult) const; int64_t Index() const; + void SetIndex(int64_t aIndex) { mIndex = aIndex; } bool SameDocument() const; void GetState(JSContext* aCx, JS::MutableHandle<JS::Value> aResult, @@ -53,6 +54,9 @@ class NavigationHistoryEntry final : public DOMEventTargetHelper { void ResetIndexForDisposal(); + MOZ_CAN_RUN_SCRIPT + void FireDisposeEvent(); + private: ~NavigationHistoryEntry(); diff --git a/testing/web-platform/meta/navigation-api/navigation-activation/activation-after-bfcache-cross-origin.html.ini b/testing/web-platform/meta/navigation-api/navigation-activation/activation-after-bfcache-cross-origin.html.ini @@ -1,4 +0,0 @@ -[activation-after-bfcache-cross-origin.html] - expected: [OK, TIMEOUT] - [navigation.activation must be updated when restored from bfcache] - expected: [FAIL, TIMEOUT] diff --git a/testing/web-platform/meta/navigation-api/navigation-activation/activation-after-bfcache.html.ini b/testing/web-platform/meta/navigation-api/navigation-activation/activation-after-bfcache.html.ini @@ -1,4 +0,0 @@ -[activation-after-bfcache.html] - - [navigation.activation must be updated when restored from bfcache] - expected: [TIMEOUT,FAIL] diff --git a/testing/web-platform/meta/navigation-api/navigation-history-entry/entries-after-bfcache.html.ini b/testing/web-platform/meta/navigation-api/navigation-history-entry/entries-after-bfcache.html.ini @@ -1,4 +1,4 @@ [entries-after-bfcache.html] expected: [TIMEOUT,OK] [entries() must contain the forward-history page after navigating back to a bfcached page] - expected: [TIMEOUT,FAIL] + expected: [TIMEOUT,PASS] diff --git a/testing/web-platform/meta/navigation-api/per-entry-events/dispose-after-bfcache.html.ini b/testing/web-platform/meta/navigation-api/per-entry-events/dispose-after-bfcache.html.ini @@ -1,4 +1,4 @@ [dispose-after-bfcache.html] expected: [OK, TIMEOUT] [entries() must contain the forward-history page after navigating back to a bfcached page] - expected: [FAIL, TIMEOUT] + expected: [PASS, TIMEOUT]