Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

<!DOCTYPE html>
<html>
<head>
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
<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 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>