blob: 8522e1287665f294dff676e32f933f5eff07dd48 [file] [edit]
<!doctype html>
<meta charset="utf-8" />
<title>HTML insertion methods and the html element</title>
<link rel="help" href="https://html.spec.whatwg.org/#filter-and-pre-insert-html" />
<link rel="help" href="https://html.spec.whatwg.org/#filter-and-replace-with-html" />
<link rel="help" href="https://html.spec.whatwg.org/#dom-element-appendhtml" />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
<script>
// appendHTML()/prependHTML() on the html element, and the positional methods
// on a node whose parent is the html element, throw a "HierarchyRequestError"
// DOMException, before the sanitizer config is validated. setHTML() and
// setHTMLUnsafe() on the html element are allowed: they replace all of its
// children and parse with the html element as context.
// The live-document cases run against an iframe's document so that a
// non-conforming implementation cannot remove this document's head/body.
function load_iframe(t) {
return new Promise((resolve) => {
const iframe = document.createElement("iframe");
iframe.srcdoc = "<!doctype html><title>old</title><p>old</p>";
iframe.onload = () => resolve(iframe);
t.add_cleanup(() => iframe.remove());
document.body.appendChild(iframe);
});
}
const invalidSanitizer = { sanitizer: { elements: ["div"], removeElements: ["div"] } };
for (const safe of [true, false]) {
const suffix = safe ? "" : "Unsafe";
for (const method of [`appendHTML${suffix}`, `prependHTML${suffix}`]) {
promise_test(async (t) => {
const iframe = await load_iframe(t);
const html = iframe.contentDocument.documentElement;
assert_throws_dom("HierarchyRequestError", iframe.contentWindow.DOMException, () => {
html[method]("<p>new</p>");
});
assert_equals(html.childNodes.length, 2, "head and body are untouched");
}, `${method} on the html element throws HierarchyRequestError`);
test(() => {
const doc = document.implementation.createHTMLDocument();
assert_throws_dom("HierarchyRequestError", () => {
doc.documentElement[method]("<p>new</p>");
});
}, `${method} on the html element of an inert document throws HierarchyRequestError`);
test(() => {
const html = document.createElement("html");
assert_throws_dom("HierarchyRequestError", () => {
html[method]("<p>new</p>");
});
assert_throws_dom("HierarchyRequestError", () => {
html[method]("<p>new</p>", invalidSanitizer);
}, "HierarchyRequestError is thrown before the sanitizer config is validated");
assert_equals(html.childNodes.length, 0);
}, `${method} on a detached html element throws HierarchyRequestError`);
}
for (const method of [`beforeHTML${suffix}`, `afterHTML${suffix}`, `replaceWithHTML${suffix}`]) {
promise_test(async (t) => {
const iframe = await load_iframe(t);
const doc = iframe.contentDocument;
const DOMException = iframe.contentWindow.DOMException;
assert_throws_dom("HierarchyRequestError", DOMException, () => {
doc.head[method]("<p>new</p>");
}, "head");
assert_throws_dom("HierarchyRequestError", DOMException, () => {
doc.body[method]("<p>new</p>");
}, "body");
assert_equals(doc.head.parentNode, doc.documentElement, "head is not removed");
assert_equals(doc.body.parentNode, doc.documentElement, "body is not removed");
assert_equals(doc.documentElement.childNodes.length, 2);
}, `${method} on head/body (parent is the html element) throws HierarchyRequestError`);
test(() => {
const html = document.createElement("html");
const comment = html.appendChild(document.createComment("c"));
assert_throws_dom("HierarchyRequestError", () => {
comment[method]("<p>new</p>");
});
assert_throws_dom("HierarchyRequestError", () => {
comment[method]("<p>new</p>", invalidSanitizer);
}, "HierarchyRequestError is thrown before the sanitizer config is validated");
assert_equals(comment.parentNode, html, "the node is not removed");
assert_equals(html.childNodes.length, 1);
}, `${method} on a child of a detached html element throws HierarchyRequestError`);
}
promise_test(async (t) => {
const iframe = await load_iframe(t);
const doc = iframe.contentDocument;
const html = doc.documentElement;
const oldHead = doc.head;
const oldBody = doc.body;
html[`setHTML${suffix}`]("<head><meta name='x'></head><body><p>new</p></body>");
assert_equals(oldHead.parentNode, null);
assert_equals(oldBody.parentNode, null);
assert_equals(html.childNodes.length, 2);
assert_equals(doc.head, html.firstChild);
assert_equals(doc.body, html.lastChild);
assert_equals(doc.body.innerHTML, "<p>new</p>");
assert_equals(doc.title, "", "the old title was removed");
}, `setHTML${suffix} on the html element replaces head and body`);
}
</script>
</body>