diff --git a/Libraries/LibWeb/HTML/Navigable.cpp b/Libraries/LibWeb/HTML/Navigable.cpp
index faa3d04842..7efafd7491 100644
--- a/Libraries/LibWeb/HTML/Navigable.cpp
+++ b/Libraries/LibWeb/HTML/Navigable.cpp
@@ -2014,12 +2014,12 @@ void Navigable::begin_navigation(NavigateParams params)
if (!active_window())
return;
- auto const& url = params.url;
+ auto url = params.url;
auto source_document = params.source_document;
- auto const& document_resource = params.document_resource;
+ auto document_resource = params.document_resource;
auto response = params.response;
auto history_handling = params.history_handling;
- auto const& navigation_api_state = params.navigation_api_state;
+ auto navigation_api_state = params.navigation_api_state;
auto referrer_policy = params.referrer_policy;
auto user_involvement = params.user_involvement;
auto source_element = params.source_element;
@@ -2133,6 +2133,7 @@ void Navigable::begin_navigation(NavigateParams params)
// AD-HOC: The HTML Standard cancels a navigation that starts while a traversal is ongoing. We defer it
// instead so UI-initiated navigations that race the tail end of a previous load are not dropped.
// Match Chromium, WebKit, and Gecko's observable behavior by letting the newest navigation win.
+ // See https://github.com/whatwg/html/issues/12581.
queue_pending_navigation(move(params), PendingNavigationBehavior::Replace);
// 2. Return.
@@ -2201,7 +2202,7 @@ void Navigable::begin_navigation(NavigateParams params)
// FIXME: 22. If sourceDocument is navigable's container document, then reserve deferred fetch quota for navigable's container given url's origin.
// 23. In parallel, run these steps:
- Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(heap(), [this, source_snapshot_params, target_snapshot_params, csp_navigation_type, document_resource, url, navigation_id, referrer_policy, initiator_origin_snapshot, response, history_handling, initiator_base_url_snapshot, user_involvement] {
+ Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(heap(), [this, source_snapshot_params, target_snapshot_params, csp_navigation_type, document_resource, url, navigation_id, referrer_policy, initiator_origin_snapshot, response, history_handling, initiator_base_url_snapshot, user_involvement, params = move(params)] mutable {
// AD-HOC: Not in the spec but subsequent steps will fail if the navigable doesn't have an active window.
if (!active_window()) {
set_delaying_load_events(false);
@@ -2210,7 +2211,7 @@ void Navigable::begin_navigation(NavigateParams params)
// 1. Let unloadPromptCanceled be the result of checking if unloading is user-canceled for navigable's active document's inclusive descendant navigables.
traversable_navigable()->check_if_unloading_is_canceled(this->active_document()->inclusive_descendant_navigables(),
- GC::create_function(heap(), [this, source_snapshot_params, target_snapshot_params, csp_navigation_type, document_resource, url, navigation_id, referrer_policy, initiator_origin_snapshot, response, history_handling, initiator_base_url_snapshot, user_involvement](TraversableNavigable::CheckIfUnloadingIsCanceledResult unload_prompt_canceled) {
+ GC::create_function(heap(), [this, source_snapshot_params, target_snapshot_params, csp_navigation_type, document_resource, url, navigation_id, referrer_policy, initiator_origin_snapshot, response, history_handling, initiator_base_url_snapshot, user_involvement, params = move(params)](TraversableNavigable::CheckIfUnloadingIsCanceledResult unload_prompt_canceled) mutable {
// 2. If unloadPromptCanceled is not "continue", or navigable's ongoing navigation is no longer navigationId:
if (unload_prompt_canceled != TraversableNavigable::CheckIfUnloadingIsCanceledResult::Continue) {
// FIXME: 1. Invoke WebDriver BiDi navigation failed with navigable and a new WebDriver BiDi navigation status whose id is navigationId, status is "canceled", and url is url.
@@ -2234,6 +2235,13 @@ void Navigable::begin_navigation(NavigateParams params)
}
if (ongoing_navigation() != navigation_id) {
+ // AD-HOC: If an ongoing traversal re-stamped our navigation ID while we were checking whether
+ // unloading was canceled, this navigation wasn't actually superseded by a newer navigation.
+ // Re-defer it (mirroring 66c54b129514), so it runs once the traversal completes — rather
+ // than silently dropping it and leaving the navigable stuck with no load event ever firing.
+ // See https://github.com/whatwg/html/issues/12581.
+ if (ongoing_navigation().has())
+ queue_pending_navigation(move(params), PendingNavigationBehavior::Append);
set_delaying_load_events(false);
return;
}
diff --git a/Tests/LibWeb/Text/expected/navigation/navigation-not-dropped-by-concurrent-traversal.txt b/Tests/LibWeb/Text/expected/navigation/navigation-not-dropped-by-concurrent-traversal.txt
new file mode 100644
index 0000000000..e33bed7c2f
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/navigation/navigation-not-dropped-by-concurrent-traversal.txt
@@ -0,0 +1 @@
+victim-loaded
diff --git a/Tests/LibWeb/Text/input/navigation/navigation-not-dropped-by-concurrent-traversal.html b/Tests/LibWeb/Text/input/navigation/navigation-not-dropped-by-concurrent-traversal.html
new file mode 100644
index 0000000000..e63af28b3e
--- /dev/null
+++ b/Tests/LibWeb/Text/input/navigation/navigation-not-dropped-by-concurrent-traversal.html
@@ -0,0 +1,47 @@
+
+
+