fune/testing/web-platform/tests/custom-elements/throw-on-dynamic-markup-insertion-counter-construct-xml-parser.xhtml
L. David Baron 1ea8a79355 Bug 1841279 [wpt PR 40831] - Fix two custom elements tests to avoid depending on undefined ordering., a=testonly
Automatic update from web-platform-tests
Fix two custom elements tests to avoid depending on undefined ordering.

Each test file has a single test that is currently failing identically
in both Chromium and Gecko, while passing in WebKit.

The tests that fail invoke:
  document.open(URL, "_self", "");
in a custom element constructor, and then rely on a script element
*following* the custom element to be executed.

This change fixes the test to avoid depending on that following script,
but instead run the necessary code in the custom element constructor.
(Only the test that does the document.open(URL, "_self", "") call needs
this change, but the change affects all the tests in these files.)

I believe the current test is depending on undefined behavior because
(although I fully admit I'm skimming relatively long lists of complex
steps in the HTML specification quite quickly) the document.open call is
specified at
https://html.spec.whatwg.org/multipage/C#dom-document-open-window which
invokes https://html.spec.whatwg.org/multipage/C#window-open-steps where
either step 12.6 or 13.1.3 are the same and invoke
https://html.spec.whatwg.org/multipage/C#navigate .  Step 19 of that
algorithm is run "in parallel"), and step 19.3 invokes
https://html.spec.whatwg.org/multipage/C#abort-a-document which in turn
invokes https://html.spec.whatwg.org/multipage/C#abort-a-parser which I
believe will stop later scripts from executing.  The definition of "in
parallel" is explicitly undefined at
https://html.spec.whatwg.org/multipage/C#in-parallel so it's not clear
when the parser is aborted relative to other things.  I think this means
that either the Gecko/Chromium behavior of aborting the parser before
executing the following script, or the WebKit behavior of aborting at
some later time, are both permitted.

Fixed: 1365368
Fixed: https://bugzilla.mozilla.org/show_bug.cgi?id=1819922
Fixed: https://bugzilla.mozilla.org/show_bug.cgi?id=1819925
Change-Id: Ied396b36e5b7466de7ef98b9ab4559c9556be995
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4661389
Commit-Queue: Joey Arhar <jarhar@chromium.org>
Auto-Submit: David Baron <dbaron@chromium.org>
Reviewed-by: Joey Arhar <jarhar@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1164733}

--

wpt-commits: 63e0e7019a119d75308e436bcec179c6cc40edae
wpt-pr: 40831
2023-07-12 07:53:55 +00:00

130 lines
7.2 KiB
HTML

<?xml version="1.0" encoding="utf-8"?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Custom Elements: create an element for a token must increment and decrement document's throw-on-dynamic-markup-insertion counter</title>
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org" />
<meta name="assert" content="Invoking document.open, document.write, document.writeln, and document.write must throw an exception when the HTML parser is creating a custom element for a token" />
<meta name="help" content="https://html.spec.whatwg.org/multipage/parsing.html#create-an-element-for-the-token" />
<meta name="help" content="https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#throw-on-dynamic-markup-insertion-counter" />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/custom-elements-helpers.js"></script>
</head>
<body>
<div id="log"></div>
<script>
<![CDATA[
async function construct_custom_element_in_parser(test, code)
{
window.executed = false;
window.exception = null;
const content_window = await create_window_in_test_async(test, 'application/xml', `<?xml version="1.0" encoding="utf-8"?>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script>
<![CDATA[
class CustomElement extends window.HTMLElement {
constructor() {
super();
let exception = null;
try {
${code}
} catch (error) {
exception = error;
}
top.executed = true;
top.exception = exception;
}
}
customElements.define('some-element', CustomElement);
]]` + `>
</` + `script>
</head>
<body>
<some-element></some-element>
</body>
</html>`);
let content_document;
try {
content_document = content_window.document;
} catch (error) { }
assert_true(executed, 'Must synchronously instantiate a custom element');
return {window: content_window, document: content_document, exception};
}
promise_test(async function () {
const result = await construct_custom_element_in_parser(this, `document.open()`);
assert_throws_dom('InvalidStateError', result.window.DOMException, () => { throw result.exception; }, 'Must throw an InvalidStateError');
}, 'document.open() must throw an InvalidStateError when synchronously constructing a custom element');
promise_test(async function () {
const result = await construct_custom_element_in_parser(this, `document.open('text/html')`);
assert_throws_dom('InvalidStateError', result.window.DOMException, () => { throw result.exception; }, 'Must throw an InvalidStateError');
}, 'document.open("text/html") must throw an InvalidStateError when synchronously constructing a custom element');
// https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#dom-document-open-window
promise_test(async function () {
let load_promise = new Promise((resolve) => window.onmessage = (event) => resolve(event.data));
const url = top.location.href.substring(0, top.location.href.lastIndexOf('/')) + '/resources/navigation-destination.html';
const result = await construct_custom_element_in_parser(this, `document.open('${url}', '_self', '')`);
assert_equals(result.exception, null);
assert_equals(await load_promise, 'didNavigate');
}, 'document.open(URL) must NOT throw an InvalidStateError when synchronously constructing a custom element');
promise_test(async function () {
const result = await construct_custom_element_in_parser(this, `document.close()`);
assert_not_equals(result.exception, null);
assert_throws_dom('InvalidStateError', result.window.DOMException, () => { throw result.exception; }, 'Must throw an InvalidStateError');
}, 'document.close() must throw an InvalidStateError when synchronously constructing a custom element');
promise_test(async function () {
const result = await construct_custom_element_in_parser(this, `document.write('<b>some text</b>')`);
assert_throws_dom('InvalidStateError', result.window.DOMException, () => { throw result.exception; }, 'Must throw an InvalidStateError');
assert_equals(result.document.querySelector('b'), null, 'Must not insert new content');
assert_false(result.document.body.innerHTML.includes('some text'), 'Must not insert new content');
}, 'document.write must throw an InvalidStateError when synchronously constructing a custom element');
promise_test(async function () {
const result = await construct_custom_element_in_parser(this, `document.writeln('<b>some text</b>')`);
assert_throws_dom('InvalidStateError', result.window.DOMException, () => { throw result.exception; }, 'Must throw an InvalidStateError');
assert_equals(result.document.querySelector('b'), null, 'Must not insert new content');
assert_false(result.document.body.innerHTML.includes('some text'), 'Must not insert new content');
}, 'document.writeln must throw an InvalidStateError when synchronously constructing a custom element');
promise_test(async function () {
window.another_window = await create_window_in_test_async(this, 'text/html', '<!DOCTYPE html><html><body>');
const result = await construct_custom_element_in_parser(this, `top.another_window.document.open()`);
assert_equals(result.exception, null);
}, 'document.open() of another document must not throw an InvalidStateError when synchronously constructing a custom element');
promise_test(async function () {
window.another_window = await create_window_in_test_async(this, 'text/html', '<!DOCTYPE html><html><body>');
const result = await construct_custom_element_in_parser(this, `top.another_window.document.open('text/html')`);
assert_equals(result.exception, null);
}, 'document.open("text/html") of another document must not throw an InvalidStateError when synchronously constructing a custom element');
promise_test(async function () {
window.another_window = await create_window_in_test_async(this, 'text/html', '<!DOCTYPE html><html><body>');
const result = await construct_custom_element_in_parser(this, `top.another_window.document.close()`);
assert_equals(result.exception, null);
}, 'document.close() of another document must not throw an InvalidStateError when synchronously constructing a custom element');
promise_test(async function () {
window.another_window = await create_window_in_test_async(this, 'text/html', '<!DOCTYPE html><html><body>');
const result = await construct_custom_element_in_parser(this, `top.another_window.document.write('<b>some text</b>')`);
assert_equals(result.exception, null);
assert_equals(another_window.document.querySelector('b').outerHTML, '<b>some text</b>');
}, 'document.write of another document must not throw an InvalidStateError when synchronously constructing a custom element');
promise_test(async function () {
window.another_window = await create_window_in_test_async(this, 'text/html', '<!DOCTYPE html><html><body>');
const result = await construct_custom_element_in_parser(this, `top.another_window.document.writeln('<b>some text</b>')`);
assert_equals(result.exception, null);
assert_equals(another_window.document.querySelector('b').outerHTML, '<b>some text</b>');
}, 'document.writeln of another document must not throw an InvalidStateError when synchronously constructing a custom element');
]]>
</script>
</body>
</html>