diff --git a/Libraries/LibWeb/HTML/Navigable.cpp b/Libraries/LibWeb/HTML/Navigable.cpp index a407425f14f..a4a9594e8ca 100644 --- a/Libraries/LibWeb/HTML/Navigable.cpp +++ b/Libraries/LibWeb/HTML/Navigable.cpp @@ -2234,4 +2234,18 @@ void Navigable::unregister_navigation_observer(Badge, Naviga VERIFY(was_removed); } +// https://html.spec.whatwg.org/multipage/document-lifecycle.html#nav-stop +void Navigable::stop_loading() +{ + // 1. Let document be navigable's active document. + auto document = active_document(); + + // 2. If document's unload counter is 0, and navigable's ongoing navigation is a navigation ID, then set the ongoing navigation for navigable to null. + if (document->unload_counter() == 0 && ongoing_navigation().has()) + set_ongoing_navigation(Empty {}); + + // 3. Abort a document and its descendants given document. + document->abort_a_document_and_its_descendants(); +} + } diff --git a/Libraries/LibWeb/HTML/Navigable.h b/Libraries/LibWeb/HTML/Navigable.h index 4ee694c4a9a..3d81aa48b52 100644 --- a/Libraries/LibWeb/HTML/Navigable.h +++ b/Libraries/LibWeb/HTML/Navigable.h @@ -74,6 +74,8 @@ public: void set_closing(bool value) { m_closing = value; } bool is_script_closable(); + void stop_loading(); + void set_delaying_load_events(bool value); bool is_delaying_load_events() const { return m_delaying_the_load_event.has_value(); } diff --git a/Libraries/LibWeb/HTML/Window.cpp b/Libraries/LibWeb/HTML/Window.cpp index db340cbe288..4168c3f4625 100644 --- a/Libraries/LibWeb/HTML/Window.cpp +++ b/Libraries/LibWeb/HTML/Window.cpp @@ -856,6 +856,18 @@ GC::Ref Window::history() const return associated_document().history(); } +// https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-window-stop +void Window::stop() +{ + // 1. If this's navigable is null, then return. + auto navigable = this->navigable(); + if (!navigable) + return; + + // 2. Stop loading this's navigable. + navigable->stop_loading(); +} + // https://html.spec.whatwg.org/multipage/interaction.html#dom-window-focus void Window::focus() { diff --git a/Libraries/LibWeb/HTML/Window.h b/Libraries/LibWeb/HTML/Window.h index 2ea6f22ce78..0e21066b602 100644 --- a/Libraries/LibWeb/HTML/Window.h +++ b/Libraries/LibWeb/HTML/Window.h @@ -154,6 +154,7 @@ public: [[nodiscard]] GC::Ref location(); GC::Ref history() const; GC::Ref navigation(); + void stop(); void focus(); void blur(); diff --git a/Libraries/LibWeb/HTML/Window.idl b/Libraries/LibWeb/HTML/Window.idl index d78fbc20a2e..26ba6f977fb 100644 --- a/Libraries/LibWeb/HTML/Window.idl +++ b/Libraries/LibWeb/HTML/Window.idl @@ -30,6 +30,7 @@ interface Window : EventTarget { readonly attribute History history; readonly attribute Navigation navigation; readonly attribute CustomElementRegistry customElements; + undefined stop(); undefined focus(); undefined blur(); diff --git a/Tests/LibWeb/TestConfig.ini b/Tests/LibWeb/TestConfig.ini index 610b69ec9ca..9f756a0a5f5 100644 --- a/Tests/LibWeb/TestConfig.ini +++ b/Tests/LibWeb/TestConfig.ini @@ -47,6 +47,8 @@ Text/input/wpt-import/html/syntax/parsing/support/no-doctype-name-line.html Text/input/wpt-import/html/syntax/parsing/support/no-doctype-name-space.html Text/input/wpt-import/html/semantics/embedded-content/the-iframe-element/support/sandbox_allow_script.html Text/input/wpt-import/html/semantics/embedded-content/the-iframe-element/support/blank.htm +Text/input/wpt-import/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/aborted-parser-frame.html +Text/input/wpt-import/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/aborted-parser-async-frame.html ; Unknown, imported as skipped in #2148 Text/input/wpt-import/html/infrastructure/safe-passing-of-structured-data/structuredclone_0.html diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window.txt b/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window.txt new file mode 100644 index 00000000000..fe57db893f3 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window.txt @@ -0,0 +1,7 @@ +Harness status: OK + +Found 2 tests + +2 Pass +Pass document.open() after parser is aborted +Pass async document.open() after parser is aborted \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/wpt-import/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window.html b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window.html new file mode 100644 index 00000000000..96a547e1a1a --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window.html @@ -0,0 +1,8 @@ + + + + + + +
+ diff --git a/Tests/LibWeb/Text/input/wpt-import/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window.js b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window.js new file mode 100644 index 00000000000..ba7278ef18a --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/aborted-parser.window.js @@ -0,0 +1,31 @@ +// document.open() bails out early if there is an active parser with non-zero +// script nesting level or if a load was aborted while there was an active +// parser. window.stop() aborts the current parser, so once it has been called +// while a parser is active, document.open() will no longer do anything to that +// document, + +window.handlers = {}; + +async_test(t => { + const frame = document.body.appendChild(document.createElement("iframe")); + t.add_cleanup(() => frame.remove()); + frame.src = "resources/aborted-parser-frame.html"; + window.handlers.afterOpen = t.step_func_done(() => { + const openCalled = frame.contentDocument.childNodes.length === 0; + assert_false(openCalled, "child document should not be empty"); + assert_equals(frame.contentDocument.querySelector("p").textContent, + "Text", "Should still have our paragraph"); + }); +}, "document.open() after parser is aborted"); + +async_test(t => { + const frame = document.body.appendChild(document.createElement("iframe")); + t.add_cleanup(() => frame.remove()); + frame.src = "resources/aborted-parser-async-frame.html"; + window.handlers.afterOpenAsync = t.step_func_done(() => { + const openCalled = frame.contentDocument.childNodes.length === 0; + assert_false(openCalled, "child document should not be empty"); + assert_equals(frame.contentDocument.querySelector("p").textContent, + "Text", "Should still have our paragraph"); + }); +}, "async document.open() after parser is aborted"); diff --git a/Tests/LibWeb/Text/input/wpt-import/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/aborted-parser-async-frame.html b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/aborted-parser-async-frame.html new file mode 100644 index 00000000000..d5535630be0 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/aborted-parser-async-frame.html @@ -0,0 +1,9 @@ + +

Text

+ diff --git a/Tests/LibWeb/Text/input/wpt-import/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/aborted-parser-frame.html b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/aborted-parser-frame.html new file mode 100644 index 00000000000..d9ec23590bf --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/webappapis/dynamic-markup-insertion/opening-the-input-stream/resources/aborted-parser-frame.html @@ -0,0 +1,7 @@ + +

Text

+