LibWeb: Execute the correct script in XMLDocumentBuilder::element_end()

We were mistakenly executing the current node's script instead of the
document's pending parsing-blocking script.

This caused ~1000 WPT tests to time out, since we never ended up firing
a load event for XHTML pages that load multiple external scripts.
This commit is contained in:
Andreas Kling 2024-07-25 14:08:45 +02:00 committed by Andreas Kling
commit 007c292af3
Notes: github-actions[bot] 2024-07-25 13:06:19 +00:00
7 changed files with 26 additions and 13 deletions

View file

@ -0,0 +1 @@
PASS

View file

@ -0,0 +1,10 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
<script src="../include.js"></script>
<script>
test(() => {
println("PASS");
});
</script>
</body>
</html>

View file

@ -1724,7 +1724,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<Event>> Document::create_event(StringView i
return JS::NonnullGCPtr(*event); return JS::NonnullGCPtr(*event);
} }
void Document::set_pending_parsing_blocking_script(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement* script) void Document::set_pending_parsing_blocking_script(HTML::HTMLScriptElement* script)
{ {
m_pending_parsing_blocking_script = script; m_pending_parsing_blocking_script = script;
} }

View file

@ -286,7 +286,7 @@ public:
WebIDL::ExceptionOr<JS::NonnullGCPtr<Event>> create_event(StringView interface); WebIDL::ExceptionOr<JS::NonnullGCPtr<Event>> create_event(StringView interface);
JS::NonnullGCPtr<Range> create_range(); JS::NonnullGCPtr<Range> create_range();
void set_pending_parsing_blocking_script(Badge<HTML::HTMLScriptElement>, HTML::HTMLScriptElement*); void set_pending_parsing_blocking_script(HTML::HTMLScriptElement*);
HTML::HTMLScriptElement* pending_parsing_blocking_script() { return m_pending_parsing_blocking_script.ptr(); } HTML::HTMLScriptElement* pending_parsing_blocking_script() { return m_pending_parsing_blocking_script.ptr(); }
JS::NonnullGCPtr<HTML::HTMLScriptElement> take_pending_parsing_blocking_script(Badge<HTML::HTMLParser>); JS::NonnullGCPtr<HTML::HTMLScriptElement> take_pending_parsing_blocking_script(Badge<HTML::HTMLParser>);

View file

@ -536,7 +536,7 @@ void HTMLScriptElement::prepare_script()
// 5. Otherwise: // 5. Otherwise:
else { else {
// 1. Set el's parser document's pending parsing-blocking script to el. // 1. Set el's parser document's pending parsing-blocking script to el.
m_parser_document->set_pending_parsing_blocking_script({}, this); m_parser_document->set_pending_parsing_blocking_script(this);
// FIXME: 2. Block rendering on el. // FIXME: 2. Block rendering on el.
@ -563,7 +563,7 @@ void HTMLScriptElement::prepare_script()
&& is_parser_inserted() && is_parser_inserted()
&& m_parser_document->has_a_style_sheet_that_is_blocking_scripts()) { && m_parser_document->has_a_style_sheet_that_is_blocking_scripts()) {
// 1. Set el's parser document's pending parsing-blocking script to el. // 1. Set el's parser document's pending parsing-blocking script to el.
m_parser_document->set_pending_parsing_blocking_script({}, this); m_parser_document->set_pending_parsing_blocking_script(this);
// 2. Set el's ready to be parser-executed to true. (The parser will handle executing the script.) // 2. Set el's ready to be parser-executed to true. (The parser will handle executing the script.)
m_ready_to_be_parser_executed = true; m_ready_to_be_parser_executed = true;

View file

@ -117,25 +117,27 @@ void XMLDocumentBuilder::element_end(const XML::Name& name)
// and then prepare the script element. // and then prepare the script element.
auto& script_element = static_cast<HTML::HTMLScriptElement&>(*m_current_node); auto& script_element = static_cast<HTML::HTMLScriptElement&>(*m_current_node);
script_element.prepare_script(Badge<XMLDocumentBuilder> {}); script_element.prepare_script(Badge<XMLDocumentBuilder> {});
// If this causes there to be a pending parsing-blocking script, then the user agent must run the following steps: // If this causes there to be a pending parsing-blocking script, then the user agent must run the following steps:
if (m_document->pending_parsing_blocking_script()) { if (auto pending_parsing_blocking_script = m_document->pending_parsing_blocking_script()) {
// Block this instance of the XML parser, such that the event loop will not run tasks that invoke it. // 1. Block this instance of the XML parser, such that the event loop will not run tasks that invoke it.
// NOTE: Noop. // NOTE: Noop.
// Spin the event loop until the parser's Document has no style sheet that is blocking scripts and the pending parsing-blocking script's "ready to be parser-executed" flag is set. // 2. Spin the event loop until the parser's Document has no style sheet that is blocking scripts and the pending parsing-blocking script's "ready to be parser-executed" flag is set.
if (m_document->has_a_style_sheet_that_is_blocking_scripts() || !script_element.is_ready_to_be_parser_executed()) { if (m_document->has_a_style_sheet_that_is_blocking_scripts() || !pending_parsing_blocking_script->is_ready_to_be_parser_executed()) {
HTML::main_thread_event_loop().spin_until([&] { HTML::main_thread_event_loop().spin_until([&] {
return !m_document->has_a_style_sheet_that_is_blocking_scripts() && script_element.is_ready_to_be_parser_executed(); return !m_document->has_a_style_sheet_that_is_blocking_scripts() && pending_parsing_blocking_script->is_ready_to_be_parser_executed();
}); });
} }
// Unblock this instance of the XML parser, such that tasks that invoke it can again be run. // 3. Unblock this instance of the XML parser, such that tasks that invoke it can again be run.
// NOTE: Noop. // NOTE: Noop.
// Execute the pending parsing-blocking script. // 4. Execute the script element given by the pending parsing-blocking script.
script_element.execute_script(); pending_parsing_blocking_script->execute_script();
// There is no longer a pending parsing-blocking script. // 5. Set the pending parsing-blocking script to null.
m_document->set_pending_parsing_blocking_script(nullptr);
} }
} else if (m_scripting_support == XMLScriptingSupport::Enabled && m_current_node->is_svg_script_element()) { } else if (m_scripting_support == XMLScriptingSupport::Enabled && m_current_node->is_svg_script_element()) {
// https://www.w3.org/TR/SVGMobile12/struct.html#ProgressiveRendering // https://www.w3.org/TR/SVGMobile12/struct.html#ProgressiveRendering