From 2ae54d77e0f3f2ed2113b4dd4e4b48effde8a0eb Mon Sep 17 00:00:00 2001 From: Shannon Booth Date: Sun, 3 May 2026 13:36:47 +0200 Subject: [PATCH] LibWeb: Reuse initial about:blank window for first same-origin load Follow the spec reuse of the initial same-origin about:blank window for a browsing context's first real navigation. This fixes a crash in promise-job-entry-different-function-realm.html by preserving the correct iframe realm. However, reusing the initial about:blank Window means create-and-initialize can associate that Window with the pending Document before session history activation has made the Document active. Treating a browsing context's active document as its active Window's associated Document therefore exposes the pending Document too early. To fix this, add an explicit active document slot to BrowsingContext and update it when a Document is made active. A similar fix was attempted in 7fc7263a4d5e5ee67dc5db26507651bdf1454c4d, but that version still queued navigation tasks through the reused active Window, tagging them with the pending Document before it was active. This caused parser-created iframe loads to hang in encoding WPTs. This commit instead queues those navigation-internal tasks against the navigable's currently active Document. The newly added iframe-initial-load-chunked-body.html mimics the same type of failure seen by the WPT regressions mentioned above. --- Libraries/LibWeb/DOM/Document.cpp | 16 +-- Libraries/LibWeb/HTML/BrowsingContext.cpp | 24 ++-- Libraries/LibWeb/HTML/BrowsingContext.h | 3 + .../LibWeb/HTML/TraversableNavigable.cpp | 12 +- Tests/LibWeb/TestConfig.ini | 2 + .../iframe-initial-load-chunked-body.txt | 1 + ...ise-job-entry-different-function-realm.txt | 10 ++ .../iframe-initial-load-chunked-body.html | 34 ++++++ ...se-job-entry-different-function-realm.html | 112 ++++++++++++++++++ .../resources/current/current.html | 3 + .../current/resources/window-to-open.html | 3 + .../resources/function/function.html | 3 + .../function/resources/window-to-open.html | 3 + .../promise-job-entry-incumbent.html | 15 +++ .../resources/relevant/relevant.html | 14 +++ .../relevant/resources/window-to-open.html | 3 + .../resources/window-to-open.html | 3 + 17 files changed, 241 insertions(+), 20 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/navigation/iframe-initial-load-chunked-body.txt create mode 100644 Tests/LibWeb/Text/expected/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry-different-function-realm.txt create mode 100644 Tests/LibWeb/Text/input/navigation/iframe-initial-load-chunked-body.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry-different-function-realm.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/current.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/resources/window-to-open.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/function.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/resources/window-to-open.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-entry-incumbent.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/relevant.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/resources/window-to-open.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/window-to-open.html diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 0b7482f51b..939cad4ec7 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -344,15 +344,11 @@ WebIDL::ExceptionOr> Document::create_and_initialize(Type type // 6. If browsingContext's active document's is initial about:blank is true, // and browsingContext's active document's origin is same origin-domain with navigationParams's origin, // then set window to browsingContext's active window. - // FIXME: still_on_its_initial_about_blank_document() is not in the spec anymore. - // However, replacing this with the spec-mandated is_initial_about_blank() results in the browsing context - // holding an incorrect active document for the replace from initial about:blank to the real document. - // See #22293 for more details. - if (false - && (browsing_context->active_document() && browsing_context->active_document()->origin().is_same_origin(navigation_params.origin))) { + VERIFY(browsing_context->active_document()); + if (browsing_context->active_document()->is_initial_about_blank() + && browsing_context->active_document()->origin().is_same_origin_domain(navigation_params.origin)) { window = browsing_context->active_window(); } - // 7. Otherwise: else { // FIXME: 1. Let oacHeader be the result of getting a structured field value given `Origin-Agent-Cluster` and "item" from response's header list. @@ -5631,6 +5627,12 @@ void Document::make_active() set_window(window); + // AD-HOC: Keep the browsing context's active document distinct from the Window's associated Document. + // The associated Document can be updated during document creation, but the browsing context + // should only expose the new Document once it is made active. + // Spec issue: https://github.com/whatwg/html/issues/12415 + m_browsing_context->set_active_document(*this); + // 2. Set document's browsing context's WindowProxy's [[Window]] internal slot value to window. m_browsing_context->window_proxy()->set_window(window); diff --git a/Libraries/LibWeb/HTML/BrowsingContext.cpp b/Libraries/LibWeb/HTML/BrowsingContext.cpp index e3eefe4fe1..02ea5ddd06 100644 --- a/Libraries/LibWeb/HTML/BrowsingContext.cpp +++ b/Libraries/LibWeb/HTML/BrowsingContext.cpp @@ -302,6 +302,7 @@ void BrowsingContext::visit_edges(Cell::Visitor& visitor) visitor.visit(m_page); visitor.visit(m_window_proxy); + visitor.visit(m_active_document); visitor.visit(m_group); visitor.visit(m_opener_browsing_context); } @@ -347,21 +348,24 @@ GC::Ptr BrowsingContext::top_level_browsing_context() const // https://html.spec.whatwg.org/multipage/document-sequences.html#active-document DOM::Document const* BrowsingContext::active_document() const { - // A browsing context's active document is its active window's associated Document. - auto* window = active_window(); - if (!window) - return nullptr; - return &window->associated_document(); + // AD-HOC: The HTML Standard currently defines this as the active window's associated Document. + // That changes too early when the initial about:blank Window is reused for its first + // same-origin navigation, because create-and-initialize updates the associated Document + // before the new Document is made active. + // Spec issue: https://github.com/whatwg/html/issues/12415 + return m_active_document; } // https://html.spec.whatwg.org/multipage/document-sequences.html#active-document DOM::Document* BrowsingContext::active_document() { - // A browsing context's active document is its active window's associated Document. - auto* window = active_window(); - if (!window) - return nullptr; - return &window->associated_document(); + // AD-HOC: See the const overload above. + return m_active_document; +} + +void BrowsingContext::set_active_document(GC::Ptr document) +{ + m_active_document = document; } // https://html.spec.whatwg.org/multipage/browsers.html#active-window diff --git a/Libraries/LibWeb/HTML/BrowsingContext.h b/Libraries/LibWeb/HTML/BrowsingContext.h index 390f9b3447..5d39126272 100644 --- a/Libraries/LibWeb/HTML/BrowsingContext.h +++ b/Libraries/LibWeb/HTML/BrowsingContext.h @@ -42,6 +42,7 @@ public: DOM::Document const* active_document() const; DOM::Document* active_document(); + void set_active_document(GC::Ptr); HTML::WindowProxy* window_proxy(); HTML::WindowProxy const* window_proxy() const; @@ -93,6 +94,8 @@ private: // https://html.spec.whatwg.org/multipage/document-sequences.html#browsing-context GC::Ptr m_window_proxy; + GC::Ptr m_active_document; + // https://html.spec.whatwg.org/multipage/browsers.html#opener-browsing-context GC::Ptr m_opener_browsing_context; diff --git a/Libraries/LibWeb/HTML/TraversableNavigable.cpp b/Libraries/LibWeb/HTML/TraversableNavigable.cpp index 5f8d145894..41ee39b607 100644 --- a/Libraries/LibWeb/HTML/TraversableNavigable.cpp +++ b/Libraries/LibWeb/HTML/TraversableNavigable.cpp @@ -657,7 +657,10 @@ void ApplyHistoryStepState::start() signal_progress(); continue; } - queue_global_task(Task::Source::NavigationAndTraversal, *navigable->active_window(), GC::create_function(heap(), [this, navigable] { + // AD-HOC: Queue with navigable's active Document instead of using queue_global_task(active_window). + // During initial about:blank Window reuse, active_window()->associated_document() can already be + // the pending Document. This activation task must stay runnable against the current active Document. + queue_a_task(Task::Source::NavigationAndTraversal, nullptr, navigable->active_document(), GC::create_function(heap(), [this, navigable] { // NOTE: This check is not in the spec but we should not continue navigation if navigable has been destroyed. if (navigable->has_been_destroyed()) { ++m_completed_change_jobs; @@ -827,9 +830,12 @@ void ApplyHistoryStepState::start() *potentially_target_specific_source_snapshot_params, target_snapshot_params, user_involvement, {}, Navigable::NullOrError {}, ContentSecurityPolicy::Directives::Directive::NavigationType::Other, allow_POST, - GC::create_function(this->heap(), [this, after_document_populated](GC::Ptr output) { + GC::create_function(this->heap(), [this, after_document_populated, navigable](GC::Ptr output) { VERIFY(m_traversable->active_window()); - queue_global_task(Task::Source::NavigationAndTraversal, *m_traversable->active_window(), GC::create_function(heap(), [after_document_populated, output]() { + // AD-HOC: Queue with navigable's active Document instead of using queue_global_task(active_window). + // During initial about:blank Window reuse, active_window()->associated_document() can already be + // the pending Document. This continuation must stay runnable against the current active Document. + queue_a_task(Task::Source::NavigationAndTraversal, nullptr, navigable->active_document(), GC::create_function(heap(), [after_document_populated, output]() { after_document_populated->function()(output); })); })); diff --git a/Tests/LibWeb/TestConfig.ini b/Tests/LibWeb/TestConfig.ini index e6c1065ebb..aad27eed7e 100644 --- a/Tests/LibWeb/TestConfig.ini +++ b/Tests/LibWeb/TestConfig.ini @@ -189,6 +189,7 @@ Text/input/XHR/XMLHttpRequest-network-error-message.html Text/input/XHR/XMLHttpRequest-override-mimetype-blob.html Text/input/XML/error-page.html Text/input/base/a-element-target.html +Text/input/navigation/iframe-initial-load-chunked-body.html Text/input/navigation/iframe-navigate-javascript-url.html Text/input/navigation/iframe-referrer-policy.html Text/input/parse-document-from-string-in-fetch-callback.html @@ -230,6 +231,7 @@ Text/input/wpt-import/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml Text/input/wpt-import/html/the-xhtml-syntax/parsing-xhtml-documents/xhtml-mathml-dtd-entity-9.htm Text/input/wpt-import/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window.html Text/input/wpt-import/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/url-entry-document-sync-call.window.html +Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry-different-function-realm.html Text/input/wpt-import/navigation-api/navigation-methods/reload-state-and-info.html Text/input/wpt-import/navigation-api/navigation-methods/reload-state-undefined.html Text/input/wpt-import/page-visibility/test_child_document.html diff --git a/Tests/LibWeb/Text/expected/navigation/iframe-initial-load-chunked-body.txt b/Tests/LibWeb/Text/expected/navigation/iframe-initial-load-chunked-body.txt new file mode 100644 index 0000000000..1d9f092306 --- /dev/null +++ b/Tests/LibWeb/Text/expected/navigation/iframe-initial-load-chunked-body.txt @@ -0,0 +1 @@ +chunked iframe loaded diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry-different-function-realm.txt b/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry-different-function-realm.txt new file mode 100644 index 0000000000..9effb466ea --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry-different-function-realm.txt @@ -0,0 +1,10 @@ +Harness status: OK + +Found 5 tests + +5 Pass +Pass Fulfillment handler on fulfilled promise +Pass Rejection handler on rejected promise +Pass Fulfillment handler on pending-then-fulfilled promise +Pass Rejection handler on pending-then-rejected promise +Pass Thenable resolution \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/navigation/iframe-initial-load-chunked-body.html b/Tests/LibWeb/Text/input/navigation/iframe-initial-load-chunked-body.html new file mode 100644 index 0000000000..b90b2575c2 --- /dev/null +++ b/Tests/LibWeb/Text/input/navigation/iframe-initial-load-chunked-body.html @@ -0,0 +1,34 @@ + + + + + diff --git a/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry-different-function-realm.html b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry-different-function-realm.html new file mode 100644 index 0000000000..218a2a047a --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/promise-job-entry-different-function-realm.html @@ -0,0 +1,112 @@ + + +Entry settings object for promise jobs when the function realm is different from the test realm + + + + + + + + + + + diff --git a/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/current.html b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/current.html new file mode 100644 index 0000000000..2c690a79d6 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/current.html @@ -0,0 +1,3 @@ + + +Current page used as a test helper diff --git a/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/resources/window-to-open.html b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/resources/window-to-open.html new file mode 100644 index 0000000000..1bc4cca9a3 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/current/resources/window-to-open.html @@ -0,0 +1,3 @@ + + +If the current settings object is used this page will be opened diff --git a/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/function.html b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/function.html new file mode 100644 index 0000000000..15841d387d --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/function.html @@ -0,0 +1,3 @@ + + +Realm for a "then" function used as a test helper diff --git a/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/resources/window-to-open.html b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/resources/window-to-open.html new file mode 100644 index 0000000000..3928c1f8aa --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/function/resources/window-to-open.html @@ -0,0 +1,3 @@ + + +If the function's settings object is used this page will be opened diff --git a/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-entry-incumbent.html b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-entry-incumbent.html new file mode 100644 index 0000000000..3740c1467d --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/promise-job-entry-incumbent.html @@ -0,0 +1,15 @@ + + +Incumbent page used as a test helper + + + + + diff --git a/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/relevant.html b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/relevant.html new file mode 100644 index 0000000000..f5965f2231 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/relevant.html @@ -0,0 +1,14 @@ + + +Relevant page used as a test helper + + diff --git a/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/resources/window-to-open.html b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/resources/window-to-open.html new file mode 100644 index 0000000000..4138b5a084 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/relevant/resources/window-to-open.html @@ -0,0 +1,3 @@ + + +If the relevant settings object is used this page will be opened diff --git a/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/window-to-open.html b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/window-to-open.html new file mode 100644 index 0000000000..ce357937f5 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/scripting/processing-model-2/integration-with-the-javascript-job-queue/resources/window-to-open.html @@ -0,0 +1,3 @@ + + +If the entry settings object is used this page will be opened