Skip to content

Commit

Permalink
DOM: Added a few batch-insertion scenarios that are incompatible acro…
Browse files Browse the repository at this point in the history
…ss engines (#44658)

Co-authored-by: Anthony Ramine <n.oxyde@gmail.com>
Co-authored-by: Anne van Kesteren <annevk@annevk.nl>
Co-authored-by: Dominic Farolino <domfarolino@gmail.com>
  • Loading branch information
4 people authored Feb 29, 2024
1 parent 7a902e2 commit 870bb35
Show file tree
Hide file tree
Showing 12 changed files with 526 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!doctype html>
<meta charset=utf-8>
<title>Node.appendChild: inserting script and button from a div</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<form id="form"></form>
<script>
let button = null;
let buttonForm = null;
test(() => {
const form = document.getElementById("form");
const script = document.createElement("script");
script.textContent = `
buttonForm = button.form;
`;
button = document.createElement("button");
const div = document.createElement("div");
div.appendChild(script);
div.appendChild(button);
assert_equals(buttonForm, null);
form.appendChild(div);
assert_equals(buttonForm, form);
}, "Script inserted before a form-associated button can observe the button's " +
"form, because by the time the script executes, the DOM insertion that " +
"associates the button with the form is already done");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<!doctype html>
<meta charset=utf-8>
<title>Node.appendChild: inserting script and custom element from a DocumentFragment</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<body>
<script>
let customConstructed = false;
let customConstructedDuringEarlierScript = false;
class CustomElement extends HTMLElement {
constructor() {
super();
customConstructed = true;
}
}
test(() => {
const script = document.createElement("script");
script.textContent = `
customElements.define("custom-element", CustomElement);
customConstructedDuringEarlierScript = customConstructed;
`;
const custom = document.createElement("custom-element");
const df = document.createDocumentFragment();
df.appendChild(script);
df.appendChild(custom);
assert_false(customConstructed);
assert_false(customConstructedDuringEarlierScript);
document.head.appendChild(df);
assert_true(customConstructed);
assert_true(customConstructedDuringEarlierScript);
}, "An earlier-inserted script can upgrade a later-inserted custom element, " +
"whose upgrading is synchronously observable to the script, since DOM " +
"insertion has been completed by the time it runs");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!doctype html>
<meta charset=utf-8>
<title>Node.appendChild: inserting script and default-style meta from a fragment</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<link rel="alternate stylesheet" title="alternative" href="data:text/css,%23div{display:none}">
<div id="div">hello</div>
<script>
let scriptRan = false;
let computedStyleDuringInsertion = null;
test(() => {
const div = document.getElementById("div");
const meta = document.createElement("meta");
meta.httpEquiv = "default-style";
meta.content = "alternative";
const script = document.createElement("script");
script.textContent = `
computedStyleDuringInsertion = getComputedStyle(div).display;
scriptRan = true;
`;
const df = document.createDocumentFragment();
df.appendChild(script);
df.appendChild(meta);
assert_equals(getComputedStyle(div).display, "block", "div has block display");
assert_false(scriptRan, "script has not run before insertion");
document.head.appendChild(df);
assert_true(scriptRan, "script has run after insertion");
assert_equals(computedStyleDuringInsertion, "none",
"display: none; style was applied during DOM insertion, before " +
"later-inserted script runs");
assert_equals(getComputedStyle(div).display, "none",
"style remains display: none; after insertion");
}, "Inserting <meta> that uses alternate stylesheets, applies the style " +
"during DOM insertion, and before script runs as a result of any atomic insertions");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<!doctype html>
<meta charset=utf-8>
<title>Node.appendChild: inserting script and div from a DocumentFragment</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<body>
<script>
let script = null;
let scriptParent = null;
let div = null;
let divParent = null;
test(() => {
script = document.createElement("script");
div = document.createElement("div");
script.textContent = `
divParent = div.parentNode;
scriptParent = script.parentNode;
`;
const df = document.createDocumentFragment();
df.appendChild(script);
df.appendChild(div);
assert_equals(divParent, null);
assert_equals(scriptParent, null);
document.head.appendChild(df);
assert_equals(divParent, scriptParent);
assert_equals(divParent, document.head);
}, "Earlier-inserted scripts can observe the parentNode of later-inserted " +
"nodes, because script runs after DOM insertion completes");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Node.appendChild: inserting script and iframe</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<body>
<script>

const kScriptContent = `
state = iframe.contentWindow ? "iframe with content window" : "contentWindow is null";
`;

// This test ensures that a later-inserted script can observe an
// earlier-inserted iframe's contentWindow.
test(t => {
window.state = "script not run yet";
window.iframe = document.createElement("iframe");
t.add_cleanup(() => window.iframe.remove());

const script = document.createElement("script");
script.textContent = kScriptContent;

const div = document.createElement("div");
div.appendChild(iframe);
div.appendChild(script);

assert_equals(state, "script not run yet");
document.body.appendChild(div);
assert_equals(state, "iframe with content window");
}, "Script inserted after an iframe in the same appendChild() call can " +
"observe the iframe's non-null contentWindow");

// The below tests assert that an earlier-inserted script does not observe a
// later-inserted iframe's contentWindow.
test(t => {
window.state = "script not run yet";
window.iframe = document.createElement("iframe");
t.add_cleanup(() => window.iframe.remove());

const script = document.createElement("script");
script.textContent = kScriptContent;

const div = document.createElement("div");
div.appendChild(script);
div.appendChild(iframe);

assert_equals(state, "script not run yet");
document.body.appendChild(div);
assert_equals(state, "contentWindow is null");
}, "A script inserted atomically before an iframe (using a div) does not " +
"observe the iframe's contentWindow, since the 'script running' and " +
"'iframe setup' both happen in order, after DOM insertion completes");

test(t => {
window.state = "script not run yet";
window.iframe = document.createElement("iframe");
t.add_cleanup(() => window.iframe.remove());

const script = document.createElement("script");
script.textContent = kScriptContent;

const df = document.createDocumentFragment();
df.appendChild(script);
df.appendChild(iframe);

assert_equals(state, "script not run yet");
document.body.appendChild(df);
assert_equals(state, "contentWindow is null");
}, "A script inserted atomically before an iframe (using a DocumentFragment) " +
"does not observe the iframe's contentWindow, since the 'script running' " +
"and 'iframe setup' both happen in order, after DOM insertion completes");

test(t => {
window.state = "script not run yet";
window.iframe = document.createElement("iframe");
t.add_cleanup(() => window.iframe.remove());

const script = document.createElement("script");
script.textContent = kScriptContent;

assert_equals(state, "script not run yet");
document.body.append(script, iframe);

assert_equals(state, "contentWindow is null");
}, "A script inserted atomically before an iframe (using a append() with " +
"multiple arguments) does not observe the iframe's contentWindow, since " +
"the 'script running' and 'iframe setup' both happen in order, after DOM " +
"insertion completes");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<!doctype html>
<meta charset=utf-8>
<title>Node.appendChild: inserting script and source from a fragment</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<video id="media"></video>
<script>
const happened = [];
const media = document.getElementById("media");
test(() => {
const source = document.createElement("source");
const script = document.createElement("script");
script.textContent = `
happened.push(media.networkState);
`;

const df = document.createDocumentFragment();
df.appendChild(script);
df.appendChild(source);

assert_array_equals(happened, []);
media.appendChild(df);
// This is because immediately during DOM insertion, before the
// post-insertion steps invoke script, `<source>` insertion invokes the
// resource selection algorithm [1] which does this assignment. This
// assignment takes place before earlier-inserted script elements run
// post-insertion.
//
// [1]: https://html.spec.whatwg.org/#concept-media-load-algorithm
assert_array_equals(happened, [HTMLMediaElement.NETWORK_NO_SOURCE]);
}, "Empty <source> immediately sets media.networkState during DOM insertion, " +
"so that an earlier-running script can observe networkState");
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<!doctype html>
<meta charset=utf-8>
<title>Node.appendChild: inserting a script and a style where the script modifies the style</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<body>
<script>
// <script> & <style> element tests.
test(() => {
window.happened = [];
window.style = document.createElement("style");
let styleSheet = null;

style.appendChild(new Text("body {}"));
const script = document.createElement("script");
script.textContent = `
styleSheet = style.sheet;
happened.push(style.sheet ? "sheet" : null);
style.appendChild(new Text("body {}"));
happened.push(style.sheet?.cssRules.length);
`;

const div = document.createElement("div");
div.appendChild(script);
div.appendChild(style);

assert_array_equals(happened, []);
document.body.appendChild(div);
assert_array_equals(happened, ["sheet", 2]);
assert_not_equals(style.sheet, styleSheet, "style sheet was created only once");
}, "An earlier-inserted <script> synchronously observes a later-inserted " +
"<style> (via a div) being applied");

test(() => {
window.happened = [];
window.style = document.createElement("style");
let styleSheet = null;

style.appendChild(new Text("body {}"));
const script = document.createElement("script");
script.textContent = `
styleSheet = style.sheet;
happened.push(style.sheet ? "sheet" : null);
style.appendChild(new Text("body {}"));
happened.push(style.sheet?.cssRules.length);
`;

const df = document.createDocumentFragment();
df.appendChild(script);
df.appendChild(style);

assert_array_equals(happened, []);
document.body.appendChild(df);
assert_array_equals(happened, ["sheet", 2]);
assert_not_equals(style.sheet, styleSheet, "style sheet was created only once");
}, "An earlier-inserted <script> synchronously observes a later-inserted " +
"<style> (via a DocumentFragment) being applied");

// <script> & <link rel=stylesheet> element tests.
test(() => {
window.happened = [];
window.link = document.createElement("link");
link.rel = "stylesheet";
link.href = "data:text/css,";

const script = document.createElement("script");
script.textContent = `
happened.push(link.sheet ? "sheet" : null);
`;

const df = document.createDocumentFragment();
df.appendChild(script);
df.appendChild(link);

assert_array_equals(happened, []);
document.body.appendChild(df);
assert_array_equals(happened, ["sheet"]);
}, "Earlier-inserted <script> (via a DocumentFragment) synchronously " +
"observes a later-inserted <link rel=stylesheet>'s CSSStyleSheet creation");

test(() => {
window.happened = [];
window.link = document.createElement("link");
link.rel = "stylesheet";
link.href = "data:text/css,";

const script = document.createElement("script");
script.textContent = `
happened.push(link.sheet ? "sheet" : null);
`;

const div = document.createElement("div");
div.appendChild(script);
div.appendChild(link);

assert_array_equals(happened, []);
document.body.appendChild(div);
assert_array_equals(happened, ["sheet"]);
}, "Earlier-inserted <script> (via a div) synchronously observes a " +
"later-inserted <link rel=stylesheet>'s CSSStyleSheet creation");

test(() => {
window.happened = [];
window.link = document.createElement("link");
link.rel = "stylesheet";
link.href = "data:text/css,";

const script = document.createElement("script");
script.textContent = `
happened.push(link.sheet ? "sheet" : null);
`;

assert_array_equals(happened, []);
document.body.append(script, link);
assert_array_equals(happened, ["sheet"]);
}, "Earlier-inserted <script> (via a append()) synchronously observes a " +
"later-inserted <link rel=stylesheet>'s CSSStyleSheet creation");
</script>
Loading

0 comments on commit 870bb35

Please sign in to comment.