Branch: refs/heads/main
  Home:   https://github.com/WebKit/WebKit
  Commit: c584246121e006a3bf3bbc39e3a9f9a3444bb0f3
      
https://github.com/WebKit/WebKit/commit/c584246121e006a3bf3bbc39e3a9f9a3444bb0f3
  Author: Basuke Suzuki <[email protected]>
  Date:   2026-09-22 (Tue, 22 Sep 2026)

  Changed paths:
    A 
LayoutTests/http/wpt/site-isolation/navigation-api/preventdefault-cancels-cross-site-swap-subframe.sub-expected.txt
    A 
LayoutTests/http/wpt/site-isolation/navigation-api/preventdefault-cancels-cross-site-swap-subframe.sub.html
    A 
LayoutTests/http/wpt/site-isolation/navigation-api/preventdefault-cancels-cross-site-swap.sub-expected.txt
    A 
LayoutTests/http/wpt/site-isolation/navigation-api/preventdefault-cancels-cross-site-swap.sub.html
    A 
LayoutTests/http/wpt/site-isolation/navigation-api/preventdefault-reentrant-navigation.sub-expected.txt
    A 
LayoutTests/http/wpt/site-isolation/navigation-api/preventdefault-reentrant-navigation.sub.html
    A 
LayoutTests/http/wpt/site-isolation/navigation-api/resources/preventdefault-canceller-then-navigate.html
    A 
LayoutTests/http/wpt/site-isolation/navigation-api/resources/preventdefault-canceller.html
    A 
LayoutTests/http/wpt/site-isolation/navigation-api/resources/preventdefault-destination.html
    M LayoutTests/platform/ios-site-isolation/TestExpectations
    M LayoutTests/platform/mac-site-isolation/TestExpectations
    M Source/WebCore/Headers.cmake
    M Source/WebCore/WebCore.xcodeproj/project.pbxproj
    M Source/WebCore/loader/FrameLoader.cpp
    M Source/WebCore/loader/FrameLoader.h
    M Source/WebCore/loader/NavigationAction.h
    A Source/WebCore/loader/PendingNavigateEventIdentifier.h
    M Source/WebKit/Scripts/webkit/messages.py
    M Source/WebKit/Scripts/webkit/tests/MessageArgumentDescriptions.cpp
    M Source/WebKit/Shared/NavigationActionData.h
    M Source/WebKit/Shared/NavigationActionData.serialization.in
    M Source/WebKit/Shared/WTFArgumentCoders.serialization.in
    M Source/WebKit/UIProcess/WebPageProxy.cpp
    M Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp
    M Source/WebKit/WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp
    M Source/WebKit/WebProcess/WebCoreSupport/WebLocalFrameLoaderClient.cpp
    M Source/WebKit/WebProcess/WebPage/WebFrame.cpp
    M Source/WebKit/WebProcess/WebPage/WebFrame.h
    M Source/WebKit/WebProcess/WebPage/WebPage.cpp
    M Source/WebKit/WebProcess/WebPage/WebPage.h
    M Source/WebKit/WebProcess/WebPage/WebPage.messages.in

  Log Message:
  -----------
  [Navigation API] preventDefault() must cancel a cross-site process-swapping 
navigation under Site Isolation
https://bugs.webkit.org/show_bug.cgi?id=318357
rdar://181141493

Reviewed by Alex Christensen.

Under Site Isolation the client approves a cross-site navigation as
NavigationPolicyDecision::LoadWillContinueInAnotherProcess, an approval that 
needs a process swap
rather than a denial. FrameLoader::continueLoadAfterNavigationPolicy computes 
canContinue as
`decision == ContinueLoad`, so it lumps that in with IgnoreLoad and returns 
early, past the point
where the push/replace navigate event is dispatched. The event never fires and 
preventDefault() has
nothing to cancel, for subframes as well as the main frame.

The event cannot simply move earlier: 302468@main deliberately delayed it until 
after the client's
policy decision, and policy-decision-deny-prevents-navigate-event.html pins 
that ordering. It
has to fire between the decision and the swap, so the UI process has to wait 
for the source process
there.

Rather than deferring the swap and having the source process send a separate 
message to resume it,
the wait is one sendWithAsyncReply: when receivedNavigationActionPolicyDecision 
decides to swap it
asks the source process to run the pending navigate event, and only calls
continueNavigationInNewProcess from the reply. A preventDefault() has no 
in-flight load to unwind;
it comes back as PolicyAction::Ignore and takes the same path as any other 
policy-denied
navigation.

The request names the pending navigate event rather than the frame, because 
script can run while
the decision is outstanding and move the frame on to another navigation, whose 
own pending event is
not this decision's to dispatch. NavigationAction mints the identifier when it 
stores the event and
takePendingDispatchNavigateEvent() will not hand the event over without it, so 
that check cannot be
dropped by a later edit. The identifier is absent when there is nothing to 
dispatch, so pages that
do not use the Navigation API need no extra round trip. The navigationID cannot 
serve here, since
the UI process assigns it in the very reply this dispatch gates. The reply says 
whether the event
cancelled the navigation rather than whether to proceed, so that losing it 
leaves the swap going.

Worth recording: only one of the navigate event's dispatch sites is deferred 
past the policy
decision, the lambda in loadURL, so only link activations, iframe.src and 
navigation.navigate()
reach any of this. NavigationScheduler dispatches up front for the location 
changes it schedules
and sets skipNavigateEvent, which makes that lambda a no-op, and reload, 
fragment, POST submission
and traversal all dispatch before the check.

Also worth recording: the identity check above is not reachable from a test. 
The UI process keeps
one navigation policy listener per frame and setUpPolicyListenerProxy() 
resolves the previous one
with ignore(), so a second navigation drops the first check before its decision 
can come back. Only
the two messages crossing reaches it, which a delegate cannot force.

Process selection, the destination process' shutdown preventing scope and, for 
a back/forward
target, takeSuspendedPage() all happen before the answer, so a preventDefault() 
loses that
suspended page rather than returning it to the cache. Moving those after the 
answer needs the
suspended page carried across the reply and is left for a follow-up.

Tests: http/wpt/site-isolation/navigation-api/
       preventdefault-cancels-cross-site-swap.sub.html
       preventdefault-cancels-cross-site-swap-subframe.sub.html
       preventdefault-reentrant-navigation.sub.html

The upstream navigate-anchor-cross-origin.html calls done() synchronously from 
the handler, so it
passes whenever the event merely fires even if the navigation then proceeds. 
The new tests keep the
document alive past the point where the destination could have committed, and 
the subframe variants
drive the cancellation from a same-origin subframe so that a navigation which 
was not cancelled
fails an assertion instead of losing the test.

* 
LayoutTests/http/wpt/site-isolation/navigation-api/preventdefault-cancels-cross-site-swap-subframe.sub-expected.txt:
 Added.
* 
LayoutTests/http/wpt/site-isolation/navigation-api/preventdefault-cancels-cross-site-swap-subframe.sub.html:
 Added.
* 
LayoutTests/http/wpt/site-isolation/navigation-api/preventdefault-cancels-cross-site-swap.sub-expected.txt:
 Added.
* 
LayoutTests/http/wpt/site-isolation/navigation-api/preventdefault-cancels-cross-site-swap.sub.html:
 Added.
* 
LayoutTests/http/wpt/site-isolation/navigation-api/preventdefault-reentrant-navigation.sub-expected.txt:
 Added.
* 
LayoutTests/http/wpt/site-isolation/navigation-api/preventdefault-reentrant-navigation.sub.html:
 Added.
* 
LayoutTests/http/wpt/site-isolation/navigation-api/resources/preventdefault-canceller-then-navigate.html:
 Added.
* 
LayoutTests/http/wpt/site-isolation/navigation-api/resources/preventdefault-canceller.html:
 Added.
* 
LayoutTests/http/wpt/site-isolation/navigation-api/resources/preventdefault-destination.html:
 Added.
* LayoutTests/platform/ios-site-isolation/TestExpectations:
* LayoutTests/platform/mac-site-isolation/TestExpectations:
* Source/WebCore/Headers.cmake:
* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
* Source/WebCore/loader/FrameLoader.cpp:
(WebCore::FrameLoader::dispatchPendingNavigateEventAfterNavigationPolicy):
* Source/WebCore/loader/FrameLoader.h:
* Source/WebCore/loader/NavigationAction.h:
(WebCore::NavigationAction::setPendingDispatchNavigateEvent):
(WebCore::NavigationAction::pendingDispatchNavigateEventIdentifier):
(WebCore::NavigationAction::takePendingDispatchNavigateEvent):
(WebCore::NavigationAction::hasPendingDispatchNavigateEvent): Deleted.
* Source/WebCore/loader/PendingNavigateEventIdentifier.h: Added.
* Source/WebKit/Scripts/webkit/messages.py:
* Source/WebKit/Scripts/webkit/tests/MessageArgumentDescriptions.cpp:
* Source/WebKit/Shared/NavigationActionData.h:
* Source/WebKit/Shared/NavigationActionData.serialization.in:
* Source/WebKit/Shared/WTFArgumentCoders.serialization.in:
* Source/WebKit/UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::receivedNavigationActionPolicyDecision):
* Source/WebKit/WebProcess/WebCoreSupport/WebChromeClient.cpp:
(WebKit::WebChromeClient::createWindow):
* Source/WebKit/WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
(WebKit::WebFrameLoaderClient::navigationActionData):
(WebKit::WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction):
* Source/WebKit/WebProcess/WebCoreSupport/WebLocalFrameLoaderClient.cpp:
(WebKit::WebLocalFrameLoaderClient::didSameDocumentNavigationForFrameViaJS):
(WebKit::WebLocalFrameLoaderClient::dispatchDecidePolicyForNewWindowAction):
* Source/WebKit/WebProcess/WebPage/WebFrame.cpp:
(WebKit::WebFrame::dispatchPendingNavigateEventAfterNavigationPolicy):
* Source/WebKit/WebProcess/WebPage/WebFrame.h:
* Source/WebKit/WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::dispatchPendingNavigateEventForProcessSwap):
* Source/WebKit/WebProcess/WebPage/WebPage.h:
* Source/WebKit/WebProcess/WebPage/WebPage.messages.in:

Canonical link: https://commits.webkit.org/321637@main



To unsubscribe from these emails, change your notification settings at 
https://github.com/WebKit/WebKit/settings/notifications

Reply via email to