mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 18:38:10 +00:00
LibWeb: Capture <script> element's node document on execution
Step 1 of the spec is to capture the <script> element's node document
into a local variable.
When I originally implemented this, I thought this was not necessary.
However, I realised that the script that runs can adopt the current
script element into a different document, meaning step 5.4 and 6 then
operate on the incorrect document.
Covered by this WPT: 7b0ebaccc6/html/semantics/scripting-1/the-script-element/moving-between-documents-during-evaluation.html
This commit is contained in:
parent
cacac7927b
commit
54454952e0
2 changed files with 45 additions and 8 deletions
|
@ -45,10 +45,11 @@ void HTMLScriptElement::set_non_blocking(Badge<HTMLParser>, bool non_blocking)
|
||||||
// https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-block
|
// https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-block
|
||||||
void HTMLScriptElement::execute_script()
|
void HTMLScriptElement::execute_script()
|
||||||
{
|
{
|
||||||
// 1. Let document be scriptElement's node document. (NOTE: This is not necessary)
|
// 1. Let document be scriptElement's node document.
|
||||||
|
NonnullRefPtr<DOM::Document> node_document = document();
|
||||||
|
|
||||||
// 2. If scriptElement's preparation-time document is not equal to document, then return.
|
// 2. If scriptElement's preparation-time document is not equal to document, then return.
|
||||||
if (m_preparation_time_document.ptr() != &document()) {
|
if (m_preparation_time_document.ptr() != node_document.ptr()) {
|
||||||
dbgln("HTMLScriptElement: Refusing to run script because the preparation time document is not the same as the node document.");
|
dbgln("HTMLScriptElement: Refusing to run script because the preparation time document is not the same as the node document.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -63,7 +64,7 @@ void HTMLScriptElement::execute_script()
|
||||||
// 4. If scriptElement is from an external file, or the script's type for scriptElement is "module", then increment document's ignore-destructive-writes counter.
|
// 4. If scriptElement is from an external file, or the script's type for scriptElement is "module", then increment document's ignore-destructive-writes counter.
|
||||||
bool incremented_destructive_writes_counter = false;
|
bool incremented_destructive_writes_counter = false;
|
||||||
if (m_from_an_external_file || m_script_type == ScriptType::Module) {
|
if (m_from_an_external_file || m_script_type == ScriptType::Module) {
|
||||||
document().increment_ignore_destructive_writes_counter();
|
node_document->increment_ignore_destructive_writes_counter();
|
||||||
incremented_destructive_writes_counter = true;
|
incremented_destructive_writes_counter = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,9 +75,9 @@ void HTMLScriptElement::execute_script()
|
||||||
auto old_current_script = document().current_script();
|
auto old_current_script = document().current_script();
|
||||||
// 2. If scriptElement's root is not a shadow root, then set document's currentScript attribute to scriptElement. Otherwise, set it to null.
|
// 2. If scriptElement's root is not a shadow root, then set document's currentScript attribute to scriptElement. Otherwise, set it to null.
|
||||||
if (!is<DOM::ShadowRoot>(root()))
|
if (!is<DOM::ShadowRoot>(root()))
|
||||||
document().set_current_script({}, this);
|
node_document->set_current_script({}, this);
|
||||||
else
|
else
|
||||||
document().set_current_script({}, nullptr);
|
node_document->set_current_script({}, nullptr);
|
||||||
|
|
||||||
if (m_from_an_external_file)
|
if (m_from_an_external_file)
|
||||||
dbgln_if(HTML_SCRIPT_DEBUG, "HTMLScriptElement: Running script {}", attribute(HTML::AttributeNames::src));
|
dbgln_if(HTML_SCRIPT_DEBUG, "HTMLScriptElement: Running script {}", attribute(HTML::AttributeNames::src));
|
||||||
|
@ -86,8 +87,8 @@ void HTMLScriptElement::execute_script()
|
||||||
// 3. Run the classic script given by the script's script for scriptElement.
|
// 3. Run the classic script given by the script's script for scriptElement.
|
||||||
verify_cast<ClassicScript>(*m_script).run();
|
verify_cast<ClassicScript>(*m_script).run();
|
||||||
|
|
||||||
// Set document's currentScript attribute to oldCurrentScript.
|
// 4. Set document's currentScript attribute to oldCurrentScript.
|
||||||
document().set_current_script({}, old_current_script);
|
node_document->set_current_script({}, old_current_script);
|
||||||
} else {
|
} else {
|
||||||
// -> "module"
|
// -> "module"
|
||||||
// 1. Assert: document's currentScript attribute is null.
|
// 1. Assert: document's currentScript attribute is null.
|
||||||
|
@ -99,7 +100,7 @@ void HTMLScriptElement::execute_script()
|
||||||
|
|
||||||
// 6. Decrement the ignore-destructive-writes counter of document, if it was incremented in the earlier step.
|
// 6. Decrement the ignore-destructive-writes counter of document, if it was incremented in the earlier step.
|
||||||
if (incremented_destructive_writes_counter)
|
if (incremented_destructive_writes_counter)
|
||||||
document().decrement_ignore_destructive_writes_counter();
|
node_document->decrement_ignore_destructive_writes_counter();
|
||||||
|
|
||||||
// 7. If scriptElement is from an external file, then fire an event named load at scriptElement.
|
// 7. If scriptElement is from an external file, then fire an event named load at scriptElement.
|
||||||
if (m_from_an_external_file)
|
if (m_from_an_external_file)
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
describe("currentScript", () => {
|
||||||
|
loadLocalPage("/res/html/misc/blank.html");
|
||||||
|
|
||||||
|
beforeInitialPageLoad(page => {
|
||||||
|
expect(page.document.currentScript).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterInitialPageLoad(page => {
|
||||||
|
test("reset to null even if currentScript is adopted into another document", () => {
|
||||||
|
const script = page.document.createElement("script");
|
||||||
|
script.id = "test";
|
||||||
|
script.innerText = `
|
||||||
|
const newDocument = globalThis.pageObject.document.implementation.createHTMLDocument();
|
||||||
|
const thisScript = globalThis.pageObject.document.getElementById("test");
|
||||||
|
|
||||||
|
// currentScript should stay the same even across adoption.
|
||||||
|
expect(globalThis.pageObject.document.currentScript).toBe(thisScript);
|
||||||
|
newDocument.adoptNode(thisScript);
|
||||||
|
expect(globalThis.pageObject.document.currentScript).toBe(thisScript);
|
||||||
|
`;
|
||||||
|
|
||||||
|
// currentScript should be null before and after running the script on insertion.
|
||||||
|
expect(page.document.currentScript).toBeNull();
|
||||||
|
expect(script.ownerDocument).toBe(page.document);
|
||||||
|
|
||||||
|
globalThis.pageObject = page;
|
||||||
|
page.document.body.appendChild(script);
|
||||||
|
globalThis.pageObject = undefined;
|
||||||
|
|
||||||
|
expect(page.document.currentScript).toBeNull();
|
||||||
|
expect(script.ownerDocument).not.toBe(page.document);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
waitForPageToLoad();
|
||||||
|
});
|
Loading…
Add table
Add a link
Reference in a new issue