Automatic update from web-platform-tests Cover a few fragment parsing edge cases (#61042) * Add test-case for DSD when calling document.open() on an XHR response See https://github.com/whatwg/html/pull/12624 This clarifies a scenario where the allow-DSD flag has to be sticky on the document rather than just a parser flag. * Add tests for additional parser edge cases: - Check insertion logic "after body" for template.innerHTML - Check that template.innerHTML takes the custom element registry from the template * Revert template special case -- wpt-commits: 35be3b44f3111c4d614b5b201e399493d20e7b38 wpt-pr: 61042
259 lines
12 KiB
HTML
259 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
|
|
<link rel="help" href="https://github.com/whatwg/html/issues/10854">
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
</head>
|
|
<div id="host">
|
|
<template shadowrootmode="open" shadowrootclonable="true" shadowrootcustomelementregistry>
|
|
<new-element></new-element>
|
|
</template>
|
|
</div>
|
|
<body>
|
|
<script>
|
|
|
|
function createConnectedShadowTree(test, customElementRegistry) {
|
|
const host = document.createElement('div');
|
|
const shadowRoot = host.attachShadow({mode: 'closed', customElementRegistry});
|
|
document.body.appendChild(host);
|
|
test.add_cleanup(() => host.remove());
|
|
return shadowRoot;
|
|
}
|
|
|
|
class GlobalSomeElement extends HTMLElement { };
|
|
customElements.define('some-element', GlobalSomeElement);
|
|
|
|
class GlobalOtherElement extends HTMLElement { };
|
|
customElements.define('other-element', GlobalOtherElement);
|
|
|
|
class WrongNewElement extends HTMLElement{ };
|
|
customElements.define('new-element', WrongNewElement);
|
|
|
|
test((test) => {
|
|
const registry = new CustomElementRegistry;
|
|
|
|
class ScopedSomeElement extends HTMLElement { };
|
|
registry.define('some-element', ScopedSomeElement);
|
|
|
|
class ScopedOtherElement extends HTMLElement { };
|
|
registry.define('other-element', ScopedOtherElement);
|
|
|
|
const someElement = document.createElement('some-element', {customElementRegistry: registry});
|
|
assert_true(someElement instanceof ScopedSomeElement);
|
|
someElement.innerHTML = '<other-element></other-element>';
|
|
assert_true(someElement.querySelector('other-element') instanceof ScopedOtherElement);
|
|
}, 'innerHTML on a disconnected element should use the scoped registry it was created with');
|
|
|
|
test((test) => {
|
|
const registry = new CustomElementRegistry;
|
|
|
|
class ScopedSomeElement extends HTMLElement { };
|
|
registry.define('some-element', ScopedSomeElement);
|
|
|
|
class ScopedOtherElement extends HTMLElement { };
|
|
registry.define('other-element', ScopedOtherElement);
|
|
|
|
const someElement = document.createElement('some-element', {customElementRegistry: registry});
|
|
assert_true(someElement instanceof ScopedSomeElement);
|
|
someElement.innerHTML = `
|
|
<other-element id="foo">
|
|
<other-element id="bar">
|
|
<div id="baz">
|
|
<other-element id="nested-ce"></other-element>
|
|
</div>
|
|
</other-element>
|
|
</other-element>
|
|
<div id="bux"></div>
|
|
<another-element></another-element>`;
|
|
|
|
assert_true(someElement.querySelector('#foo') instanceof ScopedOtherElement);
|
|
assert_true(someElement.querySelector('#bar') instanceof ScopedOtherElement);
|
|
assert_true(someElement.querySelector('#nested-ce') instanceof ScopedOtherElement);
|
|
|
|
assert_true(someElement.querySelector('#foo').customElementRegistry === registry);
|
|
assert_true(someElement.querySelector('#bar').customElementRegistry === registry);
|
|
assert_true(someElement.querySelector('#baz').customElementRegistry === registry);
|
|
assert_true(someElement.querySelector('#nested-ce').customElementRegistry === registry);
|
|
assert_true(someElement.querySelector('#bux').customElementRegistry === registry);
|
|
assert_true(someElement.querySelector('another-element').customElementRegistry === registry);
|
|
}, 'nested descendants in innerHTML on a disconnected element should use the scoped registry the element was created with');
|
|
|
|
test((test) => {
|
|
const registry = new CustomElementRegistry;
|
|
|
|
class ScopedSomeElement extends HTMLElement { };
|
|
registry.define('some-element', ScopedSomeElement);
|
|
|
|
const div = document.createElement('div', {customElementRegistry: registry});
|
|
div.innerHTML = '<span></span>';
|
|
assert_equals(div.querySelector('span').customElementRegistry, registry);
|
|
}, 'innerHTML on a disconnected element should use the scoped registry it was created with when parsing a simple HTML');
|
|
|
|
test((test) => {
|
|
const registry1 = new CustomElementRegistry;
|
|
const registry2 = new CustomElementRegistry;
|
|
|
|
class ScopedSomeElement extends HTMLElement { };
|
|
registry1.define('some-element', ScopedSomeElement);
|
|
|
|
class ScopedOtherElement1 extends HTMLElement { };
|
|
registry1.define('other-element', ScopedOtherElement1);
|
|
class ScopedOtherElement2 extends HTMLElement { };
|
|
registry2.define('other-element', ScopedOtherElement2);
|
|
|
|
const shadowRoot1 = createConnectedShadowTree(test, registry1);
|
|
const shadowRoot2 = createConnectedShadowTree(test, registry2);
|
|
const someElement = document.createElement('some-element', {customElementRegistry: registry1});
|
|
someElement.innerHTML = '<other-element></other-element>';
|
|
assert_true(someElement.querySelector('other-element') instanceof ScopedOtherElement1);
|
|
shadowRoot2.appendChild(someElement);
|
|
someElement.innerHTML = '<other-element></other-element>';
|
|
assert_true(someElement.querySelector('other-element') instanceof ScopedOtherElement1);
|
|
someElement.remove();
|
|
someElement.innerHTML = '<other-element></other-element>';
|
|
assert_true(someElement.querySelector('other-element') instanceof ScopedOtherElement1);
|
|
}, 'innerHTML on an inserted element should continue to use the scoped registry it was created with');
|
|
|
|
test((test) => {
|
|
const shadowRoot = host.cloneNode(true).shadowRoot;
|
|
const container_element = shadowRoot.lastElementChild;
|
|
assert_equals(container_element.customElementRegistry, null);
|
|
assert_false(container_element instanceof WrongNewElement);
|
|
|
|
container_element.innerHTML = '<new-element><new-element></new-element></new-element>';
|
|
const outer_element = container_element.querySelector('new-element');
|
|
const inner_element = outer_element.querySelector('new-element');
|
|
assert_equals(outer_element.customElementRegistry, null);
|
|
assert_false(outer_element instanceof WrongNewElement);
|
|
assert_equals(inner_element.customElementRegistry, null);
|
|
assert_false(inner_element instanceof WrongNewElement);
|
|
}, 'nested descendants in innerHTML should use the null registry when the container element has null registry');
|
|
|
|
test((test) => {
|
|
const shadowRoot = host.cloneNode(true).shadowRoot;
|
|
shadowRoot.firstElementChild.insertAdjacentHTML('afterend', '<new-element></new-element>');
|
|
const container_element = shadowRoot.lastElementChild;
|
|
assert_equals(container_element.customElementRegistry, null);
|
|
assert_false(container_element instanceof WrongNewElement);
|
|
}, 'insertAdjacentHTML should use the element\'s registry even when the registry is null');
|
|
|
|
test((test) => {
|
|
const registry = new CustomElementRegistry;
|
|
class ScopedNewElement extends HTMLElement { };
|
|
registry.define('new-element', ScopedNewElement);
|
|
|
|
const template = document.createElement('template', {customElementRegistry: registry});
|
|
assert_equals(template.customElementRegistry, registry);
|
|
|
|
const range = document.createRange();
|
|
range.selectNodeContents(template);
|
|
const fragment = range.createContextualFragment('<new-element><new-element></new-element></new-element>');
|
|
const outer_element = fragment.querySelector('new-element');
|
|
const inner_element = outer_element.querySelector('new-element');
|
|
assert_equals(outer_element.customElementRegistry, null);
|
|
assert_false(outer_element instanceof ScopedNewElement);
|
|
assert_false(outer_element instanceof WrongNewElement);
|
|
assert_equals(inner_element.customElementRegistry, null);
|
|
assert_false(inner_element instanceof ScopedNewElement);
|
|
assert_false(inner_element instanceof WrongNewElement);
|
|
}, 'createContextualFragment on a range inside a template should use null registry even when the template has a scoped registry');
|
|
|
|
test((test) => {
|
|
const registry = new CustomElementRegistry;
|
|
class ScopedNewElement extends HTMLElement { };
|
|
registry.define('new-element', ScopedNewElement);
|
|
|
|
const template = document.createElement('template', {customElementRegistry: registry});
|
|
assert_equals(template.customElementRegistry, registry);
|
|
|
|
template.innerHTML = '<new-element><new-element></new-element></new-element>';
|
|
const outer_element = template.content.querySelector('new-element');
|
|
const inner_element = outer_element.querySelector('new-element');
|
|
document.body.append(outer_element);
|
|
assert_equals(outer_element.customElementRegistry, document.customElementRegistry);
|
|
assert_equals(inner_element.customElementRegistry, document.customElementRegistry);
|
|
}, 'innerHTML on a template with a scoped registry should use the scoped registry of the document');
|
|
|
|
test((test) => {
|
|
const registry = new CustomElementRegistry;
|
|
class ScopedNewElement extends HTMLElement { };
|
|
registry.define('new-element', ScopedNewElement);
|
|
|
|
const div = document.createElement('div', {customElementRegistry: registry});
|
|
assert_equals(div.customElementRegistry, registry);
|
|
|
|
const range = document.createRange();
|
|
range.selectNodeContents(div);
|
|
const fragment = range.createContextualFragment('<new-element><new-element></new-element></new-element>');
|
|
const outer_element = fragment.querySelector('new-element');
|
|
const inner_element = outer_element.querySelector('new-element');
|
|
assert_equals(outer_element.customElementRegistry, registry);
|
|
assert_true(outer_element instanceof ScopedNewElement);
|
|
assert_false(outer_element instanceof WrongNewElement);
|
|
assert_equals(inner_element.customElementRegistry, registry);
|
|
assert_true(inner_element instanceof ScopedNewElement);
|
|
assert_false(inner_element instanceof WrongNewElement);
|
|
}, 'createContextualFragment on a range inside an element with scoped registry should use the scoped registry of the element');
|
|
|
|
test((test) => {
|
|
const registry = new CustomElementRegistry;
|
|
class ScopedNewElement extends HTMLElement { };
|
|
registry.define('new-element', ScopedNewElement);
|
|
|
|
const div = document.createElement('div', {customElementRegistry: registry});
|
|
document.body.append(div);
|
|
test.add_cleanup(() => div.remove());
|
|
|
|
assert_equals(div.customElementRegistry, registry);
|
|
div.insertAdjacentHTML('beforebegin', '<new-element></new-element>');
|
|
const sibling = div.previousSibling;
|
|
test.add_cleanup(() => sibling.remove());
|
|
assert_equals(sibling.customElementRegistry, customElements,
|
|
'sibling inserted via beforebegin should use parent\'s registry, not the element\'s');
|
|
assert_false(sibling instanceof ScopedNewElement,
|
|
'sibling should not be upgraded by the element\'s scoped registry');
|
|
}, 'insertAdjacentHTML with beforebegin should use the containing scope\'s registry');
|
|
|
|
test((test) => {
|
|
const registry = new CustomElementRegistry;
|
|
class ScopedNewElement extends HTMLElement { };
|
|
registry.define('new-element', ScopedNewElement);
|
|
|
|
const div = document.createElement('div', {customElementRegistry: registry});
|
|
document.body.append(div);
|
|
test.add_cleanup(() => div.remove());
|
|
|
|
assert_equals(div.customElementRegistry, registry);
|
|
div.insertAdjacentHTML('afterend', '<new-element></new-element>');
|
|
const sibling = div.nextSibling;
|
|
test.add_cleanup(() => sibling.remove());
|
|
assert_equals(sibling.customElementRegistry, customElements,
|
|
'sibling inserted via afterend should use parent\'s registry, not the element\'s');
|
|
assert_false(sibling instanceof ScopedNewElement,
|
|
'sibling should not be upgraded by the element\'s scoped registry');
|
|
}, 'insertAdjacentHTML with afterend should use the containing scope\'s registry');
|
|
|
|
test((test) => {
|
|
const registry = new CustomElementRegistry;
|
|
class ScopedNewElement extends HTMLElement { };
|
|
registry.define('new-element', ScopedNewElement);
|
|
|
|
const div = document.createElement('div', {customElementRegistry: registry});
|
|
document.body.append(div);
|
|
|
|
assert_equals(div.customElementRegistry, registry);
|
|
div.outerHTML = '<new-element></new-element>';
|
|
const replacement = document.body.lastElementChild;
|
|
test.add_cleanup(() => replacement.remove());
|
|
assert_equals(replacement.customElementRegistry, customElements,
|
|
'replacement via outerHTML should use parent\'s registry, not the replaced element\'s');
|
|
assert_false(replacement instanceof ScopedNewElement,
|
|
'replacement should not be upgraded by the replaced element\'s scoped registry');
|
|
}, 'outerHTML setter should use the parent scope\'s registry, not the replaced element\'s');
|
|
|
|
</script>
|
|
</body>
|
|
</html>
|