Skip to content

Commit 45d82ab

Browse files
authored
HTML: more tests for document.open bailout order (#12645)
A continuation of #12240, but this treats cases when two bailout criteria compete against each other. See whatwg/html#3818.
1 parent e570d8f commit 45d82ab

6 files changed

+163
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
document.domain = "{{host}}";
2+
3+
// In many cases in this test, we want to delay execution of a piece of code so
4+
// that the entry settings object would be the top-level page. A microtask is
5+
// perfect for this purpose as it is executed in the "clean up after running
6+
// script" algorithm, which is generally called right after the callback.
7+
function setEntryToTopLevel(cb) {
8+
Promise.resolve().then(cb);
9+
}
10+
11+
async_test(t => {
12+
const iframe = document.body.appendChild(document.createElement("iframe"));
13+
t.add_cleanup(() => { iframe.remove(); });
14+
iframe.onload = t.step_func_done(() => {
15+
// Since this is called as an event handler on an element of this window,
16+
// the entry settings object is that of this browsing context.
17+
assert_throws("InvalidStateError", () => {
18+
iframe.contentDocument.open();
19+
}, "opening an XML document should throw an InvalidStateError");
20+
});
21+
const frameURL = new URL("resources/bailout-order-xml-with-domain-frame.sub.xhtml", document.URL);
22+
frameURL.port = "{{ports[http][1]}}";
23+
iframe.src = frameURL.href;
24+
}, "document.open should throw an InvalidStateError with XML document even if it is cross-origin");
25+
26+
async_test(t => {
27+
const iframe = document.body.appendChild(document.createElement("iframe"));
28+
t.add_cleanup(() => { iframe.remove(); });
29+
window.onCustomElementReady = t.step_func(() => {
30+
window.onCustomElementReady = t.unreached_func("onCustomElementReady called again");
31+
// Here, the entry settings object is still the iframe's, as the function
32+
// is called from a custom element constructor in the iframe document.
33+
// Delay execution in such a way that makes the entry settings object the
34+
// top-level page's, but without delaying too much that the
35+
// throw-on-dynamic-markup-insertion counter gets decremented (which is
36+
// what this test tries to pit against the cross-origin document check).
37+
//
38+
// "Clean up after running script" is executed through the "construct" Web
39+
// IDL algorithm in "create an element", called by "create an element for a
40+
// token" in the parser.
41+
setEntryToTopLevel(t.step_func_done(() => {
42+
assert_throws("InvalidStateError", () => {
43+
iframe.contentDocument.open();
44+
}, "opening a document when the throw-on-dynamic-markup-insertion counter is incremented should throw an InvalidStateError");
45+
}));
46+
});
47+
const frameURL = new URL("resources/bailout-order-custom-element-with-domain-frame.sub.html", document.URL);
48+
frameURL.port = "{{ports[http][1]}}";
49+
iframe.src = frameURL.href;
50+
}, "document.open should throw an InvalidStateError when the throw-on-dynamic-markup-insertion counter is incremented even if the document is cross-origin");
51+
52+
async_test(t => {
53+
const iframe = document.body.appendChild(document.createElement("iframe"));
54+
t.add_cleanup(() => { iframe.remove(); });
55+
self.testSynchronousScript = t.step_func(() => {
56+
// Here, the entry settings object is still the iframe's, as the function
57+
// is synchronously called from a <script> element in the iframe's
58+
// document.
59+
//
60+
// "Clean up after running script" is executed when the </script> tag is
61+
// seen by the HTML parser.
62+
setEntryToTopLevel(t.step_func_done(() => {
63+
assert_throws("SecurityError", () => {
64+
iframe.contentDocument.open();
65+
}, "opening a same origin-domain (but not same origin) document should throw a SecurityError");
66+
}));
67+
});
68+
const frameURL = new URL("resources/bailout-order-synchronous-script-with-domain-frame.sub.html", document.URL);
69+
frameURL.port = "{{ports[http][1]}}";
70+
iframe.src = frameURL.href;
71+
}, "document.open should throw a SecurityError with cross-origin document even when there is an active parser executing script");
72+
73+
for (const ev of ["beforeunload", "pagehide", "unload"]) {
74+
async_test(t => {
75+
const iframe = document.body.appendChild(document.createElement("iframe"));
76+
t.add_cleanup(() => { iframe.remove(); });
77+
iframe.addEventListener("load", t.step_func(() => {
78+
iframe.contentWindow.addEventListener(ev, t.step_func(() => {
79+
// Here, the entry settings object should be the top-level page's, as
80+
// the callback context of this event listener is the incumbent
81+
// settings object, which is the this page. However, due to a Chrome
82+
// bug (https://crbug.com/606900), the entry settings object may be
83+
// mis-set to the iframe's.
84+
//
85+
// "Clean up after running script" is called in the task that
86+
// navigates.
87+
setEntryToTopLevel(t.step_func_done(() => {
88+
assert_throws("SecurityError", () => {
89+
iframe.contentDocument.open();
90+
}, "opening a same origin-domain (but not same origin) document should throw a SecurityError");
91+
}));
92+
}));
93+
iframe.src = "about:blank";
94+
}), { once: true });
95+
iframe.src = "http://{{host}}:{{ports[http][1]}}/common/domain-setter.sub.html";
96+
}, `document.open should throw a SecurityError with cross-origin document even when the ignore-opens-during-unload counter is greater than 0 (during ${ev} event)`);
97+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
async_test(t => {
2+
const iframe = document.body.appendChild(document.createElement("iframe"));
3+
t.add_cleanup(() => { iframe.remove(); });
4+
self.testSynchronousScript = t.step_func_done(() => {
5+
assert_throws("InvalidStateError", () => {
6+
iframe.contentDocument.open();
7+
}, "opening an XML document should throw");
8+
});
9+
iframe.src = "resources/bailout-order-xml-with-synchronous-script-frame.xhtml";
10+
}, "document.open should throw an InvalidStateError with XML document even when there is an active parser executing script");
11+
12+
for (const ev of ["beforeunload", "pagehide", "unload"]) {
13+
async_test(t => {
14+
const iframe = document.body.appendChild(document.createElement("iframe"));
15+
t.add_cleanup(() => { iframe.remove(); });
16+
iframe.addEventListener("load", t.step_func(() => {
17+
iframe.contentWindow.addEventListener(ev, t.step_func_done(() => {
18+
assert_throws("InvalidStateError", () => {
19+
iframe.contentDocument.open();
20+
}, "opening an XML document should throw");
21+
}));
22+
iframe.src = "about:blank";
23+
}), { once: true });
24+
iframe.src = "/common/dummy.xhtml";
25+
}, `document.open should throw an InvalidStateError with XML document even when the ignore-opens-during-unload counter is greater than 0 (during ${ev} event)`);
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<p>Text</p>
2+
<script>
3+
document.domain = "{{host}}";
4+
5+
class CustomElement extends HTMLElement {
6+
constructor() {
7+
super();
8+
parent.onCustomElementReady();
9+
}
10+
}
11+
customElements.define("custom-element", CustomElement);
12+
</script>
13+
<custom-element></custom-element>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<p>Text</p>
2+
<script>
3+
document.domain = "{{host}}";
4+
parent.testSynchronousScript();
5+
</script>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!DOCTYPE html>
3+
<html xmlns="http://www.w3.org/1999/xhtml">
4+
<head><title>XHTML document with domain set</title></head>
5+
<body>
6+
<p>Text</p>
7+
<script>
8+
document.domain = "{{host}}";
9+
</script>
10+
</body>
11+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!DOCTYPE html>
3+
<html xmlns="http://www.w3.org/1999/xhtml">
4+
<head><title>XHTML document with hook to run script from a script tag</title></head>
5+
<body>
6+
<p>Text</p>
7+
<script>
8+
parent.testSynchronousScript();
9+
</script>
10+
</body>
11+
</html>

0 commit comments

Comments
 (0)