From ff78746be1fbc3566f5afdc1d9ecf4b58d503986 Mon Sep 17 00:00:00 2001 From: mikiubo Date: Wed, 4 Jun 2025 09:44:03 +0200 Subject: [PATCH] LibWeb: Set readyState to complete for DOMParser documents Documents created via DOMParser.parseFromString() are parsed synchronously and do not participate in the browsing context's loading pipeline. This patch ensures that if the document has no browsing context (i.e. was parsed via DOMParser), its readiness is set to "complete" synchronously. Fixes WPT: domparsing/xmldomparser.html --- Libraries/LibWeb/HTML/Parser/HTMLParser.cpp | 9 +++++++++ Libraries/LibWeb/XML/XMLDocumentBuilder.cpp | 6 ++++++ .../document-readyState-is-initially-complete.txt | 2 +- .../expected/wpt-import/domparsing/xmldomparser.txt | 6 ++++++ .../document-readyState-is-initially-complete.html | 2 +- .../input/wpt-import/domparsing/xmldomparser.html | 13 +++++++++++++ 6 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/wpt-import/domparsing/xmldomparser.txt create mode 100644 Tests/LibWeb/Text/input/wpt-import/domparsing/xmldomparser.html diff --git a/Libraries/LibWeb/HTML/Parser/HTMLParser.cpp b/Libraries/LibWeb/HTML/Parser/HTMLParser.cpp index 68382208de0..51c8d22fed6 100644 --- a/Libraries/LibWeb/HTML/Parser/HTMLParser.cpp +++ b/Libraries/LibWeb/HTML/Parser/HTMLParser.cpp @@ -324,6 +324,15 @@ void HTMLParser::the_end(GC::Ref document, GC::Ptr pa (void)parser->m_stack_of_open_elements.pop(); } + // AD-HOC: Skip remaining steps when there's no browsing context. + // This happens when parsing HTML via DOMParser or similar mechanisms. + // Note: This diverges from the spec, which expects more steps to follow. + if (!document->browsing_context()) { + // Parsed via DOMParser, no need to wait for load events. + document->update_readiness(HTML::DocumentReadyState::Complete); + return; + } + // 5. While the list of scripts that will execute when the document has finished parsing is not empty: while (!document->scripts_to_execute_when_parsing_has_finished().is_empty()) { // 1. Spin the event loop until the first script in the list of scripts that will execute when the document has finished parsing diff --git a/Libraries/LibWeb/XML/XMLDocumentBuilder.cpp b/Libraries/LibWeb/XML/XMLDocumentBuilder.cpp index d0e5b21e4d8..f414c00b4cf 100644 --- a/Libraries/LibWeb/XML/XMLDocumentBuilder.cpp +++ b/Libraries/LibWeb/XML/XMLDocumentBuilder.cpp @@ -275,6 +275,12 @@ void XMLDocumentBuilder::document_end() // Pop all the nodes off the stack of open elements. // NOTE: Noop. + if (!m_document->browsing_context()) { + // Parsed via DOMParser, no need to wait for load events. + m_document->update_readiness(HTML::DocumentReadyState::Complete); + return; + } + // While the list of scripts that will execute when the document has finished parsing is not empty: while (!m_document->scripts_to_execute_when_parsing_has_finished().is_empty()) { // Spin the event loop until the first script in the list of scripts that will execute when the document has finished parsing has its "ready to be parser-executed" flag set diff --git a/Tests/LibWeb/Text/expected/HTML/document-readyState-is-initially-complete.txt b/Tests/LibWeb/Text/expected/HTML/document-readyState-is-initially-complete.txt index 1475ac05792..67be37ce1ae 100644 --- a/Tests/LibWeb/Text/expected/HTML/document-readyState-is-initially-complete.txt +++ b/Tests/LibWeb/Text/expected/HTML/document-readyState-is-initially-complete.txt @@ -1,5 +1,5 @@ readyState of 'new Document()' should be 'complete': 'complete' readyState of 'document.implementation.createHTMLDocument()' should be 'complete': 'complete' readyState of 'document.implementation.createDocument()' should be 'complete': 'complete' -FIXME: readyState of 'new DOMParser().parseFromString('', 'text/html')' should be 'complete': 'interactive' +readyState of 'new DOMParser().parseFromString('', 'text/html')' should be 'complete': 'complete' readyState of 'iframe.contentDocument' of initial about:blank iframe should be 'complete': 'complete' diff --git a/Tests/LibWeb/Text/expected/wpt-import/domparsing/xmldomparser.txt b/Tests/LibWeb/Text/expected/wpt-import/domparsing/xmldomparser.txt new file mode 100644 index 00000000000..70fc1775e70 --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/domparsing/xmldomparser.txt @@ -0,0 +1,6 @@ +Harness status: OK + +Found 1 tests + +1 Pass +Pass XML Dom Parse readyState Test \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/HTML/document-readyState-is-initially-complete.html b/Tests/LibWeb/Text/input/HTML/document-readyState-is-initially-complete.html index 3f5b996338a..1619bc567ae 100644 --- a/Tests/LibWeb/Text/input/HTML/document-readyState-is-initially-complete.html +++ b/Tests/LibWeb/Text/input/HTML/document-readyState-is-initially-complete.html @@ -5,7 +5,7 @@ println(`readyState of 'new Document()' should be 'complete': '${new Document().readyState}'`); println(`readyState of 'document.implementation.createHTMLDocument()' should be 'complete': '${document.implementation.createHTMLDocument().readyState}'`); println(`readyState of 'document.implementation.createDocument()' should be 'complete': '${document.implementation.createDocument('http://www.w3.org/1999/xhtml', '').readyState}'`); - println(`FIXME: readyState of 'new DOMParser().parseFromString('', 'text/html')' should be 'complete': '${new DOMParser().parseFromString('', 'text/html').readyState}'`); + println(`readyState of 'new DOMParser().parseFromString('', 'text/html')' should be 'complete': '${new DOMParser().parseFromString('', 'text/html').readyState}'`); const iframe = document.createElement("iframe"); document.body.appendChild(iframe); diff --git a/Tests/LibWeb/Text/input/wpt-import/domparsing/xmldomparser.html b/Tests/LibWeb/Text/input/wpt-import/domparsing/xmldomparser.html new file mode 100644 index 00000000000..aac4c9c97cc --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/domparsing/xmldomparser.html @@ -0,0 +1,13 @@ + + +XML Dom Parse readyState Test + + +