LibWeb: Replace spin_until in apply_the_history_step with state machine

Replace the blocking spin_processing_tasks_with_source_until calls
in apply_the_history_step_after_unload_check() with an event-driven
ApplyHistoryStepState GC cell that tracks 5 phases, following the
same pattern used by CheckUnloadingCanceledState.

Key changes:
- Introduce ApplyHistoryStepState with phases:
  WaitingForDocumentPopulation, ProcessingContinuations,
  WaitingForChangeJobCompletion, WaitingForNonChangingJobs and Completed
- Add on_complete callbacks to apply_the_push_or_replace_history_step,
  finalize_a_same_document_navigation,
  finalize_a_cross_document_navigation, and
  update_for_navigable_creation_or_destruction
- Remove spin_until from Document::open()
- Use null-document tasks for non-changing navigable updates and
  document unload/destroy to avoid stuck tasks when documents become
  non-fully-active
- Defer completely_finish_loading when document has no navigable yet,
  and re-trigger post-load steps in activate_history_entry for documents
  that completed loading before activation

Co-Authored-By: Shannon Booth <shannon@serenityos.org>
This commit is contained in:
Aliaksandr Kalenik
2026-03-29 12:06:39 +02:00
committed by Alexander Kalenik
parent b36f2361f1
commit 2a69fd4c52
Notes: github-actions[bot] 2026-03-31 07:49:17 +00:00
18 changed files with 613 additions and 438 deletions

View File

@@ -111,29 +111,18 @@ void HTMLIFrameElement::post_connection()
// The iframe HTML element post-connection steps, given insertedNode, are:
// 1. Create a new child navigable for insertedNode.
MUST(create_new_child_navigable(GC::create_function(realm().heap(), [this] {
// 2. If insertedNode has a sandbox attribute, then parse the sandboxing directive given the attribute's
// value and insertedNode's iframe sandboxing flag set.
if (has_attribute(AttributeNames::sandbox)) {
auto sandbox_attribute = attribute(AttributeNames::sandbox);
VERIFY(sandbox_attribute.has_value());
m_iframe_sandboxing_flag_set = parse_a_sandboxing_directive(sandbox_attribute.value());
}
MUST(create_new_child_navigable());
// 3. Process the iframe attributes for insertedNode, with initialInsertion set to true.
process_the_iframe_attributes(InitialInsertion::Yes);
// 2. If insertedNode has a sandbox attribute, then parse the sandboxing directive given the attribute's
// value and insertedNode's iframe sandboxing flag set.
if (has_attribute(AttributeNames::sandbox)) {
auto sandbox_attribute = attribute(AttributeNames::sandbox);
VERIFY(sandbox_attribute.has_value());
m_iframe_sandboxing_flag_set = parse_a_sandboxing_directive(sandbox_attribute.value());
}
if (auto navigable = content_navigable()) {
auto traversable = navigable->traversable_navigable();
traversable->append_session_history_traversal_steps(GC::create_function(heap(), [this] {
// 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();
set_content_navigable_has_session_history_entry_and_ready_for_navigation();
signal_to_continue_session_history_processing->resolve({});
return signal_to_continue_session_history_processing;
}));
}
})));
// 3. Process the iframe attributes for insertedNode, with initialInsertion set to true.
process_the_iframe_attributes(InitialInsertion::Yes);
}
// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#process-the-iframe-attributes