Branch: refs/heads/main
  Home:   https://github.com/WebKit/WebKit
  Commit: 6ee1aef89dac209e9afdfda943d5585b5ac291e6
      
https://github.com/WebKit/WebKit/commit/6ee1aef89dac209e9afdfda943d5585b5ac291e6
  Author: Rupin Mittal <[email protected]>
  Date:   2026-08-14 (Fri, 14 Aug 2026)

  Changed paths:
    M 
LayoutTests/imported/w3c/web-platform-tests/navigation-api/precommit-handler/precommitHandler-traversal-window-stop-before-commit-expected.txt
    M 
LayoutTests/imported/w3c/web-platform-tests/navigation-api/precommit-handler/precommitHandler-traverse-expected.txt
    M Source/WebCore/loader/FrameLoader.cpp
    M Source/WebCore/loader/FrameLoader.h
    M Source/WebCore/page/Navigation.cpp
    M Source/WebCore/page/Navigation.h

  Log Message:
  -----------
  [Navigation API] precommitHandler-traverse.html is failing
https://bugs.webkit.org/show_bug.cgi?id=321694
rdar://184840625

Reviewed by Chris Dumez.

The test builds five history entries in a single document, then runs three 
subtests.
The first subtest rejectBeforeCommit traverses back four steps to entries[0],
intercepts with a precommit handler, and has that handler reject.

It fails the assertion in the precommit handler which checks that the URL/hash
haven't changed yet (they must only change once the precommit handler is done
and the navigation has committed. Them changing is part of the commit):
FAIL ... "Error: assert_equals: hash after first async step expected "#4" but 
got """

So our issue is that part of the commit is happening too early.

When the navigation happens, FrameLoader::loadItem() is run and it does:

if (sameDocumentNavigation) {
    ...
        if (navigation->dispatchTraversalNavigateEvent(item) == 
Navigation::DispatchResult::Aborted))
            return;
        ...
    }
}
...
if (sameDocumentNavigation) {
    ...
    loadSameDocumentItem(item);     // ← runs for Intercepted too
}

Navigation::dispatchTraversalNavigateEvent() is the part that fires the navigate
event, waits for the precommit handlers to finish running, and then finally 
calls
Navigation::setupInterceptionState(), which is the Navigation API commit that
updates the URL.

FrameLoader::loadSameDocumentItem() is the normal "apply the history step" 
which also
changes the URL. The issue is that when a precommit handler exists,
innerDispatchNavigateEvent() returns "Intercepted" without actually committing.

This is wrong, the spec expects us to defer the history step when the 
navigation is
intercepted and then actually apply the history step as part of committing:

The traverse navigate event is fired from "check if unloading is canceled"
(https://html.spec.whatwg.org/multipage/browsing-the-web.html#checking-if-unloading-is-canceled):

> Let navigateEventResult be the result of firing a traverse navigate event ...
  If navigateEventResult is false, return [a non continue status]

Fire a traverse navigate event returns false whenever the event was intercepted
(https://html.spec.whatwg.org/multipage/nav-history-apis.html#fire-a-traverse-navigate-event)

Apply the history step then bails on that result
(https://html.spec.whatwg.org/multipage/browsing-the-web.html#apply-the-history-step)

> The result of checking if unloading is canceled is not continue, then early 
> return.

Commit a navigate event then picks it back up
(https://html.spec.whatwg.org/multipage/nav-history-apis.html#commit-a-navigate-event)

> Resume applying the traverse history step.

So we fix this by implementing this deferred commit. When the navigate event is
firing, if it is intercepted and has precommit handlers, 
innerDispatchNavigateEvent()
will return "DeferredCommit" instead of "Intercepted", which lets 
FrameLoader::loadItem()
know that it should stash this item so the history step can be resumed later 
when we
commit after the precommit handlers are done running.

After we make this change, the subtest progresses but still fails:

FAIL ... reject before commit  assert_false: navigatesuccess fired expected 
false got true

When tracing the rejectBeforeCommit, we see that both promises are indeed 
rejected
and a navigate success is not actually coming from it. It's coming from the 
initial setup:

There are 4 history.pushState() calls back to back, each one cancels the 
navigate event
of the one before it, except the last one, whose navigate success event is not 
cancelled
since there is no subsequent pushState().

The issue is that this navigate success arrives a whole task late.
Navigation::handleSameDocumentNavigation() special-cased exactly pushState's 
shape —
not intercepted, no API method tracker — and scheduled it with
queueTask(TaskSource::DOMManipulation, ...) instead of a microtask.

For a same document navigation (like pushState), the spec says to run the 
intercept
commit handler steps, which fire the navigate success from wait-for-all whcih 
uses
a microtask:
(https://html.spec.whatwg.org/multipage/nav-history-apis.html#update-the-navigation-api-entries-for-a-same-document-navigation).

This special case was initially added by 310748@main which had concerns about 
creating
a wrapper DeferredPromise which looks to have been resolved by 317203@main. So 
we
fix this by removing the special case that uses a task, so that a microtask is 
used
as per spec. The tests added in those two commits still pass after this change.

This makes the rejectBeforeCommit test pass, as well as other tests.

* 
LayoutTests/imported/w3c/web-platform-tests/navigation-api/precommit-handler/precommitHandler-traversal-window-stop-before-commit-expected.txt:
* 
LayoutTests/imported/w3c/web-platform-tests/navigation-api/precommit-handler/precommitHandler-traverse-expected.txt:
* Source/WebCore/loader/FrameLoader.cpp:
(WebCore::FrameLoader::clearDeferredTraversal):
(WebCore::FrameLoader::resumeDeferredTraversal):
(WebCore::FrameLoader::loadItem):
* Source/WebCore/loader/FrameLoader.h:
* Source/WebCore/page/Navigation.cpp:
(WebCore::Navigation::clearDeferredTraversalIfNeeded):
(WebCore::Navigation::resumeDeferredTraversalIfNeeded):
(WebCore::Navigation::abortOngoingNavigation):
(WebCore::Navigation::setupInterceptionState):
(WebCore::Navigation::handleSameDocumentNavigation):
(WebCore::Navigation::runNavigatePrecommitHandlers):
(WebCore::Navigation::innerDispatchNavigateEvent):
* Source/WebCore/page/Navigation.h:

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



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

Reply via email to