Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

<!DOCTYPE html>
<title>Tests which nodes are upgraded after adding a scoped custom element definition</title>
<meta name="author" title="Xiaocheng Hu" href="mailto:xiaochengh@chromium.org">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body>
<script>
function attachShadowForTest(t, registry) {
const host = document.createElement('div');
const shadow = host.attachShadow({mode: 'open', registry});
document.body.appendChild(host);
t.add_cleanup(() => host.remove());
return shadow;
}
function createIFrameForTest(t) {
const iframe = document.createElement('iframe');
document.body.appendChild(iframe);
if (!iframe.contentDocument.body) {
iframe.contentDocument.body = iframe.contentDocument.createElement('body');
}
t.add_cleanup(() => iframe.remove());
return iframe;
}
let definitionCount = 0;
function nextCustomElementName() {
return `test-element-${++definitionCount}`;
}
test(t => {
const name = nextCustomElementName();
document.body.appendChild(document.createElement(name));
const registry = new CustomElementRegistry;
const shadow = attachShadowForTest(t, registry);
shadow.appendChild(document.createElement(name));
class TestElement extends HTMLElement {};
customElements.define(name, TestElement);
assert_true(document.querySelector(name) instanceof TestElement);
assert_false(shadow.querySelector(name) instanceof TestElement);
}, 'Adding definition to global registry should not affect shadow roots using scoped registry');
test(t => {
const name = nextCustomElementName();
const shadow1 = attachShadowForTest(t, customElements);
shadow1.appendChild(document.createElement(name));
const shadow2 = attachShadowForTest(t);
shadow2.appendChild(document.createElement(name));
class TestElement extends HTMLElement {};
customElements.define(name, TestElement);
assert_true(shadow1.querySelector(name) instanceof TestElement);
assert_true(shadow2.querySelector(name) instanceof TestElement);
}, 'Adding definition to global registry should affect shadow roots also using global registry');
test(t => {
const name = nextCustomElementName();
const registry = new CustomElementRegistry;
const shadow1 = attachShadowForTest(t, registry);
shadow1.appendChild(document.createElement(name));
const shadow2 = attachShadowForTest(t, registry);
shadow2.appendChild(document.createElement(name));
class TestElement extends HTMLElement {};
registry.define(name, TestElement);
assert_true(shadow1.querySelector(name) instanceof TestElement);
assert_true(shadow2.querySelector(name) instanceof TestElement);
}, 'Adding definition to scoped registry should affect all associated shadow roots');
test(t => {
const name = nextCustomElementName();
document.body.appendChild(document.createElement(name));
const registry = new CustomElementRegistry;
const shadow = attachShadowForTest(t, registry);
shadow.appendChild(document.createElement(name));
class TestElement extends HTMLElement {};
registry.define(name, TestElement);
assert_false(document.querySelector(name) instanceof TestElement);
assert_true(shadow.querySelector(name) instanceof TestElement);
}, 'Adding definition to scoped registry should not affect document tree scope');
test(t => {
const name = nextCustomElementName();
const registry = new CustomElementRegistry;
const shadow1 = attachShadowForTest(t, registry);
shadow1.appendChild(document.createElement(name));
const shadow2 = attachShadowForTest(t, new CustomElementRegistry);
shadow2.appendChild(document.createElement(name));
const shadow3 = attachShadowForTest(t);
shadow3.appendChild(document.createElement(name));
class TestElement extends HTMLElement {};
registry.define(name, TestElement);
assert_true(shadow1.querySelector(name) instanceof TestElement);
assert_false(shadow2.querySelector(name) instanceof TestElement);
assert_false(shadow3.querySelector(name) instanceof TestElement);
}, 'Adding definition to scoped registry should not affect shadow roots using other registries');
test(t => {
const name = nextCustomElementName();
const node = document.body.appendChild(document.createElement(name));
const registry = new CustomElementRegistry;
const shadow = attachShadowForTest(t, registry);
shadow.appendChild(node);
class TestElement extends HTMLElement {};
customElements.define(name, TestElement);
assert_false(node instanceof TestElement);
}, 'Adding definition to global registry should not upgrade nodes no longer using the registry');
test(t => {
const name = nextCustomElementName();
const registry = new CustomElementRegistry;
const shadow1 = attachShadowForTest(t, registry);
const node = shadow1.appendChild(document.createElement(name));
const shadow2 = attachShadowForTest(t, new CustomElementRegistry);
shadow2.appendChild(node);
class TestElement extends HTMLElement {};
registry.define(name, TestElement);
assert_false(node instanceof TestElement);
}, 'Adding definition to scoped registry should not upgrade nodes no longer using the registry');
test(t => {
const name = nextCustomElementName();
const registry = new CustomElementRegistry;
const shadow1 = attachShadowForTest(t, registry);
const node1 = shadow1.appendChild(document.createElement(name));
const iframe = createIFrameForTest(t);
const host2 = iframe.contentDocument.createElement('div');
const shadow2 = host2.attachShadow({mode: 'open', registry});
const node2 = shadow2.appendChild(iframe.contentDocument.createElement(name));
iframe.contentDocument.body.appendChild(host2);
class TestElement extends HTMLElement {};
registry.define(name, TestElement);
assert_true(node1 instanceof TestElement);
assert_true(node2 instanceof TestElement);
}, 'Adding definition to scoped registry affects associated shadow roots in all iframes');
test(t => {
const name = nextCustomElementName();
const newWindow = window.open('about:blank');
t.add_cleanup(() => newWindow.close());
const host = newWindow.document.createElement('div');
const shadow = host.attachShadow({mode: 'open', registry: window.customElements});
const node = shadow.appendChild(newWindow.document.createElement(name));
newWindow.document.body.appendChild(host);
class TestElement extends HTMLElement {};
window.customElements.define(name, TestElement);
assert_true(node instanceof TestElement);
}, 'Adding definition to scoped registry affects associated shadow roots in other frame trees');
test(t => {
const name = nextCustomElementName();
const registry = new CustomElementRegistry;
const shadow = attachShadowForTest(t, registry);
const node = shadow.appendChild(document.createElement(name));
shadow.host.remove();
class TestElement extends HTMLElement {};
registry.define(name, TestElement);
assert_false(node instanceof TestElement);
}, 'Adding definition to scoped registry should not upgrade disconnected elements');
test(t => {
const name = nextCustomElementName();
const registry = new CustomElementRegistry;
const doc = document.implementation.createHTMLDocument();
const host = doc.createElement('div');
const shadow = host.attachShadow({mode: 'open', registry});
const node = shadow.appendChild(doc.createElement(name));
doc.body.appendChild(host);
class TestElement extends HTMLElement {};
registry.define(name, TestElement);
assert_false(node instanceof TestElement);
}, 'Adding definition to scoped registry should not upgrade nodes in constructed documents');
test(t => {
const name = nextCustomElementName();
const iframe = createIFrameForTest(t);
const registry = new CustomElementRegistry;
const host = iframe.contentDocument.createElement('div');
const shadow = host.attachShadow({mode: 'open', registry});
const node = shadow.appendChild(iframe.contentDocument.createElement(name));
iframe.contentDocument.body.appendChild(host);
iframe.remove();
class TestElement extends HTMLElement {};
registry.define(name, TestElement);
assert_false(node instanceof TestElement);
}, 'Adding definition to scoped registry should not upgrade nodes in detached frames');
promise_test(async t => {
const name = nextCustomElementName();
const newWindow = window.open('about:blank');
t.add_cleanup(() => newWindow.close());
const host = newWindow.document.createElement('div');
const shadow = host.attachShadow({mode: 'open', registry: window.customElements});
const node = shadow.appendChild(newWindow.document.createElement(name));
newWindow.document.body.appendChild(host);
newWindow.close();
// `window.close()` is async. Wait a while until it's fully closed
await new Promise(resolve => setTimeout(resolve, 500));
class TestElement extends HTMLElement {};
window.customElements.define(name, TestElement);
assert_false(node instanceof TestElement);
}, 'Adding definition to scoped registry should not upgrade nodes in closed windows');
</script>
</body>