commit 2efb9540ff0bd24830cb58cf8527ad5ae2b9ee8b
parent cf526ee996bd497c93e083f98094b331b9f4e120
Author: Andreas Farre <farre@mozilla.com>
Date: Tue, 21 Oct 2025 08:28:17 +0000
Bug 1992939 - Pre-compute current entry index for intercepted traverse navigations. r=jjaschke
Differential Revision: https://phabricator.services.mozilla.com/D269220
Diffstat:
4 files changed, 56 insertions(+), 28 deletions(-)
diff --git a/dom/events/NavigateEvent.cpp b/dom/events/NavigateEvent.cpp
@@ -19,6 +19,9 @@
extern mozilla::LazyLogModule gNavigationAPILog;
+#define LOG_FMTI(format, ...) \
+ MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Info, format, ##__VA_ARGS__);
+
#define LOG_FMT(format, ...) \
MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug, format, ##__VA_ARGS__);
@@ -136,6 +139,8 @@ static void MaybeReportWarningToConsole(Document* aDocument,
// https://html.spec.whatwg.org/#dom-navigateevent-intercept
void NavigateEvent::Intercept(const NavigationInterceptOptions& aOptions,
ErrorResult& aRv) {
+ LOG_FMTI("Called NavigateEvent.intercept()");
+
// Step 1
if (PerformSharedChecks(aRv); aRv.Failed()) {
return;
@@ -197,6 +202,8 @@ void NavigateEvent::Intercept(const NavigationInterceptOptions& aOptions,
// https://html.spec.whatwg.org/#dom-navigateevent-scroll
void NavigateEvent::Scroll(ErrorResult& aRv) {
+ LOG_FMTI("Called NavigateEvent.scroll()");
+
// Step 1
if (PerformSharedChecks(aRv); aRv.Failed()) {
return;
@@ -474,4 +481,5 @@ void NavigateEvent::ProcessScrollBehavior() {
}
} // namespace mozilla::dom
+#undef LOG_FMTI
#undef LOG_FMT
diff --git a/dom/navigation/Navigation.cpp b/dom/navigation/Navigation.cpp
@@ -44,9 +44,18 @@
mozilla::LazyLogModule gNavigationAPILog("NavigationAPI");
-#define LOG_FMT(format, ...) \
+#define LOG_FMTW(format, ...) \
+ MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Warning, format, ##__VA_ARGS__);
+
+#define LOG_FMTI(format, ...) \
+ MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Info, format, ##__VA_ARGS__);
+
+#define LOG_FMTD(format, ...) \
MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug, format, ##__VA_ARGS__);
+#define LOG_FMTV(format, ...) \
+ MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Verbose, format, ##__VA_ARGS__);
+
namespace mozilla::dom {
static void InitNavigationResult(NavigationResult& aResult,
@@ -227,6 +236,7 @@ already_AddRefed<NavigationHistoryEntry> Navigation::GetCurrentEntry() const {
void Navigation::UpdateCurrentEntry(
JSContext* aCx, const NavigationUpdateCurrentEntryOptions& aOptions,
ErrorResult& aRv) {
+ LOG_FMTI("Called navigation.updateCurrentEntry()");
RefPtr currentEntry(GetCurrentEntry());
if (!currentEntry) {
aRv.ThrowInvalidStateError(
@@ -287,10 +297,10 @@ bool Navigation::HasEntriesAndEventsDisabled() const {
void Navigation::InitializeHistoryEntries(
mozilla::Span<const SessionHistoryInfo> aNewSHInfos,
const SessionHistoryInfo* aInitialSHInfo) {
- LOG_FMT("Attempting to initialize history entries for {}.",
- aInitialSHInfo->GetURI()
- ? aInitialSHInfo->GetURI()->GetSpecOrDefault()
- : "<no uri>"_ns)
+ LOG_FMTD("Attempting to initialize history entries for {}.",
+ aInitialSHInfo->GetURI()
+ ? aInitialSHInfo->GetURI()->GetSpecOrDefault()
+ : "<no uri>"_ns)
mEntries.Clear();
mCurrentEntryIndex.reset();
@@ -332,13 +342,7 @@ void Navigation::UpdateEntriesForSameDocumentNavigation(
switch (aNavigationType) {
case NavigationType::Traverse:
MOZ_LOG(gNavigationAPILog, LogLevel::Debug, ("Traverse navigation"));
- mCurrentEntryIndex.reset();
- for (auto i = 0ul; i < mEntries.Length(); i++) {
- if (mEntries[i]->IsSameEntry(aDestinationSHE)) {
- mCurrentEntryIndex = Some(i);
- break;
- }
- }
+ SetCurrentEntryIndex(aDestinationSHE);
MOZ_ASSERT(mCurrentEntryIndex);
break;
@@ -493,9 +497,8 @@ Navigation::CreateSerializedStateAndMaybeSetEarlyErrorResult(
void Navigation::Navigate(JSContext* aCx, const nsAString& aUrl,
const NavigationNavigateOptions& aOptions,
NavigationResult& aResult) {
- MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug,
- "Called navigation.navigate() with url = {}",
- NS_ConvertUTF16toUTF8(aUrl));
+ LOG_FMTI("Called navigation.navigate() with url = {}",
+ NS_ConvertUTF16toUTF8(aUrl));
// 4. Let document be this's relevant global object's associated Document.
const RefPtr<Document> document = GetAssociatedDocument();
if (!document) {
@@ -607,7 +610,7 @@ void Navigation::Navigate(JSContext* aCx, const nsAString& aUrl,
void Navigation::PerformNavigationTraversal(JSContext* aCx, const nsID& aKey,
const NavigationOptions& aOptions,
NavigationResult& aResult) {
- LOG_FMT("traverse navigation to {}", aKey.ToString().get());
+ LOG_FMTV("traverse navigation to {}", aKey.ToString().get());
// 1. Let document be navigation's relevant global object's associated
// Document.
const Document* document = GetAssociatedDocument();
@@ -730,7 +733,7 @@ void Navigation::PerformNavigationTraversal(JSContext* aCx, const nsID& aKey,
// https://html.spec.whatwg.org/#dom-navigation-reload
void Navigation::Reload(JSContext* aCx, const NavigationReloadOptions& aOptions,
NavigationResult& aResult) {
- MOZ_LOG(gNavigationAPILog, LogLevel::Debug, ("Called navigation.reload()"));
+ LOG_FMTI("Called navigation.reload()");
// 1. Let document be this's relevant global object's associated Document.
const RefPtr<Document> document = GetAssociatedDocument();
if (!document) {
@@ -809,9 +812,8 @@ void Navigation::Reload(JSContext* aCx, const NavigationReloadOptions& aOptions,
void Navigation::TraverseTo(JSContext* aCx, const nsAString& aKey,
const NavigationOptions& aOptions,
NavigationResult& aResult) {
- MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug,
- "Called navigation.traverseTo() with key = {}",
- NS_ConvertUTF16toUTF8(aKey).get());
+ LOG_FMTI("Called navigation.traverseTo() with key = {}",
+ NS_ConvertUTF16toUTF8(aKey).get());
// 1. If this's current entry index is −1, then return an early error result
// for an "InvalidStateError" DOMException.
@@ -846,7 +848,7 @@ void Navigation::TraverseTo(JSContext* aCx, const nsAString& aKey,
// https://html.spec.whatwg.org/#dom-navigation-back
void Navigation::Back(JSContext* aCx, const NavigationOptions& aOptions,
NavigationResult& aResult) {
- MOZ_LOG(gNavigationAPILog, LogLevel::Debug, ("Called navigation.back()"));
+ LOG_FMTI("Called navigation.back()");
// 1. If this's current entry index is −1 or 0, then return an early error
// result for an "InvalidStateError" DOMException.
if (mCurrentEntryIndex.isNothing() || *mCurrentEntryIndex == 0 ||
@@ -869,7 +871,7 @@ void Navigation::Back(JSContext* aCx, const NavigationOptions& aOptions,
// https://html.spec.whatwg.org/#dom-navigation-forward
void Navigation::Forward(JSContext* aCx, const NavigationOptions& aOptions,
NavigationResult& aResult) {
- MOZ_LOG(gNavigationAPILog, LogLevel::Debug, ("Called navigation.forward()"));
+ LOG_FMTI("Called navigation.forward()");
// 1. If this's current entry index is −1 or is equal to this's entry list's
// size − 1, then return an early error result for an "InvalidStateError"
@@ -1111,7 +1113,7 @@ static void LogEvent(Event* aEvent, NavigateEvent* aOngoingEvent,
}
}
- LOG_FMT("{}", fmt::join(log.begin(), log.end(), std::string_view{" "}));
+ LOG_FMTD("{}", fmt::join(log.begin(), log.end(), std::string_view{" "}));
}
nsresult Navigation::FireEvent(const nsAString& aName) {
@@ -1384,6 +1386,11 @@ bool Navigation::InnerFireNavigateEvent(
// 32.7.1.4
ResumeApplyTheHistoryStep(entry->SessionHistoryInfo(),
navigable->Top(), userInvolvement);
+
+ // This is not in the spec, but both Chrome and Safari does this or
+ // something similar.
+ MOZ_ASSERT(entry->Index() >= 0);
+ mCurrentEntryIndex = Some(entry->Index());
}
break;
@@ -1555,7 +1562,7 @@ bool Navigation::InnerFireNavigateEvent(
// performs these steps, since spec can perform more of
// #apply-the-history-steps in a synchronous way.
if (apiMethodTracker) {
- LOG_FMT("Waiting for committed");
+ LOG_FMTD("Waiting for committed");
apiMethodTracker->CommittedPromise()->AddCallbacksWithCycleCollectedArgs(
[successSteps, failureSteps](
JSContext*, JS::Handle<JS::Value>, ErrorResult&,
@@ -1572,7 +1579,7 @@ bool Navigation::InnerFireNavigateEvent(
nsCOMPtr(globalObject),
nsTArray<RefPtr<Promise>>(std::move(promiseList)), scope);
} else {
- LOG_FMT("No API method tracker, not waiting for committed");
+ LOG_FMTD("No API method tracker, not waiting for committed");
// If we don't have an apiMethodTracker we can immediately start waiting
// for the promise list.
Promise::WaitForAll(globalObject, promiseList, successSteps, failureSteps,
@@ -1654,6 +1661,16 @@ void Navigation::PromoteUpcomingAPIMethodTrackerToOngoing(
navigation->mUpcomingTraverseAPIMethodTrackers.Remove(*key);
}
+void Navigation::SetCurrentEntryIndex(const SessionHistoryInfo* aTargetInfo) {
+ mCurrentEntryIndex.reset();
+ if (auto* entry = FindNavigationHistoryEntry(*aTargetInfo)) {
+ MOZ_ASSERT(entry->Index() > 0);
+ mCurrentEntryIndex = Some(entry->Index());
+ }
+
+ LOG_FMTW("Session history entry did not exist");
+}
+
// https://html.spec.whatwg.org/#inform-the-navigation-api-about-aborting-navigation
void Navigation::InnerInformAboutAbortingNavigation(JSContext* aCx) {
// As per https://github.com/whatwg/html/issues/11579, we should abort all
@@ -1957,3 +1974,7 @@ void Navigation::CreateNavigationActivationFrom(
}
} // namespace mozilla::dom
+
+#undef LOG_FMTV
+#undef LOG_FMTD
+#undef LOG_FMTI
diff --git a/dom/navigation/Navigation.h b/dom/navigation/Navigation.h
@@ -205,6 +205,8 @@ class Navigation final : public DOMEventTargetHelper {
static void CleanUp(NavigationAPIMethodTracker* aNavigationAPIMethodTracker);
+ void SetCurrentEntryIndex(const SessionHistoryInfo* aTargetInfo);
+
Document* GetAssociatedDocument() const;
// Update the state managing if we need to dispatch the traverse event or not.
diff --git a/testing/web-platform/meta/navigation-api/navigate-event/intercept-same-document-history-back.html.ini b/testing/web-platform/meta/navigation-api/navigate-event/intercept-same-document-history-back.html.ini
@@ -1,3 +0,0 @@
-[intercept-same-document-history-back.html]
- [event.intercept() can intercept same-document history.back()]
- expected: FAIL