From d595369ae49f46f712c3a3395426b7dad4c15df1 Mon Sep 17 00:00:00 2001 From: Shannon Booth Date: Fri, 15 May 2026 16:42:19 +0200 Subject: [PATCH] LibWeb: Let document.write() reenter parser from parser-blocking scripts When resuming after an async wait for a pending parser-blocking script, clear the parser pause flag before executing the script. The spec has unblocked the tokenizer by this point, and document.write() calls from the script must be able to synchronously process inserted markup up to the insertion point. This fixes ordering for document.write()'d inserted scripts during external parser-blocking script execution. --- Libraries/LibWeb/HTML/Parser/HTMLParser.cpp | 13 ++++++++ .../execution-timing/009.txt | 6 ++++ .../execution-timing/012.txt | 6 ++++ .../execution-timing/041.txt | 6 ++++ .../document-write/iframe_005.txt | 4 +-- .../document-write/script_011.txt | 4 +-- .../execution-timing/009.html | 29 ++++++++++++++++++ .../execution-timing/012.html | 30 +++++++++++++++++++ .../execution-timing/041.html | 29 ++++++++++++++++++ .../execution-timing/scripts/include-1.js | 1 + .../execution-timing/scripts/include-2.js | 1 + .../execution-timing/scripts/include-3.js | 3 ++ .../execution-timing/scripts/include-4.js | 3 ++ 13 files changed, 131 insertions(+), 4 deletions(-) create mode 100644 Tests/LibWeb/Text/expected/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/009.txt create mode 100644 Tests/LibWeb/Text/expected/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/012.txt create mode 100644 Tests/LibWeb/Text/expected/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/041.txt create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/009.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/012.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/041.html create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-1.js create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-2.js create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-3.js create mode 100644 Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-4.js diff --git a/Libraries/LibWeb/HTML/Parser/HTMLParser.cpp b/Libraries/LibWeb/HTML/Parser/HTMLParser.cpp index 6196c5aa19..497cb38c53 100644 --- a/Libraries/LibWeb/HTML/Parser/HTMLParser.cpp +++ b/Libraries/LibWeb/HTML/Parser/HTMLParser.cpp @@ -3509,6 +3509,11 @@ void HTMLParser::resume_after_parser_blocking_script() VERIFY(script_nesting_level() == 0); increment_script_nesting_level(); + // Step 8 unblocked the tokenizer above. Our async "spin the event loop" implementation uses the parser pause flag + // to yield while waiting for the pending script, so clear it before executing the script. This allows + // document.write() calls made by the script to synchronously re-enter the parser up to the insertion point. + m_parser_pause_flag = false; + // 1. Let the script be the pending parsing-blocking script. // 2. Set the pending parsing-blocking script to null. // 11. Execute the script element the script. @@ -3528,6 +3533,14 @@ void HTMLParser::resume_after_parser_blocking_script() // 13. Let the insertion point be undefined again. m_tokenizer.undefine_insertion_point(); + // The spec's loop would handle the next pending parsing-blocking script before continuing normal tokenization. + // In this async implementation, pause again and resume when that next script is ready. + if (document().has_pending_parsing_blocking_script()) { + m_parser_pause_flag = true; + schedule_resume_check(); + return; + } + // The spec's "While the pending parsing-blocking script is not null" iteration is realized by run() pausing again // on the next end tag if the executed script set up a new pending blocking script (e.g. via // document.write). diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/009.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/009.txt new file mode 100644 index 0000000000..f4a590fe4b --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/009.txt @@ -0,0 +1,6 @@ +Harness status: OK + +Found 1 tests + +1 Pass +Pass scheduler: document.write external - multiple with doc.write diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/012.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/012.txt new file mode 100644 index 0000000000..ed379308cb --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/012.txt @@ -0,0 +1,6 @@ +Harness status: OK + +Found 1 tests + +1 Pass +Pass scheduler: document.write external and onload events \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/041.txt b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/041.txt new file mode 100644 index 0000000000..d956c0b83b --- /dev/null +++ b/Tests/LibWeb/Text/expected/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/041.txt @@ -0,0 +1,6 @@ +Harness status: OK + +Found 1 tests + +1 Pass +Pass scheduler: document.write scripts that write scripts \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/dynamic-markup-insertion/document-write/iframe_005.txt b/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/dynamic-markup-insertion/document-write/iframe_005.txt index 5c0d65ca6c..a51164f5e5 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/dynamic-markup-insertion/document-write/iframe_005.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/dynamic-markup-insertion/document-write/iframe_005.txt @@ -2,5 +2,5 @@ Harness status: OK Found 1 tests -1 Fail -Fail document.write external script into iframe write back into parent \ No newline at end of file +1 Pass +Pass document.write external script into iframe write back into parent \ No newline at end of file diff --git a/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/dynamic-markup-insertion/document-write/script_011.txt b/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/dynamic-markup-insertion/document-write/script_011.txt index 4b510d645a..1fb367dff3 100644 --- a/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/dynamic-markup-insertion/document-write/script_011.txt +++ b/Tests/LibWeb/Text/expected/wpt-import/html/webappapis/dynamic-markup-insertion/document-write/script_011.txt @@ -2,5 +2,5 @@ Harness status: OK Found 1 tests -1 Fail -Fail document.write external script that document.writes external script \ No newline at end of file +1 Pass +Pass document.write external script that document.writes external script \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/009.html b/Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/009.html new file mode 100644 index 0000000000..cbd1ae0ac3 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/009.html @@ -0,0 +1,29 @@ + + + scheduler: document.write external - multiple with doc.write + + + + + + +
FAILED (This TC requires JavaScript enabled)
+ + + + + + diff --git a/Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/012.html b/Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/012.html new file mode 100644 index 0000000000..2db784f09b --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/012.html @@ -0,0 +1,30 @@ + + + scheduler: document.write external and onload events + + + + + + +
FAILED (This TC requires JavaScript enabled)
+ + + + + + diff --git a/Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/041.html b/Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/041.html new file mode 100644 index 0000000000..3b1e350b06 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/041.html @@ -0,0 +1,29 @@ + + + scheduler: document.write scripts that write scripts + + + + + + +
FAILED (This TC requires JavaScript enabled)
+ + + + + + diff --git a/Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-1.js b/Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-1.js new file mode 100644 index 0000000000..8ff291ad57 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-1.js @@ -0,0 +1 @@ +log('external script #1'); \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-2.js b/Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-2.js new file mode 100644 index 0000000000..31319423af --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-2.js @@ -0,0 +1 @@ +log('external script #2'); \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-3.js b/Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-3.js new file mode 100644 index 0000000000..53352e0f83 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-3.js @@ -0,0 +1,3 @@ +log('external script before doc write'); +document.write( ''); +log('external script after doc write'); \ No newline at end of file diff --git a/Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-4.js b/Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-4.js new file mode 100644 index 0000000000..0597a22624 --- /dev/null +++ b/Tests/LibWeb/Text/input/wpt-import/html/semantics/scripting-1/the-script-element/execution-timing/scripts/include-4.js @@ -0,0 +1,3 @@ +log('include-4 before doc write'); +document.write( ''); +log('include-4 after doc write'); \ No newline at end of file