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