WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
NEW
314976
[Navigation API] Private Browsing leaves navigation.currentEntry stale after intercepted same-origin cross-document navigation
https://bugs.webkit.org/show_bug.cgi?id=314976
Summary
[Navigation API] Private Browsing leaves navigation.currentEntry stale after ...
k8o
Reported
2026-05-17 07:29:41 PDT
## Summary In Private Browsing (Reproduced on 26.3.1 and 26.4), after `NavigateEvent.intercept({ handler })` commits an intercepted same-origin cross-document navigation: - `location.href` is updated to the new URL (correct) - `navigation.currentEntry.url` and `.id` are NOT updated — they still reference the previous entry - `currententrychange` event does NOT fire - `navigatesuccess` event fires, but `navigation.currentEntry` still reports the previous entry at that point The same code works correctly in non-private Safari 26.4 and in Chrome (any mode), so this is specific to WebKit Private Browsing. This affects routing code that follows the Navigation API contract by reading `navigation.currentEntry` (and listening to `currententrychange`) after an intercepted navigation commits. ## Steps to Reproduce Save the HTML below as `index.html` and serve it from an HTTP origin, for example: ``` python3 -m http.server 8000 ``` Then open `
http://localhost:8000/index.html
` in a Safari **Private Browsing** window. The `./next` resource does **not** need to exist; the navigation is same-origin and intercepted before loading a new document. ```html <!DOCTYPE html> <html> <body> <a id="link" href="./next">click me</a> <pre id="log"></pre> <script> const $log = document.getElementById("log"); const out = (label, data) => { const line = label + " " + JSON.stringify(data); $log.textContent += line + "\n"; console.log(line); }; if (!("navigation" in window)) { out("unsupported", { ua: navigator.userAgent }); } navigation.addEventListener("navigate", (event) => { const beforeId = navigation.currentEntry?.id; out("navigate", { dest: event.destination.url, canIntercept: event.canIntercept, sameDocument: event.destination.sameDocument, navigationType: event.navigationType, beforeCurrentUrl: navigation.currentEntry?.url, }); if (!event.canIntercept) return; event.intercept({ handler: async () => { out("handler", { locationHref: location.href, currentEntryUrl: navigation.currentEntry?.url, currentEntryId: navigation.currentEntry?.id, currentEntryUnchanged: navigation.currentEntry?.id === beforeId, }); }, }); }); navigation.addEventListener("currententrychange", (e) => { out("currententrychange", { navigationType: e.navigationType, currentEntryUrl: navigation.currentEntry?.url, }); }); navigation.addEventListener("navigatesuccess", () => { out("navigatesuccess", { locationHref: location.href, currentEntryUrl: navigation.currentEntry?.url, currentEntryId: navigation.currentEntry?.id, }); }); </script> </body> </html> ``` Click "click me". The browser attempts a same-origin cross-document navigation to `./next`; the script intercepts it via `event.intercept({ handler })`. ## Expected (matches non-private Safari behavior and Chrome) ``` navigate beforeCurrentUrl: ".../index.html" currententrychange navigationType:"push", currentEntryUrl:".../next" handler currentEntryUrl:".../next", currentEntryUnchanged:false navigatesuccess locationHref:".../next", currentEntryUrl:".../next" ``` ## Actual (Safari 26.4 Private Browsing) ``` navigate beforeCurrentUrl: ".../index.html" handler locationHref:".../next", currentEntryUrl:".../index.html", currentEntryUnchanged:true navigatesuccess locationHref:".../next", currentEntryUrl:".../index.html" (STALE) ``` `currententrychange` never fires. `navigation.currentEntry` continues to return the previous entry — both `.url` and `.id` are stale — for the rest of the document's lifetime, until a reload occurs. ## Environment - Safari 26.3.1 on macOS Tahoe 26.3.1 - Safari 26.4 on iOS 26.4.2 - Reproduces in macOS Safari Private Browsing windows - Reproduces in iOS Safari Private tabs - Does NOT reproduce in macOS / iOS Safari normal windows - Does NOT reproduce in Chrome (any mode including Incognito) ## Related Bugs -
Bug 298466
— `[Navigation API] Push operation does not always create a new history item`. Appears related because it also concerns Navigation API push/entry creation for intercepted navigations. This report is narrower: it reproduces only in Private Browsing, uses a different destination URL, and additionally observes that `location.href` updates while `navigation.currentEntry` and `currententrychange` remain stale/missing. ## Impact - The user-visible symptom is "URL bar changes but page content stays the old one until reload" in Private Browsing for applications that use the Navigation API for client-side routing. - Any application following the documented Navigation API SPA pattern (read `navigation.currentEntry` on `currententrychange` to update the view) is affected. ## Workaround for Web Developers Derive the current URL from `location.href` in Private Browsing; do not rely on `navigation.currentEntry.url` or the `currententrychange` event for this case. (Subscribing to `navigatesuccess` does not help on its own because `navigation.currentEntry` is still stale at that point.)
Attachments
Add attachment
proposed patch, testcase, etc.
Note
You need to
log in
before you can comment on or make changes to this bug.
Top of Page
Format For Printing
XML
Clone This Bug