LibWeb/HTML: Use DOM's post connection steps for <script> elements

This aligns our behaviour with WebKit and Chrome.

See: https://github.com/whatwg/html/commit/ddd2d0dd
This commit is contained in:
Shannon Booth 2024-12-10 19:35:21 +13:00 committed by Luke Wilde
parent 02efb64e64
commit 0a216f9c14
Notes: github-actions[bot] 2024-12-10 10:39:52 +00:00
6 changed files with 82 additions and 16 deletions

View file

@ -59,13 +59,13 @@ void HTMLScriptElement::attribute_changed(FlyString const& name, Optional<String
} else if (name == HTML::AttributeNames::referrerpolicy) {
m_referrer_policy = ReferrerPolicy::from_string(value.value_or(""_string)).value_or(ReferrerPolicy::ReferrerPolicy::EmptyString);
} else if (name == HTML::AttributeNames::src) {
// https://html.spec.whatwg.org/multipage/scripting.html#script-processing-model
// When a script element el that is not parser-inserted experiences one of the events listed in the following list, the user agent must immediately prepare the script element el:
// - [...]
// - The script element is connected and has a src attribute set where previously the element had no such attribute.
if (!is_parser_inserted() && is_connected() && value.has_value() && !old_value.has_value()) {
prepare_script();
}
// https://html.spec.whatwg.org/multipage/scripting.html#script-processing-model:html-element-post-connection-steps-6
// 1. If namespace is not null, then return.
if (namespace_.has_value())
return;
// 2. If localName is src, then run the script HTML element post-connection steps, given element.
post_connection();
} else if (name == HTML::AttributeNames::async) {
// https://html.spec.whatwg.org/multipage/scripting.html#script-processing-model:script-force-async
// When an async attribute is added to a script element el, the user agent must set el's force async to false.
@ -586,15 +586,26 @@ void HTMLScriptElement::prepare_script()
}
}
void HTMLScriptElement::inserted()
// https://html.spec.whatwg.org/multipage/scripting.html#script-processing-model:html-element-post-connection-steps-4
void HTMLScriptElement::children_changed()
{
if (!is_parser_inserted()) {
// FIXME: Only do this if the element was previously not connected.
if (is_connected()) {
prepare_script();
}
}
HTMLElement::inserted();
// 1. Run the script HTML element post-connection steps, given the script element.
post_connection();
}
// https://html.spec.whatwg.org/multipage/scripting.html#script-processing-model:prepare-the-script-element-5
void HTMLScriptElement::post_connection()
{
// 1. If insertedNode is not connected, then return.
if (!is_connected())
return;
// 2. If insertedNode is parser-inserted, then return.
if (is_parser_inserted())
return;
// 3. Prepare the script element given insertedNode.
prepare_script();
}
// https://html.spec.whatwg.org/multipage/scripting.html#mark-as-ready

View file

@ -43,7 +43,8 @@ public:
bool is_parser_inserted() const { return !!m_parser_document; }
virtual void inserted() override;
virtual void children_changed() override;
virtual void post_connection() override;
// https://html.spec.whatwg.org/multipage/scripting.html#dom-script-supports
static bool supports(JS::VM&, StringView type)

View file

@ -0,0 +1,6 @@
Harness status: OK
Found 1 tests
1 Pass
Pass Mutating `src` attribute from an already-valid value does 'prepare' the script

View file

@ -0,0 +1,42 @@
<!doctype html>
<meta charset=utf-8>
<link rel=help href=https://github.com/whatwg/html/pull/10188#discussion_r1719338657>
<title>Adding/changing src attribute does "prepare the script"</title>
<script src="../../../../resources/testharness.js"></script>
<script src="../../../../resources/testharnessreport.js"></script>
<body>
<script>
// The "old" HTML Standard specification text around `src` attribute mutation
// for non-parser-inserted scripts *ONLY* "prepared"/run the script iff the src
// attribute "previously had no such attribute". This changed in
// https://github.com/whatwg/html/pull/10188 to align with a majority of
// browsers. This test ensures that `src` mutations on these kinds of scripts
// *where a previous valid `src` attribute existed* do indeed "prepare" the
// script.
promise_test(async () => {
window.didExecute = false;
const script = document.createElement('script');
// Invalid type, so the script won't execute upon insertion.
script.type = 'invalid';
script.src = 'resources/flag-setter.js';
document.body.append(script);
assert_false(window.didExecute);
// Make script valid, but don't immediately execute it.
script.type = '';
const scriptPromise = new Promise(resolve => {
script.onload = resolve;
});
// Mutating the `src` attribute, which has an existing valid value, triggers
// the "prepare a script" algorithm via the post-connection steps.
script.src = 'resources/flag-setter-different.js';
await scriptPromise;
assert_true(window.didExecute);
}, "Mutating `src` attribute from an already-valid value does 'prepare' the script");
</script>
</body>

View file

@ -0,0 +1,3 @@
"use strict";
window.didExecute = true;

View file

@ -0,0 +1,3 @@
"use strict";
window.didExecute = true;