Bug 1890747 - Track Navigation history entries. r=dom-core,farre

Differential Revision: https://phabricator.services.mozilla.com/D219007
This commit is contained in:
Adam Vandolder
2025-02-13 03:11:20 +00:00
parent 739b849487
commit 23e9c9915a
30 changed files with 697 additions and 72 deletions

View File

@@ -35,6 +35,7 @@
#include "mozilla/dom/Location.h" #include "mozilla/dom/Location.h"
#include "mozilla/dom/LocationBinding.h" #include "mozilla/dom/LocationBinding.h"
#include "mozilla/dom/MediaDevices.h" #include "mozilla/dom/MediaDevices.h"
#include "mozilla/dom/Navigation.h"
#include "mozilla/dom/PopupBlocker.h" #include "mozilla/dom/PopupBlocker.h"
#include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/SessionStoreChild.h" #include "mozilla/dom/SessionStoreChild.h"
@@ -88,6 +89,7 @@
#include "GVAutoplayRequestStatusIPC.h" #include "GVAutoplayRequestStatusIPC.h"
extern mozilla::LazyLogModule gAutoplayPermissionLog; extern mozilla::LazyLogModule gAutoplayPermissionLog;
extern mozilla::LazyLogModule gNavigationLog;
extern mozilla::LazyLogModule gTimeoutDeferralLog; extern mozilla::LazyLogModule gTimeoutDeferralLog;
#define AUTOPLAY_LOG(msg, ...) \ #define AUTOPLAY_LOG(msg, ...) \
@@ -3924,6 +3926,25 @@ void BrowsingContext::ClearCachedValuesOfLocations() {
} }
} }
void BrowsingContext::GetContiguousHistoryEntries(
SessionHistoryInfo& aActiveEntry, Navigation* aNavigation) {
if (!aNavigation) {
return;
}
if (XRE_IsContentProcess()) {
MOZ_ASSERT(ContentChild::GetSingleton());
ContentChild::GetSingleton()->SendGetContiguousSessionHistoryInfos(
this, aActiveEntry,
[aActiveEntry, navigation = RefPtr(aNavigation)](auto aInfos) mutable {
navigation->InitializeHistoryEntries(aInfos, &aActiveEntry);
},
[](auto aReason) { MOZ_ASSERT(false, "How did this happen?"); });
} else {
auto infos = Canonical()->GetContiguousSessionHistoryInfos(aActiveEntry);
aNavigation->InitializeHistoryEntries(infos, &aActiveEntry);
}
}
} // namespace dom } // namespace dom
namespace ipc { namespace ipc {

View File

@@ -975,6 +975,9 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache {
void LocationCreated(dom::Location* aLocation); void LocationCreated(dom::Location* aLocation);
void ClearCachedValuesOfLocations(); void ClearCachedValuesOfLocations();
void GetContiguousHistoryEntries(SessionHistoryInfo& aActiveEntry,
Navigation* aNavigation);
protected: protected:
virtual ~BrowsingContext(); virtual ~BrowsingContext();
BrowsingContext(WindowContext* aParentWindow, BrowsingContextGroup* aGroup, BrowsingContext(WindowContext* aParentWindow, BrowsingContextGroup* aGroup,

View File

@@ -18,6 +18,7 @@
#include "mozilla/dom/BrowsingContextGroup.h" #include "mozilla/dom/BrowsingContextGroup.h"
#include "mozilla/dom/ContentParent.h" #include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/EventTarget.h" #include "mozilla/dom/EventTarget.h"
#include "mozilla/dom/Navigation.h"
#include "mozilla/dom/PBrowserParent.h" #include "mozilla/dom/PBrowserParent.h"
#include "mozilla/dom/PBackgroundSessionStorageCache.h" #include "mozilla/dom/PBackgroundSessionStorageCache.h"
#include "mozilla/dom/PWindowGlobalParent.h" #include "mozilla/dom/PWindowGlobalParent.h"
@@ -671,6 +672,26 @@ CanonicalBrowsingContext::ReplaceLoadingSessionHistoryEntryForLoad(
return nullptr; return nullptr;
} }
mozilla::Span<const SessionHistoryInfo>
CanonicalBrowsingContext::GetContiguousSessionHistoryInfos(
SessionHistoryInfo& aInfo) {
MOZ_ASSERT(Navigation::IsAPIEnabled());
nsISHistory* history = GetSessionHistory();
if (!history) {
return {};
}
mActiveContiguousEntries.ClearAndRetainStorage();
nsSHistory::WalkContiguousEntriesInOrder(mActiveEntry, [&](auto* aEntry) {
if (nsCOMPtr<SessionHistoryEntry> entry = do_QueryObject(aEntry)) {
mActiveContiguousEntries.AppendElement(entry->Info());
}
});
return mActiveContiguousEntries;
}
using PrintPromise = CanonicalBrowsingContext::PrintPromise; using PrintPromise = CanonicalBrowsingContext::PrintPromise;
#ifdef NS_PRINTING #ifdef NS_PRINTING
// Clients must call StaticCloneForPrintingCreated or // Clients must call StaticCloneForPrintingCreated or

View File

@@ -325,6 +325,9 @@ class CanonicalBrowsingContext final : public BrowsingContext {
void GetLoadingSessionHistoryInfoFromParent( void GetLoadingSessionHistoryInfoFromParent(
Maybe<LoadingSessionHistoryInfo>& aLoadingInfo); Maybe<LoadingSessionHistoryInfo>& aLoadingInfo);
mozilla::Span<const SessionHistoryInfo> GetContiguousSessionHistoryInfos(
SessionHistoryInfo& aInfo);
void HistoryCommitIndexAndLength(); void HistoryCommitIndexAndLength();
void SynchronizeLayoutHistoryState(); void SynchronizeLayoutHistoryState();
@@ -637,6 +640,8 @@ class CanonicalBrowsingContext final : public BrowsingContext {
bool mFullyDiscarded = false; bool mFullyDiscarded = false;
nsTArray<std::function<void(uint64_t)>> mFullyDiscardedListeners; nsTArray<std::function<void(uint64_t)>> mFullyDiscardedListeners;
nsTArray<SessionHistoryInfo> mActiveContiguousEntries;
}; };
} // namespace dom } // namespace dom

View File

@@ -68,6 +68,9 @@
#include "mozilla/dom/FragmentDirective.h" #include "mozilla/dom/FragmentDirective.h"
#include "mozilla/dom/HTMLAnchorElement.h" #include "mozilla/dom/HTMLAnchorElement.h"
#include "mozilla/dom/HTMLIFrameElement.h" #include "mozilla/dom/HTMLIFrameElement.h"
#include "mozilla/dom/Navigation.h"
#include "mozilla/dom/NavigationBinding.h"
#include "mozilla/dom/NavigationHistoryEntry.h"
#include "mozilla/dom/PerformanceNavigation.h" #include "mozilla/dom/PerformanceNavigation.h"
#include "mozilla/dom/PermissionMessageUtils.h" #include "mozilla/dom/PermissionMessageUtils.h"
#include "mozilla/dom/PopupBlocker.h" #include "mozilla/dom/PopupBlocker.h"
@@ -280,6 +283,7 @@ static mozilla::LazyLogModule gDocShellAndDOMWindowLeakLogging(
#endif #endif
static mozilla::LazyLogModule gDocShellLeakLog("nsDocShellLeak"); static mozilla::LazyLogModule gDocShellLeakLog("nsDocShellLeak");
extern mozilla::LazyLogModule gPageCacheLog; extern mozilla::LazyLogModule gPageCacheLog;
extern mozilla::LazyLogModule gNavigationLog;
mozilla::LazyLogModule gSHLog("SessionHistory"); mozilla::LazyLogModule gSHLog("SessionHistory");
extern mozilla::LazyLogModule gSHIPBFCacheLog; extern mozilla::LazyLogModule gSHIPBFCacheLog;
@@ -9059,6 +9063,21 @@ nsresult nsDocShell::HandleSameDocumentNavigation(
// destroy the docshell, nulling out mScriptGlobal. Hold a stack // destroy the docshell, nulling out mScriptGlobal. Hold a stack
// reference to avoid null derefs. See bug 914521. // reference to avoid null derefs. See bug 914521.
if (win) { if (win) {
if (RefPtr navigation = win->Navigation()) {
MOZ_LOG(gNavigationLog, LogLevel::Debug,
("nsDocShell %p triggering a navigation event from "
"HandleSameDocumentNavigation",
this));
// Corresponds to step 6.4.2 from the Updating the document algorithm:
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#updating-the-document
navigation->UpdateEntriesForSameDocumentNavigation(
mActiveEntry.get(),
LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)
? NavigationType::Replace
: aLoadState->LoadIsFromSessionHistory() ? NavigationType::Traverse
: NavigationType::Push);
}
// Fire a hashchange event URIs differ, and only in their hashes. // Fire a hashchange event URIs differ, and only in their hashes.
// If the fragment contains a directive, compare hasRef. // If the fragment contains a directive, compare hasRef.
bool doHashchange = aState.mSameExceptHashes && bool doHashchange = aState.mSameExceptHashes &&
@@ -11323,19 +11342,23 @@ nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
} // end of same-origin check } // end of same-origin check
// Step 8: call "URL and history update steps" // Step 8: call "URL and history update steps"
rv = UpdateURLAndHistory(document, newURI, scContainer, aTitle, aReplace, rv = UpdateURLAndHistory(document, newURI, scContainer,
aReplace ? NavigationHistoryBehavior::Replace
: NavigationHistoryBehavior::Push,
currentURI, equalURIs); currentURI, equalURIs);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
return NS_OK; return NS_OK;
} }
nsresult nsDocShell::UpdateURLAndHistory(Document* aDocument, nsIURI* aNewURI, nsresult nsDocShell::UpdateURLAndHistory(
nsIStructuredCloneContainer* aData, Document* aDocument, nsIURI* aNewURI, nsIStructuredCloneContainer* aData,
const nsAString& aTitle, bool aReplace, NavigationHistoryBehavior aHistoryHandling, nsIURI* aCurrentURI,
nsIURI* aCurrentURI, bool aEqualURIs) { bool aEqualURIs) {
// Implements // Implements
// https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps // https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps
MOZ_ASSERT(aHistoryHandling != NavigationHistoryBehavior::Auto);
bool isReplace = aHistoryHandling == NavigationHistoryBehavior::Replace;
// If we have a pending title change, handle it before creating a new entry. // If we have a pending title change, handle it before creating a new entry.
aDocument->DoNotifyPossibleTitleChange(); aDocument->DoNotifyPossibleTitleChange();
@@ -11344,7 +11367,7 @@ nsresult nsDocShell::UpdateURLAndHistory(Document* aDocument, nsIURI* aNewURI,
// history. This will erase all SHEntries after the new entry and make this // history. This will erase all SHEntries after the new entry and make this
// entry the current one. This operation may modify mOSHE, which we need // entry the current one. This operation may modify mOSHE, which we need
// later, so we keep a reference here. // later, so we keep a reference here.
NS_ENSURE_TRUE(mOSHE || mActiveEntry || aReplace, NS_ERROR_FAILURE); NS_ENSURE_TRUE(mOSHE || mActiveEntry || isReplace, NS_ERROR_FAILURE);
nsCOMPtr<nsISHEntry> oldOSHE = mOSHE; nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
// If this push/replaceState changed the document's current URI and the new // If this push/replaceState changed the document's current URI and the new
@@ -11367,7 +11390,7 @@ nsresult nsDocShell::UpdateURLAndHistory(Document* aDocument, nsIURI* aNewURI,
mLoadType = LOAD_PUSHSTATE; mLoadType = LOAD_PUSHSTATE;
nsCOMPtr<nsISHEntry> newSHEntry; nsCOMPtr<nsISHEntry> newSHEntry;
if (!aReplace) { if (!isReplace) {
// Step 2. // Step 2.
// Step 2.2, "Remove any tasks queued by the history traversal task // Step 2.2, "Remove any tasks queued by the history traversal task
@@ -11520,7 +11543,7 @@ nsresult nsDocShell::UpdateURLAndHistory(Document* aDocument, nsIURI* aNewURI,
RefPtr<ChildSHistory> rootSH = GetRootSessionHistory(); RefPtr<ChildSHistory> rootSH = GetRootSessionHistory();
if (rootSH) { if (rootSH) {
rootSH->LegacySHistory()->EvictDocumentViewersOrReplaceEntry(newSHEntry, rootSH->LegacySHistory()->EvictDocumentViewersOrReplaceEntry(newSHEntry,
aReplace); isReplace);
} }
} }
@@ -11560,6 +11583,19 @@ nsresult nsDocShell::UpdateURLAndHistory(Document* aDocument, nsIURI* aNewURI,
} }
aDocument->SetStateObject(aData); aDocument->SetStateObject(aData);
if (RefPtr navigation = aDocument->GetInnerWindow()->Navigation()) {
MOZ_LOG(gNavigationLog, LogLevel::Debug,
("nsDocShell %p triggering a navigation event for a same-document "
"navigation from UpdateURLAndHistory -> isReplace: %s",
this, isReplace ? "true" : "false"));
// Step 11: Update the navigation API entries for a same-document
// navigation given document's relevant global object's navigation API,
// newEntry, and historyHandling.
navigation->UpdateEntriesForSameDocumentNavigation(
mActiveEntry.get(),
isReplace ? NavigationType::Replace : NavigationType::Push);
}
return NS_OK; return NS_OK;
} }
@@ -13659,6 +13695,16 @@ void nsDocShell::MoveLoadingToActiveEntry(bool aPersist, bool aExpired,
*loadingEntry, loadType, aPreviousURI, previousActiveEntry.get(), *loadingEntry, loadType, aPreviousURI, previousActiveEntry.get(),
aPersist, false, aExpired, aCacheKey); aPersist, false, aExpired, aCacheKey);
} }
// Only update navigation if the new entry will be persisted (i.e., is not
// an about: page).
if (aPersist && GetWindow() && GetWindow()->GetCurrentInnerWindow()) {
if (RefPtr navigation =
GetWindow()->GetCurrentInnerWindow()->Navigation()) {
mBrowsingContext->GetContiguousHistoryEntries(*mActiveEntry,
navigation);
}
}
} }
} }
@@ -13802,3 +13848,8 @@ void nsDocShell::MaybeDisconnectChildListenersOnPageHide() {
mChannelToDisconnectOnPageHide = 0; mChannelToDisconnectOnPageHide = 0;
} }
} }
bool nsDocShell::IsSameDocumentAsActiveEntry(
const mozilla::dom::SessionHistoryInfo& aSHInfo) {
return mActiveEntry ? mActiveEntry->SharesDocumentWith(aSHInfo) : false;
}

View File

@@ -48,6 +48,7 @@ namespace dom {
class ClientInfo; class ClientInfo;
class ClientSource; class ClientSource;
class EventTarget; class EventTarget;
enum class NavigationHistoryBehavior : uint8_t;
class SessionHistoryInfo; class SessionHistoryInfo;
struct LoadingSessionHistoryInfo; struct LoadingSessionHistoryInfo;
struct Wireframe; struct Wireframe;
@@ -1111,8 +1112,7 @@ class nsDocShell final : public nsDocLoader,
* URI. * URI.
* @param aNewURI the new URI. * @param aNewURI the new URI.
* @param aData The serialized state data. May be null. * @param aData The serialized state data. May be null.
* @param aTitle The new title. May be empty. * @param aHistoryHandling how to handle updating the history entries.
* @param aReplace whether this should replace the exising SHEntry.
* *
* Arguments we need internally because deriving them from the * Arguments we need internally because deriving them from the
* others is a bit complicated: * others is a bit complicated:
@@ -1120,11 +1120,14 @@ class nsDocShell final : public nsDocLoader,
* @param aCurrentURI the current URI we're working with. Might be null. * @param aCurrentURI the current URI we're working with. Might be null.
* @param aEqualURIs whether the two URIs involved are equal. * @param aEqualURIs whether the two URIs involved are equal.
*/ */
nsresult UpdateURLAndHistory(mozilla::dom::Document* aDocument, nsresult UpdateURLAndHistory(
nsIURI* aNewURI, mozilla::dom::Document* aDocument, nsIURI* aNewURI,
nsIStructuredCloneContainer* aData, nsIStructuredCloneContainer* aData,
const nsAString& aTitle, bool aReplace, mozilla::dom::NavigationHistoryBehavior aHistoryHandling,
nsIURI* aCurrentURI, bool aEqualURIs); nsIURI* aCurrentURI, bool aEqualURIs);
bool IsSameDocumentAsActiveEntry(
const mozilla::dom::SessionHistoryInfo& aSHInfo);
private: private:
void SetCurrentURIInternal(nsIURI* aURI); void SetCurrentURIInternal(nsIURI* aURI);

View File

@@ -242,6 +242,10 @@ bool SessionHistoryInfo::IsSubFrame() const {
return mSharedState.Get()->mIsFrameNavigation; return mSharedState.Get()->mIsFrameNavigation;
} }
nsStructuredCloneContainer* SessionHistoryInfo::GetNavigationState() const {
return mSharedState.Get()->mNavigationState.get();
}
void SessionHistoryInfo::SetSaveLayoutStateFlag(bool aSaveLayoutStateFlag) { void SessionHistoryInfo::SetSaveLayoutStateFlag(bool aSaveLayoutStateFlag) {
MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(XRE_IsParentProcess());
static_cast<SHEntrySharedParentState*>(mSharedState.Get())->mSaveLayoutState = static_cast<SHEntrySharedParentState*>(mSharedState.Get())->mSaveLayoutState =
@@ -1557,6 +1561,8 @@ void IPDLParamTraits<dom::SessionHistoryInfo>::Write(
WriteIPDLParam(aWriter, aActor, stateData); WriteIPDLParam(aWriter, aActor, stateData);
WriteIPDLParam(aWriter, aActor, aParam.mSrcdocData); WriteIPDLParam(aWriter, aActor, aParam.mSrcdocData);
WriteIPDLParam(aWriter, aActor, aParam.mBaseURI); WriteIPDLParam(aWriter, aActor, aParam.mBaseURI);
WriteIPDLParam(aWriter, aActor, aParam.mNavigationKey);
WriteIPDLParam(aWriter, aActor, aParam.mNavigationId);
WriteIPDLParam(aWriter, aActor, aParam.mLoadReplace); WriteIPDLParam(aWriter, aActor, aParam.mLoadReplace);
WriteIPDLParam(aWriter, aActor, aParam.mURIWasModified); WriteIPDLParam(aWriter, aActor, aParam.mURIWasModified);
WriteIPDLParam(aWriter, aActor, aParam.mScrollRestorationIsManual); WriteIPDLParam(aWriter, aActor, aParam.mScrollRestorationIsManual);
@@ -1599,6 +1605,8 @@ bool IPDLParamTraits<dom::SessionHistoryInfo>::Read(
!ReadIPDLParam(aReader, aActor, &stateData) || !ReadIPDLParam(aReader, aActor, &stateData) ||
!ReadIPDLParam(aReader, aActor, &aResult->mSrcdocData) || !ReadIPDLParam(aReader, aActor, &aResult->mSrcdocData) ||
!ReadIPDLParam(aReader, aActor, &aResult->mBaseURI) || !ReadIPDLParam(aReader, aActor, &aResult->mBaseURI) ||
!ReadIPDLParam(aReader, aActor, &aResult->mNavigationKey) ||
!ReadIPDLParam(aReader, aActor, &aResult->mNavigationId) ||
!ReadIPDLParam(aReader, aActor, &aResult->mLoadReplace) || !ReadIPDLParam(aReader, aActor, &aResult->mLoadReplace) ||
!ReadIPDLParam(aReader, aActor, &aResult->mURIWasModified) || !ReadIPDLParam(aReader, aActor, &aResult->mURIWasModified) ||
!ReadIPDLParam(aReader, aActor, &aResult->mScrollRestorationIsManual) || !ReadIPDLParam(aReader, aActor, &aResult->mScrollRestorationIsManual) ||

View File

@@ -161,6 +161,12 @@ class SessionHistoryInfo {
bool GetPersist() const { return mPersist; } bool GetPersist() const { return mPersist; }
nsID& NavigationKey() { return mNavigationKey; }
const nsID& NavigationKey() const { return mNavigationKey; }
const nsID& NavigationId() const { return mNavigationId; }
nsStructuredCloneContainer* GetNavigationState() const;
private: private:
friend class SessionHistoryEntry; friend class SessionHistoryEntry;
friend struct mozilla::ipc::IPDLParamTraits<SessionHistoryInfo>; friend struct mozilla::ipc::IPDLParamTraits<SessionHistoryInfo>;
@@ -182,6 +188,10 @@ class SessionHistoryInfo {
Maybe<nsString> mSrcdocData; Maybe<nsString> mSrcdocData;
nsCOMPtr<nsIURI> mBaseURI; nsCOMPtr<nsIURI> mBaseURI;
// Fields needed for NavigationHistoryEntry.
nsID mNavigationKey = nsID::GenerateUUID();
nsID mNavigationId = nsID::GenerateUUID();
bool mLoadReplace = false; bool mLoadReplace = false;
bool mURIWasModified = false; bool mURIWasModified = false;
bool mScrollRestorationIsManual = false; bool mScrollRestorationIsManual = false;
@@ -353,12 +363,8 @@ class HistoryEntryCounterForBrowsingContext {
// SessionHistoryEntry is used to store session history data in the parent // SessionHistoryEntry is used to store session history data in the parent
// process. It holds a SessionHistoryInfo, some state shared amongst multiple // process. It holds a SessionHistoryInfo, some state shared amongst multiple
// SessionHistoryEntries, a parent and children. // SessionHistoryEntries, a parent and children.
#define NS_SESSIONHISTORYENTRY_IID \ #define NS_SESSIONHISTORYENTRY_IID \
{ \ {0x5b66a244, 0x8cec, 0x4caa, {0xaa, 0x0a, 0x78, 0x92, 0xfd, 0x17, 0xa6, 0x67}}
0x5b66a244, 0x8cec, 0x4caa, { \
0xaa, 0x0a, 0x78, 0x92, 0xfd, 0x17, 0xa6, 0x67 \
} \
}
class SessionHistoryEntry : public nsISHEntry, public nsSupportsWeakReference { class SessionHistoryEntry : public nsISHEntry, public nsSupportsWeakReference {
public: public:

View File

@@ -109,6 +109,7 @@ void SHEntrySharedParentState::CopyFrom(SHEntrySharedParentState* aEntry) {
mDynamicallyCreated = aEntry->mDynamicallyCreated; mDynamicallyCreated = aEntry->mDynamicallyCreated;
mCacheKey = aEntry->mCacheKey; mCacheKey = aEntry->mCacheKey;
mLastTouched = aEntry->mLastTouched; mLastTouched = aEntry->mLastTouched;
mNavigationState = aEntry->mNavigationState;
} }
void dom::SHEntrySharedParentState::NotifyListenersDocumentViewerEvicted() { void dom::SHEntrySharedParentState::NotifyListenersDocumentViewerEvicted() {

View File

@@ -14,6 +14,7 @@
#include "nsIWeakReferenceUtils.h" #include "nsIWeakReferenceUtils.h"
#include "nsRect.h" #include "nsRect.h"
#include "nsString.h" #include "nsString.h"
#include "nsStructuredCloneContainer.h"
#include "nsStubMutationObserver.h" #include "nsStubMutationObserver.h"
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
@@ -63,7 +64,8 @@ struct SHEntrySharedState {
mPrincipalToInherit(aPrincipalToInherit), mPrincipalToInherit(aPrincipalToInherit),
mPartitionedPrincipalToInherit(aPartitionedPrincipalToInherit), mPartitionedPrincipalToInherit(aPartitionedPrincipalToInherit),
mCsp(aCsp), mCsp(aCsp),
mContentType(aContentType) {} mContentType(aContentType),
mNavigationState(MakeRefPtr<nsStructuredCloneContainer>()) {}
// These members aren't copied by SHEntrySharedParentState::CopyFrom() because // These members aren't copied by SHEntrySharedParentState::CopyFrom() because
// they're specific to a particular content viewer. // they're specific to a particular content viewer.
@@ -83,6 +85,8 @@ struct SHEntrySharedState {
bool mIsFrameNavigation = false; bool mIsFrameNavigation = false;
bool mSaveLayoutState = true; bool mSaveLayoutState = true;
RefPtr<nsStructuredCloneContainer> mNavigationState;
protected: protected:
static uint64_t GenerateId(); static uint64_t GenerateId();
}; };

View File

@@ -99,6 +99,7 @@ LazyLogModule gSHistoryLog("nsSHistory");
#define LOG(format) MOZ_LOG(gSHistoryLog, mozilla::LogLevel::Debug, format) #define LOG(format) MOZ_LOG(gSHistoryLog, mozilla::LogLevel::Debug, format)
extern mozilla::LazyLogModule gPageCacheLog; extern mozilla::LazyLogModule gPageCacheLog;
extern mozilla::LazyLogModule gNavigationLog;
extern mozilla::LazyLogModule gSHIPBFCacheLog; extern mozilla::LazyLogModule gSHIPBFCacheLog;
// This macro makes it easier to print a log message which includes a URI's // This macro makes it easier to print a log message which includes a URI's
@@ -616,6 +617,63 @@ void nsSHistory::WalkContiguousEntries(
} }
} }
// static
void nsSHistory::WalkContiguousEntriesInOrder(
nsISHEntry* aEntry, const std::function<void(nsISHEntry*)>& aCallback) {
MOZ_ASSERT(aEntry);
nsCOMPtr<nsISHistory> shistory = aEntry->GetShistory();
if (!shistory) {
return;
}
int32_t index = shistory->GetIndexOfEntry(aEntry);
int32_t count = shistory->GetCount();
nsCOMPtr<nsIURI> targetURI = aEntry->GetURI();
// Walk backward to find the entries that have the same origin as the
// input entry.
int32_t lowerBound = index;
for (int32_t i = index - 1; i >= 0; i--) {
RefPtr<nsISHEntry> entry;
shistory->GetEntryAtIndex(i, getter_AddRefs(entry));
if (!entry) {
continue;
}
nsCOMPtr<nsIURI> uri = entry->GetURI();
if (NS_FAILED(nsContentUtils::GetSecurityManager()->CheckSameOriginURI(
targetURI, uri, false, false))) {
break;
}
lowerBound = i;
}
for (int32_t i = lowerBound; i < index; i++) {
RefPtr<nsISHEntry> entry;
shistory->GetEntryAtIndex(i, getter_AddRefs(entry));
MOZ_ASSERT(entry);
aCallback(entry);
}
// Then, call the callback on the input entry.
aCallback(aEntry);
// Then, Walk forward.
for (int32_t i = index + 1; i < count; i++) {
RefPtr<nsISHEntry> entry;
shistory->GetEntryAtIndex(i, getter_AddRefs(entry));
if (!entry) {
continue;
}
nsCOMPtr<nsIURI> uri = entry->GetURI();
if (NS_FAILED(nsContentUtils::GetSecurityManager()->CheckSameOriginURI(
targetURI, uri, false, false))) {
break;
}
aCallback(entry);
}
}
NS_IMETHODIMP NS_IMETHODIMP
nsSHistory::AddChildSHEntryHelper(nsISHEntry* aCloneRef, nsISHEntry* aNewEntry, nsSHistory::AddChildSHEntryHelper(nsISHEntry* aCloneRef, nsISHEntry* aNewEntry,
BrowsingContext* aRootBC, BrowsingContext* aRootBC,

View File

@@ -135,6 +135,9 @@ class nsSHistory : public mozilla::LinkedListElement<nsSHistory>,
// works for the root entries. It will do nothing for non-root entries. // works for the root entries. It will do nothing for non-root entries.
static void WalkContiguousEntries( static void WalkContiguousEntries(
nsISHEntry* aEntry, const std::function<void(nsISHEntry*)>& aCallback); nsISHEntry* aEntry, const std::function<void(nsISHEntry*)>& aCallback);
// Same as above, but calls aCallback on the entries in their history order.
static void WalkContiguousEntriesInOrder(
nsISHEntry* aEntry, const std::function<void(nsISHEntry*)>& aCallback);
nsTArray<nsCOMPtr<nsISHEntry>>& Entries() { return mEntries; } nsTArray<nsCOMPtr<nsISHEntry>>& Entries() { return mEntries; }

View File

@@ -175,6 +175,7 @@
#include "mozilla/dom/FeaturePolicyUtils.h" #include "mozilla/dom/FeaturePolicyUtils.h"
#include "mozilla/dom/FontFaceSet.h" #include "mozilla/dom/FontFaceSet.h"
#include "mozilla/dom/FragmentDirective.h" #include "mozilla/dom/FragmentDirective.h"
#include "mozilla/dom/NavigationBinding.h"
#include "mozilla/dom/fragmentdirectives_ffi_generated.h" #include "mozilla/dom/fragmentdirectives_ffi_generated.h"
#include "mozilla/dom/FromParser.h" #include "mozilla/dom/FromParser.h"
#include "mozilla/dom/HighlightRegistry.h" #include "mozilla/dom/HighlightRegistry.h"
@@ -10148,9 +10149,9 @@ Document* Document::Open(const Optional<nsAString>& /* unused */,
return nullptr; return nullptr;
} }
nsCOMPtr<nsIStructuredCloneContainer> stateContainer(mStateObjectContainer); nsCOMPtr<nsIStructuredCloneContainer> stateContainer(mStateObjectContainer);
rv = shell->UpdateURLAndHistory(this, newURI, stateContainer, u""_ns, rv = shell->UpdateURLAndHistory(this, newURI, stateContainer,
/* aReplace = */ true, currentURI, NavigationHistoryBehavior::Replace,
equalURIs); currentURI, equalURIs);
if (NS_WARN_IF(NS_FAILED(rv))) { if (NS_WARN_IF(NS_FAILED(rv))) {
aError.Throw(rv); aError.Throw(rv);
return nullptr; return nullptr;

View File

@@ -1170,6 +1170,8 @@ void nsGlobalWindowInner::FreeInnerObjects() {
mHistory = nullptr; mHistory = nullptr;
mNavigation = nullptr;
if (mNavigator) { if (mNavigator) {
mNavigator->OnNavigation(); mNavigator->OnNavigation();
mNavigator->Invalidate(); mNavigator->Invalidate();
@@ -1390,6 +1392,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindowInner)
NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindowInner, tmp->mRefCnt.get()) NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindowInner, tmp->mRefCnt.get())
} }
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigation)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
@@ -1496,6 +1500,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindowInner)
JS::SetRealmNonLive(js::GetNonCCWObjectRealm(wrapper)); JS::SetRealmNonLive(js::GetNonCCWObjectRealm(wrapper));
} }
NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigation)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator) NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance) NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
@@ -2412,6 +2418,14 @@ WindowProxyHolder nsGlobalWindowInner::Window() {
return WindowProxyHolder(GetBrowsingContext()); return WindowProxyHolder(GetBrowsingContext());
} }
Navigation* nsPIDOMWindowInner::Navigation() {
if (!mNavigation && Navigation::IsAPIEnabled()) {
mNavigation = new mozilla::dom::Navigation(this);
}
return mNavigation;
}
Navigator* nsPIDOMWindowInner::Navigator() { Navigator* nsPIDOMWindowInner::Navigator() {
if (!mNavigator) { if (!mNavigator) {
mNavigator = new mozilla::dom::Navigator(this); mNavigator = new mozilla::dom::Navigator(this);
@@ -2445,14 +2459,6 @@ nsHistory* nsGlobalWindowInner::GetHistory(ErrorResult& aError) {
return mHistory; return mHistory;
} }
Navigation* nsGlobalWindowInner::Navigation() {
if (!mNavigation && Navigation::IsAPIEnabled(nullptr, nullptr)) {
mNavigation = new mozilla::dom::Navigation();
}
return mNavigation;
}
CustomElementRegistry* nsGlobalWindowInner::CustomElements() { CustomElementRegistry* nsGlobalWindowInner::CustomElements() {
if (!mCustomElements) { if (!mCustomElements) {
mCustomElements = new CustomElementRegistry(this); mCustomElements = new CustomElementRegistry(this);
@@ -6663,6 +6669,10 @@ void nsGlobalWindowInner::AddSizeOfIncludingThis(
} }
} }
if (mNavigation) {
aWindowSizes.mDOMSizes.mDOMOtherSize +=
aWindowSizes.mState.mMallocSizeOf(mNavigation.get());
}
if (mNavigator) { if (mNavigator) {
aWindowSizes.mDOMSizes.mDOMOtherSize += aWindowSizes.mDOMSizes.mDOMOtherSize +=
mNavigator->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf); mNavigator->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf);

View File

@@ -120,7 +120,6 @@ class IdleRequestCallback;
class InstallTriggerImpl; class InstallTriggerImpl;
class IntlUtils; class IntlUtils;
class MediaQueryList; class MediaQueryList;
class Navigation;
class OwningExternalOrWindowProxy; class OwningExternalOrWindowProxy;
class Promise; class Promise;
class PostMessageEvent; class PostMessageEvent;
@@ -618,7 +617,6 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
void SetName(const nsAString& aName, mozilla::ErrorResult& aError); void SetName(const nsAString& aName, mozilla::ErrorResult& aError);
mozilla::dom::Location* Location() override; mozilla::dom::Location* Location() override;
nsHistory* GetHistory(mozilla::ErrorResult& aError); nsHistory* GetHistory(mozilla::ErrorResult& aError);
mozilla::dom::Navigation* Navigation();
mozilla::dom::CustomElementRegistry* CustomElements() override; mozilla::dom::CustomElementRegistry* CustomElements() override;
mozilla::dom::CustomElementRegistry* GetExistingCustomElements(); mozilla::dom::CustomElementRegistry* GetExistingCustomElements();
mozilla::dom::BarProp* GetLocationbar(mozilla::ErrorResult& aError); mozilla::dom::BarProp* GetLocationbar(mozilla::ErrorResult& aError);
@@ -1388,7 +1386,6 @@ class nsGlobalWindowInner final : public mozilla::dom::EventTarget,
RefPtr<mozilla::EventListenerManager> mListenerManager; RefPtr<mozilla::EventListenerManager> mListenerManager;
RefPtr<mozilla::dom::Location> mLocation; RefPtr<mozilla::dom::Location> mLocation;
RefPtr<nsHistory> mHistory; RefPtr<nsHistory> mHistory;
RefPtr<mozilla::dom::Navigation> mNavigation;
RefPtr<mozilla::dom::CustomElementRegistry> mCustomElements; RefPtr<mozilla::dom::CustomElementRegistry> mCustomElements;
nsTObserverArray<RefPtr<mozilla::dom::SharedWorker>> mSharedWorkers; nsTObserverArray<RefPtr<mozilla::dom::SharedWorker>> mSharedWorkers;

View File

@@ -308,6 +308,9 @@ class nsGlobalWindowOuter final : public mozilla::dom::EventTarget,
void DetachFromDocShell(bool aIsBeingDiscarded); void DetachFromDocShell(bool aIsBeingDiscarded);
// aState is only non-null if we are restoring from the bfcache.
// aForceReuseInnerWindow is only true if we are being triggered via XSLT.
// aActor is only non-null if the new document is about:blank.
virtual nsresult SetNewDocument( virtual nsresult SetNewDocument(
Document* aDocument, nsISupports* aState, bool aForceReuseInnerWindow, Document* aDocument, nsISupports* aState, bool aForceReuseInnerWindow,
mozilla::dom::WindowGlobalChild* aActor = nullptr) override; mozilla::dom::WindowGlobalChild* aActor = nullptr) override;

View File

@@ -58,6 +58,7 @@ class Element;
class Location; class Location;
class MediaDevices; class MediaDevices;
class MediaKeys; class MediaKeys;
class Navigation;
class Navigator; class Navigator;
class Performance; class Performance;
class Selection; class Selection;
@@ -590,6 +591,7 @@ class nsPIDOMWindowInner : public mozIDOMWindow {
uint32_t GetMarkedCCGeneration() { return mMarkedCCGeneration; } uint32_t GetMarkedCCGeneration() { return mMarkedCCGeneration; }
mozilla::dom::Navigation* Navigation();
mozilla::dom::Navigator* Navigator(); mozilla::dom::Navigator* Navigator();
mozilla::dom::MediaDevices* GetExtantMediaDevices() const; mozilla::dom::MediaDevices* GetExtantMediaDevices() const;
virtual mozilla::dom::Location* Location() = 0; virtual mozilla::dom::Location* Location() = 0;
@@ -661,6 +663,8 @@ class nsPIDOMWindowInner : public mozIDOMWindow {
RefPtr<mozilla::dom::Performance> mPerformance; RefPtr<mozilla::dom::Performance> mPerformance;
mozilla::UniquePtr<mozilla::dom::TimeoutManager> mTimeoutManager; mozilla::UniquePtr<mozilla::dom::TimeoutManager> mTimeoutManager;
RefPtr<mozilla::dom::Navigation> mNavigation;
RefPtr<mozilla::dom::Navigator> mNavigator; RefPtr<mozilla::dom::Navigator> mNavigator;
// These variables are only used on inner windows. // These variables are only used on inner windows.

View File

@@ -7654,6 +7654,18 @@ ContentParent::RecvGetLoadingSessionHistoryInfoFromParent(
return IPC_OK(); return IPC_OK();
} }
mozilla::ipc::IPCResult ContentParent::RecvGetContiguousSessionHistoryInfos(
const MaybeDiscarded<BrowsingContext>& aContext, SessionHistoryInfo&& aInfo,
GetContiguousSessionHistoryInfosResolver&& aResolver) {
if (aContext.IsNullOrDiscarded()) {
return IPC_OK();
}
aResolver(aContext.get_canonical()->GetContiguousSessionHistoryInfos(aInfo));
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvRemoveFromBFCache( mozilla::ipc::IPCResult ContentParent::RecvRemoveFromBFCache(
const MaybeDiscarded<BrowsingContext>& aContext) { const MaybeDiscarded<BrowsingContext>& aContext) {
if (aContext.IsNullOrDiscarded()) { if (aContext.IsNullOrDiscarded()) {

View File

@@ -1335,6 +1335,11 @@ class ContentParent final : public PContentParent,
const MaybeDiscarded<BrowsingContext>& aContext, const MaybeDiscarded<BrowsingContext>& aContext,
GetLoadingSessionHistoryInfoFromParentResolver&& aResolver); GetLoadingSessionHistoryInfoFromParentResolver&& aResolver);
mozilla::ipc::IPCResult RecvGetContiguousSessionHistoryInfos(
const MaybeDiscarded<BrowsingContext>& aContext,
SessionHistoryInfo&& aInfo,
GetContiguousSessionHistoryInfosResolver&& aResolver);
mozilla::ipc::IPCResult RecvRemoveFromBFCache( mozilla::ipc::IPCResult RecvRemoveFromBFCache(
const MaybeDiscarded<BrowsingContext>& aContext); const MaybeDiscarded<BrowsingContext>& aContext);

View File

@@ -1061,6 +1061,10 @@ parent:
async GetLoadingSessionHistoryInfoFromParent(MaybeDiscardedBrowsingContext aContext) async GetLoadingSessionHistoryInfoFromParent(MaybeDiscardedBrowsingContext aContext)
returns (LoadingSessionHistoryInfo? aLoadingInfo); returns (LoadingSessionHistoryInfo? aLoadingInfo);
async GetContiguousSessionHistoryInfos(MaybeDiscardedBrowsingContext aContext,
SessionHistoryInfo aInfo)
returns (SessionHistoryInfo[] infos);
async RemoveFromBFCache(MaybeDiscardedBrowsingContext aContext); async RemoveFromBFCache(MaybeDiscardedBrowsingContext aContext);
async InitBackground(Endpoint<PBackgroundStarterParent> aEndpoint); async InitBackground(Endpoint<PBackgroundStarterParent> aEndpoint);

View File

@@ -5,20 +5,39 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/Navigation.h" #include "mozilla/dom/Navigation.h"
#include "mozilla/dom/NavigationBinding.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/Logging.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/FeaturePolicy.h"
#include "mozilla/dom/NavigationCurrentEntryChangeEvent.h"
#include "mozilla/dom/NavigationHistoryEntry.h"
#include "mozilla/dom/SessionHistoryEntry.h"
#include "mozilla/StaticPrefs_dom.h" #include "mozilla/StaticPrefs_dom.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/NavigationCurrentEntryChangeEvent.h"
#include "mozilla/dom/NavigationHistoryEntry.h"
#include "mozilla/dom/SessionHistoryEntry.h"
#include "nsContentUtils.h"
#include "nsIXULRuntime.h" #include "nsIXULRuntime.h"
#include "nsNetUtil.h"
mozilla::LazyLogModule gNavigationLog("Navigation");
namespace mozilla::dom { namespace mozilla::dom {
NS_IMPL_CYCLE_COLLECTION_INHERITED(Navigation, DOMEventTargetHelper); NS_IMPL_CYCLE_COLLECTION_INHERITED(Navigation, DOMEventTargetHelper, mEntries);
NS_IMPL_ADDREF_INHERITED(Navigation, DOMEventTargetHelper) NS_IMPL_ADDREF_INHERITED(Navigation, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(Navigation, DOMEventTargetHelper) NS_IMPL_RELEASE_INHERITED(Navigation, DOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Navigation) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Navigation)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
Navigation::Navigation(nsPIDOMWindowInner* aWindow) : mWindow(aWindow) {
MOZ_ASSERT(aWindow);
}
JSObject* Navigation::WrapObject(JSContext* aCx, JSObject* Navigation::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) { JS::Handle<JSObject*> aGivenProto) {
return Navigation_Binding::Wrap(aCx, this, aGivenProto); return Navigation_Binding::Wrap(aCx, this, aGivenProto);
@@ -30,4 +49,228 @@ bool Navigation::IsAPIEnabled(JSContext* /* unused */, JSObject* /* unused */) {
StaticPrefs::dom_navigation_webidl_enabled_DoNotUseDirectly(); StaticPrefs::dom_navigation_webidl_enabled_DoNotUseDirectly();
} }
void Navigation::Entries(
nsTArray<RefPtr<NavigationHistoryEntry>>& aResult) const {
aResult = mEntries.Clone();
}
already_AddRefed<NavigationHistoryEntry> Navigation::GetCurrentEntry() const {
if (HasEntriesAndEventsDisabled()) {
return nullptr;
}
if (!mCurrentEntryIndex) {
return nullptr;
}
MOZ_LOG(gNavigationLog, LogLevel::Debug,
("Current Entry: %d; Amount of Entries: %d", int(*mCurrentEntryIndex),
int(mEntries.Length())));
MOZ_ASSERT(*mCurrentEntryIndex < mEntries.Length());
RefPtr entry{mEntries[*mCurrentEntryIndex]};
return entry.forget();
}
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-navigation-updatecurrententry
void Navigation::UpdateCurrentEntry(
JSContext* aCx, const NavigationUpdateCurrentEntryOptions& aOptions,
ErrorResult& aRv) {
RefPtr currentEntry(GetCurrentEntry());
if (!currentEntry) {
aRv.ThrowInvalidStateError(
"Can't call updateCurrentEntry without a valid entry.");
return;
}
JS::Rooted<JS::Value> state(aCx, aOptions.mState);
auto serializedState = MakeRefPtr<nsStructuredCloneContainer>();
nsresult rv = serializedState->InitFromJSVal(state, aCx);
if (NS_FAILED(rv)) {
aRv.ThrowDataCloneError(
"Failed to serialize value for updateCurrentEntry.");
return;
}
currentEntry->SetState(serializedState);
NavigationCurrentEntryChangeEventInit init;
init.mFrom = currentEntry;
// Leaving the navigation type unspecified means it will be initialized to
// null.
RefPtr event = NavigationCurrentEntryChangeEvent::Constructor(
this, u"currententrychange"_ns, init);
DispatchEvent(*event);
}
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#has-entries-and-events-disabled
bool Navigation::HasEntriesAndEventsDisabled() const {
Document* doc = mWindow->GetDoc();
return !doc->IsCurrentActiveDocument() ||
(NS_IsAboutBlank(doc->GetDocumentURI()) && doc->IsInitialDocument()) ||
doc->GetPrincipal()->GetIsNullPrincipal();
}
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#initialize-the-navigation-api-entries-for-a-new-document
void Navigation::InitializeHistoryEntries(
mozilla::Span<const SessionHistoryInfo> aNewSHInfos,
const SessionHistoryInfo* aInitialSHInfo) {
mEntries.Clear();
mCurrentEntryIndex.reset();
if (HasEntriesAndEventsDisabled()) {
return;
}
for (auto i = 0ul; i < aNewSHInfos.Length(); i++) {
mEntries.AppendElement(
MakeRefPtr<NavigationHistoryEntry>(mWindow, &aNewSHInfos[i], i));
if (aNewSHInfos[i].NavigationKey() == aInitialSHInfo->NavigationKey()) {
mCurrentEntryIndex = Some(i);
}
}
LogHistory();
nsID key = aInitialSHInfo->NavigationKey();
nsID id = aInitialSHInfo->NavigationId();
MOZ_LOG(
gNavigationLog, LogLevel::Debug,
("aInitialSHInfo: %s %s\n", key.ToString().get(), id.ToString().get()));
}
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#update-the-navigation-api-entries-for-a-same-document-navigation
void Navigation::UpdateEntriesForSameDocumentNavigation(
SessionHistoryInfo* aDestinationSHE, NavigationType aNavigationType) {
// Step 1.
if (HasEntriesAndEventsDisabled()) {
return;
}
MOZ_LOG(gNavigationLog, LogLevel::Debug,
("Updating entries for same-document navigation"));
// Steps 2-7.
RefPtr<NavigationHistoryEntry> oldCurrentEntry = GetCurrentEntry();
nsTArray<RefPtr<NavigationHistoryEntry>> disposedEntries;
switch (aNavigationType) {
case NavigationType::Traverse:
MOZ_LOG(gNavigationLog, LogLevel::Debug, ("Traverse navigation"));
mCurrentEntryIndex.reset();
for (auto i = 0ul; i < mEntries.Length(); i++) {
if (mEntries[i]->IsSameEntry(aDestinationSHE)) {
mCurrentEntryIndex = Some(i);
break;
}
}
MOZ_ASSERT(mCurrentEntryIndex);
break;
case NavigationType::Push:
MOZ_LOG(gNavigationLog, LogLevel::Debug, ("Push navigation"));
mCurrentEntryIndex =
Some(mCurrentEntryIndex ? *mCurrentEntryIndex + 1 : 0);
while (*mCurrentEntryIndex < mEntries.Length()) {
disposedEntries.AppendElement(mEntries.PopLastElement());
}
mEntries.AppendElement(MakeRefPtr<NavigationHistoryEntry>(
mWindow, aDestinationSHE, *mCurrentEntryIndex));
break;
case NavigationType::Replace:
MOZ_LOG(gNavigationLog, LogLevel::Debug, ("Replace navigation"));
disposedEntries.AppendElement(oldCurrentEntry);
aDestinationSHE->NavigationKey() = oldCurrentEntry->Key();
mEntries[*mCurrentEntryIndex] = MakeRefPtr<NavigationHistoryEntry>(
mWindow, aDestinationSHE, *mCurrentEntryIndex);
break;
case NavigationType::Reload:
break;
}
// TODO: Step 8.
// Steps 9-12.
{
nsAutoMicroTask mt;
AutoEntryScript aes(mWindow->AsGlobal(),
"UpdateEntriesForSameDocumentNavigation");
ScheduleEventsFromNavigation(aNavigationType, oldCurrentEntry,
std::move(disposedEntries));
}
}
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#update-the-navigation-api-entries-for-reactivation
void Navigation::UpdateForReactivation(SessionHistoryInfo* aReactivatedEntry) {
// NAV-TODO
}
void Navigation::ScheduleEventsFromNavigation(
NavigationType aType, const RefPtr<NavigationHistoryEntry>& aPreviousEntry,
nsTArray<RefPtr<NavigationHistoryEntry>>&& aDisposedEntries) {
nsContentUtils::AddScriptRunner(NS_NewRunnableFunction(
"mozilla::dom::Navigation::ScheduleEventsFromNavigation",
[self = RefPtr(this), previousEntry = RefPtr(aPreviousEntry),
disposedEntries = std::move(aDisposedEntries), aType]() {
if (previousEntry) {
NavigationCurrentEntryChangeEventInit init;
init.mFrom = previousEntry;
init.mNavigationType.SetValue(aType);
RefPtr event = NavigationCurrentEntryChangeEvent::Constructor(
self, u"currententrychange"_ns, init);
self->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);
}
}));
}
namespace {
void LogEntry(NavigationHistoryEntry* aEntry, uint64_t aIndex, uint64_t aTotal,
bool aIsCurrent) {
if (!aEntry) {
MOZ_LOG(gNavigationLog, LogLevel::Debug,
(" +- %d NHEntry null\n", int(aIndex)));
return;
}
nsString key, id;
aEntry->GetKey(key);
aEntry->GetId(id);
MOZ_LOG(gNavigationLog, LogLevel::Debug,
("%s+- %d NHEntry %p %s %s\n", aIsCurrent ? ">" : " ", int(aIndex),
aEntry, NS_ConvertUTF16toUTF8(key).get(),
NS_ConvertUTF16toUTF8(id).get()));
nsAutoString url;
aEntry->GetUrl(url);
MOZ_LOG(gNavigationLog, LogLevel::Debug,
(" URL = %s\n", NS_ConvertUTF16toUTF8(url).get()));
}
} // namespace
void Navigation::LogHistory() const {
if (!MOZ_LOG_TEST(gNavigationLog, LogLevel::Debug)) {
return;
}
MOZ_LOG(gNavigationLog, LogLevel::Debug,
("Navigation %p (current entry index: %d)\n", this,
mCurrentEntryIndex ? int(*mCurrentEntryIndex) : -1));
auto length = mEntries.Length();
for (uint64_t i = 0; i < length; i++) {
LogEntry(mEntries[i], i, length,
mCurrentEntryIndex && i == *mCurrentEntryIndex);
}
}
} // namespace mozilla::dom } // namespace mozilla::dom

View File

@@ -8,6 +8,7 @@
#define mozilla_dom_Navigation_h___ #define mozilla_dom_Navigation_h___
#include "mozilla/DOMEventTargetHelper.h" #include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/dom/NavigationBinding.h"
namespace mozilla::dom { namespace mozilla::dom {
@@ -20,21 +21,33 @@ struct NavigationUpdateCurrentEntryOptions;
struct NavigationReloadOptions; struct NavigationReloadOptions;
struct NavigationResult; struct NavigationResult;
class SessionHistoryInfo;
class Navigation final : public DOMEventTargetHelper { class Navigation final : public DOMEventTargetHelper {
public: public:
NS_DECL_ISUPPORTS_INHERITED NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Navigation, DOMEventTargetHelper) NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Navigation, DOMEventTargetHelper)
void Entries(nsTArray<RefPtr<NavigationHistoryEntry>>& aResult) {} explicit Navigation(nsPIDOMWindowInner* aWindow);
already_AddRefed<NavigationHistoryEntry> GetCurrentEntry() { return {}; }
// Navigation.webidl
void Entries(nsTArray<RefPtr<NavigationHistoryEntry>>& aResult) const;
already_AddRefed<NavigationHistoryEntry> GetCurrentEntry() const;
MOZ_CAN_RUN_SCRIPT
void UpdateCurrentEntry(JSContext* aCx, void UpdateCurrentEntry(JSContext* aCx,
const NavigationUpdateCurrentEntryOptions& aOptions, const NavigationUpdateCurrentEntryOptions& aOptions,
ErrorResult& aRv) {} ErrorResult& aRv);
already_AddRefed<NavigationTransition> GetTransition() { return {}; } already_AddRefed<NavigationTransition> GetTransition() { return {}; }
already_AddRefed<NavigationActivation> GetActivation() { return {}; } already_AddRefed<NavigationActivation> GetActivation() { return {}; }
bool CanGoBack() { return {}; } bool CanGoBack() {
bool CanGoForward() { return {}; } return !HasEntriesAndEventsDisabled() && mCurrentEntryIndex &&
*mCurrentEntryIndex != 0;
}
bool CanGoForward() {
return !HasEntriesAndEventsDisabled() && mCurrentEntryIndex &&
*mCurrentEntryIndex != mEntries.Length() - 1;
}
void Navigate(JSContext* aCx, const nsAString& aUrl, void Navigate(JSContext* aCx, const nsAString& aUrl,
const NavigationNavigateOptions& aOptions, const NavigationNavigateOptions& aOptions,
@@ -55,15 +68,45 @@ class Navigation final : public DOMEventTargetHelper {
IMPL_EVENT_HANDLER(navigateerror); IMPL_EVENT_HANDLER(navigateerror);
IMPL_EVENT_HANDLER(currententrychange); IMPL_EVENT_HANDLER(currententrychange);
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#initialize-the-navigation-api-entries-for-a-new-document
void InitializeHistoryEntries(
mozilla::Span<const SessionHistoryInfo> aNewSHInfos,
const SessionHistoryInfo* aInitialSHInfo);
// 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);
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#update-the-navigation-api-entries-for-a-same-document-navigation
void UpdateEntriesForSameDocumentNavigation(
SessionHistoryInfo* aDestinationSHE, NavigationType aNavigationType);
JSObject* WrapObject(JSContext* aCx, JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override; JS::Handle<JSObject*> aGivenProto) override;
// The Navigation API is only enabled if both SessionHistoryInParent and // The Navigation API is only enabled if both SessionHistoryInParent and
// the dom.navigation.webidl.enabled pref are set. // the dom.navigation.webidl.enabled pref are set.
static bool IsAPIEnabled(JSContext* /* unused */, JSObject* /* unused */); static bool IsAPIEnabled(JSContext* /* unused */ = nullptr,
JSObject* /* unused */ = nullptr);
private: private:
~Navigation() = default; ~Navigation() = default;
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#has-entries-and-events-disabled
bool HasEntriesAndEventsDisabled() const;
void ScheduleEventsFromNavigation(
NavigationType aType,
const RefPtr<NavigationHistoryEntry>& aPreviousEntry,
nsTArray<RefPtr<NavigationHistoryEntry>>&& aDisposedEntries);
void LogHistory() const;
nsCOMPtr<nsPIDOMWindowInner> mWindow;
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#navigation-entry-list
nsTArray<RefPtr<NavigationHistoryEntry>> mEntries;
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#navigation-current-entry
Maybe<uint64_t> mCurrentEntryIndex;
}; };
} // namespace mozilla::dom } // namespace mozilla::dom

View File

@@ -7,19 +7,131 @@
#include "mozilla/dom/NavigationHistoryEntry.h" #include "mozilla/dom/NavigationHistoryEntry.h"
#include "mozilla/dom/NavigationHistoryEntryBinding.h" #include "mozilla/dom/NavigationHistoryEntryBinding.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/SessionHistoryEntry.h"
#include "nsDocShell.h"
extern mozilla::LazyLogModule gNavigationLog;
namespace mozilla::dom { namespace mozilla::dom {
NS_IMPL_CYCLE_COLLECTION_INHERITED(NavigationHistoryEntry, NS_IMPL_CYCLE_COLLECTION_INHERITED(NavigationHistoryEntry, DOMEventTargetHelper,
DOMEventTargetHelper); mWindow);
NS_IMPL_ADDREF_INHERITED(NavigationHistoryEntry, DOMEventTargetHelper) NS_IMPL_ADDREF_INHERITED(NavigationHistoryEntry, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(NavigationHistoryEntry, DOMEventTargetHelper) NS_IMPL_RELEASE_INHERITED(NavigationHistoryEntry, DOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NavigationHistoryEntry) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NavigationHistoryEntry)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
NavigationHistoryEntry::NavigationHistoryEntry(
nsPIDOMWindowInner* aWindow, const SessionHistoryInfo* aSHInfo,
int64_t aIndex)
: mWindow(aWindow),
mSHInfo(MakeUnique<SessionHistoryInfo>(*aSHInfo)),
mIndex(aIndex) {}
NavigationHistoryEntry::~NavigationHistoryEntry() = default;
void NavigationHistoryEntry::GetUrl(nsAString& aResult) const {
if (!GetCurrentDocument()->IsCurrentActiveDocument()) {
return;
}
if (!SameDocument()) {
auto referrerPolicy = GetCurrentDocument()->ReferrerPolicy();
if (referrerPolicy == ReferrerPolicy::No_referrer ||
referrerPolicy == ReferrerPolicy::Origin) {
return;
}
}
MOZ_ASSERT(mSHInfo);
nsCOMPtr<nsIURI> uri = mSHInfo->GetURI();
MOZ_ASSERT(uri);
nsCString uriSpec;
uri->GetSpec(uriSpec);
CopyUTF8toUTF16(uriSpec, aResult);
}
void NavigationHistoryEntry::GetKey(nsAString& aResult) const {
if (!GetCurrentDocument()->IsCurrentActiveDocument()) {
return;
}
nsIDToCString keyString(mSHInfo->NavigationKey());
// Omit the curly braces and NUL.
CopyUTF8toUTF16(Substring(keyString.get() + 1, NSID_LENGTH - 3), aResult);
}
void NavigationHistoryEntry::GetId(nsAString& aResult) const {
if (!GetCurrentDocument()->IsCurrentActiveDocument()) {
return;
}
nsIDToCString idString(mSHInfo->NavigationId());
// Omit the curly braces and NUL.
CopyUTF8toUTF16(Substring(idString.get() + 1, NSID_LENGTH - 3), aResult);
}
int64_t NavigationHistoryEntry::Index() const {
MOZ_ASSERT(mSHInfo);
if (!GetCurrentDocument()->IsCurrentActiveDocument()) {
return -1;
}
return mIndex;
}
bool NavigationHistoryEntry::SameDocument() const {
MOZ_ASSERT(mSHInfo);
auto* docShell = static_cast<nsDocShell*>(mWindow->GetDocShell());
return docShell->IsSameDocumentAsActiveEntry(*mSHInfo);
}
void NavigationHistoryEntry::GetState(JSContext* aCx,
JS::MutableHandle<JS::Value> aResult,
ErrorResult& aRv) const {
if (!mSHInfo) {
return;
}
RefPtr<nsStructuredCloneContainer> state = mSHInfo->GetNavigationState();
if (!state) {
aResult.setUndefined();
return;
}
nsresult rv = state->DeserializeToJsval(aCx, aResult);
if (NS_FAILED(rv)) {
// TODO change this to specific exception
aRv.Throw(rv);
}
}
void NavigationHistoryEntry::SetState(nsStructuredCloneContainer* aState) {
RefPtr<nsStructuredCloneContainer> state = mSHInfo->GetNavigationState();
state->Copy(*aState);
}
bool NavigationHistoryEntry::IsSameEntry(
const SessionHistoryInfo* aSHInfo) const {
return mSHInfo->NavigationId() == aSHInfo->NavigationId();
}
bool NavigationHistoryEntry::SharesDocumentWith(
const SessionHistoryInfo& aSHInfo) const {
return mSHInfo->SharesDocumentWith(aSHInfo);
}
JSObject* NavigationHistoryEntry::WrapObject( JSObject* NavigationHistoryEntry::WrapObject(
JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
return NavigationHistoryEntry_Binding::Wrap(aCx, this, aGivenProto); return NavigationHistoryEntry_Binding::Wrap(aCx, this, aGivenProto);
} }
Document* NavigationHistoryEntry::GetCurrentDocument() const {
return mWindow->GetDoc();
}
const nsID& NavigationHistoryEntry::Key() const {
return mSHInfo->NavigationKey();
}
} // namespace mozilla::dom } // namespace mozilla::dom

View File

@@ -9,30 +9,50 @@
#include "mozilla/DOMEventTargetHelper.h" #include "mozilla/DOMEventTargetHelper.h"
class nsStructuredCloneContainer;
namespace mozilla::dom { namespace mozilla::dom {
class SessionHistoryInfo;
class NavigationHistoryEntry final : public DOMEventTargetHelper { class NavigationHistoryEntry final : public DOMEventTargetHelper {
public: public:
NS_DECL_ISUPPORTS_INHERITED NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(NavigationHistoryEntry, NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(NavigationHistoryEntry,
DOMEventTargetHelper) DOMEventTargetHelper)
void GetUrl(nsAString& aResult) const {} NavigationHistoryEntry(nsPIDOMWindowInner* aWindow,
void GetKey(nsAString& aResult) const {} const SessionHistoryInfo* aSHInfo, int64_t aIndex);
void GetId(nsAString& aResult) const {}
int64_t Index() const { return {}; } void GetUrl(nsAString& aResult) const;
bool SameDocument() const { return {}; } void GetKey(nsAString& aResult) const;
void GetId(nsAString& aResult) const;
int64_t Index() const;
bool SameDocument() const;
void GetState(JSContext* aCx, JS::MutableHandle<JS::Value> aResult, void GetState(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
ErrorResult& aRv) {} ErrorResult& aRv) const;
void SetState(nsStructuredCloneContainer* aState);
IMPL_EVENT_HANDLER(dispose); IMPL_EVENT_HANDLER(dispose);
JSObject* WrapObject(JSContext* aCx, JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override; JS::Handle<JSObject*> aGivenProto) override;
bool IsSameEntry(const SessionHistoryInfo* aSHInfo) const;
bool SharesDocumentWith(const SessionHistoryInfo& aSHInfo) const;
const nsID& Key() const;
private: private:
~NavigationHistoryEntry() = default; ~NavigationHistoryEntry();
Document* GetCurrentDocument() const;
nsCOMPtr<nsPIDOMWindowInner> mWindow;
UniquePtr<SessionHistoryInfo> mSHInfo;
int64_t mIndex;
}; };
} // namespace mozilla::dom } // namespace mozilla::dom

View File

@@ -1,8 +1,4 @@
[entries-array-equality.html] [entries-array-equality.html]
prefs: [dom.navigation.webidl.enabled:true]
expected: expected:
if (os == "android") and fission: [OK, TIMEOUT] if (os == "android") and fission: [OK, TIMEOUT]
[navigation.entries() should not return an identical object on repeated invocations]
expected: FAIL
[navigation.entries() should not return an identical object on repeated invocations]
expected: FAIL

View File

@@ -1,8 +1,4 @@
[entries-when-inactive.html] [entries-when-inactive.html]
prefs: [dom.navigation.webidl.enabled:true]
expected: expected:
if (os == "android") and fission: [OK, TIMEOUT] if (os == "android") and fission: [OK, TIMEOUT]
[A non-active entry in navigation.entries() should not be modified when a different entry is modified]
expected: FAIL
[A non-active entry in navigation.entries() should not be modified when a different entry is modified]
expected: FAIL

View File

@@ -1,3 +1,2 @@
[key-id-back-same-document.html] [key-id-back-same-document.html]
[NavigationHistoryEntry's key and id on same-document back navigation] prefs: [dom.navigation.webidl.enabled:true]
expected: FAIL

View File

@@ -1,4 +1,2 @@
[opaque-origin-data-url.html] [opaque-origin-data-url.html]
expected: TIMEOUT prefs: [dom.navigation.webidl.enabled:true]
[entries() and currentEntry after navigation to a data: URL (which has an opaque origin)]
expected: TIMEOUT

View File

@@ -1,5 +1,4 @@
[opaque-origin.html] [opaque-origin.html]
prefs: [dom.navigation.webidl.enabled:true]
expected: expected:
if (os == "android") and fission: [OK, TIMEOUT] if (os == "android") and fission: [OK, TIMEOUT]
[navigation.currentEntry/entries()/canGoBack/canGoForward in an opaque origin iframe]
expected: FAIL

View File

@@ -1,5 +1,4 @@
[sameDocument-after-fragment-navigate.html] [sameDocument-after-fragment-navigate.html]
prefs: [dom.navigation.webidl.enabled:true]
expected: expected:
if (os == "android") and fission: [OK, TIMEOUT] if (os == "android") and fission: [OK, TIMEOUT]
[entry.sameDocument after same-document navigations]
expected: FAIL