NEW314976
[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
Note You need to log in before you can comment on or make changes to this bug.