RESOLVED FIXED325553
[Navigation API] preventDefault() does not cancel a cross-site navigation.navigate() that swaps processes
https://bugs.webkit.org/show_bug.cgi?id=325553
Summary [Navigation API] preventDefault() does not cancel a cross-site navigation.nav...
Basuke Suzuki
Reported 2026-09-28 15:38:45 PDT
Summary A cross-site navigation started with navigation.navigate() still commits after the page calls preventDefault() in its navigate event handler, whenever the navigation is a process swap. The same navigation started by an <a> click is cancelled correctly (fixed by https://bugs.webkit.org/show_bug.cgi?id=318357). Steps to reproduce 1. In a page, handle navigate: navigation.onnavigate = e => e.preventDefault(); 2. Call navigation.navigate(crossSiteURL). 3. Keep the page alive until the cross-site destination could have loaded, then check location.href. Expected The navigate event fires with cancelable = true, and preventDefault() cancels the navigation. The document stays on its original URL. Actual The navigate event fires (cancelable = true, correct destination) and preventDefault() is called, but the destination commits anyway. Results (CMake Release, e9f2cf896959, which includes the 318357 reland): - Site Isolation, subframe: FAIL (the frame becomes cross-origin) - Site Isolation, main frame: FAIL (the page is replaced) - Site Isolation off, main frame (PSON process swap): FAIL. So this is not only a Site Isolation problem. - Site Isolation off, subframe (no process swap): PASS - Control: the same main-frame test driven by an <a> click, Site Isolation off: PASS Reproducers: copies of the 318357 tests in LayoutTests/http/wpt/site-isolation/navigation-api/ (preventdefault-cancels-cross-site-swap*.sub.html), with a.click() replaced by navigation.navigate(url). Likely cause (not yet confirmed with logging) FrameLoader::loadWithDocumentLoader uses PolicyDecisionMode::Synchronous for navigations from the Navigation API. WebPageProxy::decidePolicyForNavigationActionSync does not wait for the client and replies PolicyAction::Use right away ("If the client did not respond synchronously, proceed with the load"). 1. The source WebProcess receives Use, runs the pending navigate event locally, and preventDefault() stops the local load. 2. The UIProcess then decides on a process swap and sends DispatchPendingNavigateEventForProcessSwap. 3. By now FrameLoader::dispatchPendingNavigateEventAfterNavigationPolicy finds no policy DocumentLoader or no pending event, and returns true ("not cancelled"). The swap continues and the destination commits. The 318357 tests only drive the navigation with a.click(), which uses the asynchronous policy path, so they do not catch this.
Attachments
Basuke Suzuki
Comment 1 2026-09-28 15:39:00 PDT
Basuke Suzuki
Comment 2 2026-09-30 13:57:13 PDT
EWS
Comment 3 2026-10-02 22:35:57 PDT
Committed 322591@main (20cb0ee48f97): <https://commits.webkit.org/322591@main> Reviewed commits have been landed. Closing PR #75433 and removing active labels.
Note You need to log in before you can comment on or make changes to this bug.