LibWeb: Change SessionHistoryTraversalQueue to use Promises

If multiple cross-document navigations are queued on
SessionHistoryTraversalQueue, running the next entry before the current
document load is finished may result in a deadlock. If the new document
has a navigable element of its own, it will append steps to SHTQ and
hang in nested spin_until.
This change uses promises to ensure that the current document loads
before the next entry is executed.

Fixes timeouts in the imported tests.

Co-authored-by: Sam Atkins <sam@ladybird.org>
This commit is contained in:
Prajjwal
2025-07-04 10:53:28 +05:30
committed by Alexander Kalenik
parent eed4dd3745
commit 50a79c6af8
Notes: github-actions[bot] 2025-11-26 11:28:29 +00:00
20 changed files with 781 additions and 82 deletions

View File

@@ -403,8 +403,12 @@ Messages::WebDriverClient::BackResponse WebDriverConnection::back()
// 7. If the previous step completed results in a pageHide event firing, wait until pageShow event fires or
// timer' timeout fired flag to be set, whichever occurs first.
current_top_level_browsing_context()->top_level_traversable()->append_session_history_traversal_steps(GC::create_function(realm.heap(), [this, timer, on_complete]() {
if (timer->is_timed_out())
return;
// NB: Use Core::Promise to signal SessionHistoryTraversalQueue that it can continue to execute next entry.
auto signal_to_continue_session_history_processing = Core::Promise<Empty>::construct();
if (timer->is_timed_out()) {
signal_to_continue_session_history_processing->resolve({});
return signal_to_continue_session_history_processing;
}
if (auto* document = current_top_level_browsing_context()->active_document(); document->page_showing()) {
on_complete->function()();
@@ -416,6 +420,9 @@ Messages::WebDriverClient::BackResponse WebDriverConnection::back()
on_complete->function()();
});
}
signal_to_continue_session_history_processing->resolve({});
return signal_to_continue_session_history_processing;
}));
});
@@ -473,8 +480,12 @@ Messages::WebDriverClient::ForwardResponse WebDriverConnection::forward()
// 7. If the previous step completed results in a pageHide event firing, wait until pageShow event fires or
// timer' timeout fired flag to be set, whichever occurs first.
current_top_level_browsing_context()->top_level_traversable()->append_session_history_traversal_steps(GC::create_function(realm.heap(), [this, timer, on_complete]() {
if (timer->is_timed_out())
return;
// NB: Use Core::Promise to signal SessionHistoryTraversalQueue that it can continue to execute next entry.
auto signal_to_continue_session_history_processing = Core::Promise<Empty>::construct();
if (timer->is_timed_out()) {
signal_to_continue_session_history_processing->resolve({});
return signal_to_continue_session_history_processing;
}
if (auto* document = current_top_level_browsing_context()->active_document(); document->page_showing()) {
on_complete->function()();
@@ -486,6 +497,9 @@ Messages::WebDriverClient::ForwardResponse WebDriverConnection::forward()
on_complete->function()();
});
}
signal_to_continue_session_history_processing->resolve({});
return signal_to_continue_session_history_processing;
}));
});