scoped-registry-define-upgrade-criteria.html (9386B)
1 <!DOCTYPE html> 2 <title>Tests which nodes are upgraded after adding a scoped custom element definition</title> 3 <meta name="author" title="Xiaocheng Hu" href="mailto:xiaochengh@chromium.org"> 4 <link rel="help" href="https://wicg.github.io/webcomponents/proposals/Scoped-Custom-Element-Registries"> 5 <link rel="help" href="https://github.com/WICG/webcomponents/issues/923"> 6 <script src="/resources/testharness.js"></script> 7 <script src="/resources/testharnessreport.js"></script> 8 9 <body> 10 11 <script> 12 function attachShadowForTest(t, customElementRegistry) { 13 const host = document.createElement('div'); 14 const shadow = host.attachShadow({mode: 'open', customElementRegistry}); 15 document.body.appendChild(host); 16 t.add_cleanup(() => host.remove()); 17 return shadow; 18 } 19 20 function createIFrameForTest(t) { 21 const iframe = document.createElement('iframe'); 22 document.body.appendChild(iframe); 23 if (!iframe.contentDocument.body) { 24 iframe.contentDocument.body = iframe.contentDocument.createElement('body'); 25 } 26 t.add_cleanup(() => iframe.remove()); 27 return iframe; 28 } 29 30 let definitionCount = 0; 31 function nextCustomElementName() { 32 return `test-element-${++definitionCount}`; 33 } 34 35 function insertNewElementIntoShadow(shadow, name) { 36 shadow.innerHTML = `<${name}></${name}>`; 37 return shadow.querySelector(name); 38 } 39 40 test(t => { 41 const name = nextCustomElementName(); 42 document.body.appendChild(document.createElement(name)); 43 44 const registry = new CustomElementRegistry; 45 const shadow = attachShadowForTest(t, registry); 46 insertNewElementIntoShadow(shadow, name); 47 48 class TestElement extends HTMLElement {}; 49 customElements.define(name, TestElement); 50 51 assert_true(document.querySelector(name) instanceof TestElement); 52 assert_false(shadow.querySelector(name) instanceof TestElement); 53 }, 'Adding definition to global registry should not affect shadow roots using scoped registry'); 54 55 test(t => { 56 const name = nextCustomElementName(); 57 58 const shadow1 = attachShadowForTest(t, customElements); 59 shadow1.appendChild(document.createElement(name)); 60 61 const shadow2 = attachShadowForTest(t); 62 shadow2.appendChild(document.createElement(name)); 63 64 class TestElement extends HTMLElement {}; 65 customElements.define(name, TestElement); 66 67 assert_true(shadow1.querySelector(name) instanceof TestElement); 68 assert_true(shadow2.querySelector(name) instanceof TestElement); 69 }, 'Adding definition to global registry should affect shadow roots also using global registry'); 70 71 test(t => { 72 const name = nextCustomElementName(); 73 74 const registry = new CustomElementRegistry; 75 const shadow1 = attachShadowForTest(t, registry); 76 insertNewElementIntoShadow(shadow1, name); 77 78 const shadow2 = attachShadowForTest(t, registry); 79 insertNewElementIntoShadow(shadow2, name); 80 81 class TestElement extends HTMLElement {}; 82 registry.define(name, TestElement); 83 84 assert_true(shadow1.querySelector(name) instanceof TestElement); 85 assert_true(shadow2.querySelector(name) instanceof TestElement); 86 }, 'Adding definition to scoped registry should affect all associated shadow roots'); 87 88 test(t => { 89 const name = nextCustomElementName(); 90 document.body.appendChild(document.createElement(name)); 91 92 const registry = new CustomElementRegistry; 93 const shadow = attachShadowForTest(t, registry); 94 insertNewElementIntoShadow(shadow, name); 95 96 class TestElement extends HTMLElement {}; 97 registry.define(name, TestElement); 98 99 assert_false(document.querySelector(name) instanceof TestElement); 100 assert_true(shadow.querySelector(name) instanceof TestElement); 101 }, 'Adding definition to scoped registry should not affect document tree scope'); 102 103 test(t => { 104 const name = nextCustomElementName(); 105 106 const registry = new CustomElementRegistry; 107 const shadow1 = attachShadowForTest(t, registry); 108 insertNewElementIntoShadow(shadow1, name); 109 110 const shadow2 = attachShadowForTest(t, new CustomElementRegistry); 111 insertNewElementIntoShadow(shadow2, name); 112 113 const shadow3 = attachShadowForTest(t); 114 insertNewElementIntoShadow(shadow3, name); 115 116 class TestElement extends HTMLElement {}; 117 registry.define(name, TestElement); 118 119 assert_true(shadow1.querySelector(name) instanceof TestElement); 120 assert_false(shadow2.querySelector(name) instanceof TestElement); 121 assert_false(shadow3.querySelector(name) instanceof TestElement); 122 }, 'Adding definition to scoped registry should not affect shadow roots using other registries'); 123 124 test(t => { 125 const name = nextCustomElementName(); 126 const registry = new CustomElementRegistry; 127 const node = document.body.appendChild(document.createElement(name, {customElementRegistry: registry})); 128 const shadow = attachShadowForTest(t, registry); 129 shadow.appendChild(node); 130 131 class TestElement extends HTMLElement {}; 132 registry.define(name, TestElement); 133 134 assert_true(node instanceof TestElement); 135 }, 'Adding definition to scoped registry should upgrade nodes even after the node is moved into a separate shadow tree.'); 136 137 test(t => { 138 const name = nextCustomElementName(); 139 140 const registry = new CustomElementRegistry; 141 const shadow1 = attachShadowForTest(t, registry); 142 const node = insertNewElementIntoShadow(shadow1, name); 143 144 const shadow2 = attachShadowForTest(t, new CustomElementRegistry); 145 shadow2.appendChild(node); 146 147 class TestElement extends HTMLElement {}; 148 registry.define(name, TestElement); 149 150 assert_true(node instanceof TestElement); 151 }, 'Adding definition to scoped registry should upgrade nodes even after the node is moved to a separate shadow tree using a different registry.'); 152 153 test(t => { 154 const name = nextCustomElementName(); 155 156 const registry = new CustomElementRegistry; 157 const shadow1 = attachShadowForTest(t, registry); 158 const node1 = insertNewElementIntoShadow(shadow1, name); 159 160 const iframe = createIFrameForTest(t); 161 const host2 = iframe.contentDocument.createElement('div'); 162 const shadow2 = host2.attachShadow({mode: 'open', customElementRegistry: registry}); 163 const node2 = insertNewElementIntoShadow(shadow2, name); 164 iframe.contentDocument.body.appendChild(host2); 165 166 class TestElement extends HTMLElement {}; 167 registry.define(name, TestElement); 168 169 assert_true(node1 instanceof TestElement); 170 assert_true(node2 instanceof TestElement); 171 }, 'Adding definition to scoped registry affects associated shadow roots in all iframes'); 172 173 test(t => { 174 const name = nextCustomElementName(); 175 176 const registry = new CustomElementRegistry; 177 178 const newWindow = window.open('about:blank'); 179 t.add_cleanup(() => newWindow.close()); 180 181 const host = newWindow.document.createElement('div'); 182 const shadow = host.attachShadow({mode: 'open', customElementRegistry: registry}); 183 const node = insertNewElementIntoShadow(shadow, name); 184 newWindow.document.body.appendChild(host); 185 186 class TestElement extends HTMLElement {}; 187 registry.define(name, TestElement); 188 189 assert_true(node instanceof TestElement); 190 }, 'Adding definition to scoped registry affects associated shadow roots in other frame trees'); 191 192 test(t => { 193 const name = nextCustomElementName(); 194 195 const registry = new CustomElementRegistry; 196 const shadow = attachShadowForTest(t, registry); 197 const node = insertNewElementIntoShadow(shadow, name); 198 shadow.host.remove(); 199 200 class TestElement extends HTMLElement {}; 201 registry.define(name, TestElement); 202 203 assert_false(node instanceof TestElement); 204 }, 'Adding definition to scoped registry should not upgrade disconnected elements'); 205 206 test(t => { 207 const name = nextCustomElementName(); 208 209 const registry = new CustomElementRegistry; 210 const doc = document.implementation.createHTMLDocument(); 211 const host = doc.createElement('div'); 212 const shadow = host.attachShadow({mode: 'open', registry}); 213 const node = insertNewElementIntoShadow(shadow, name); 214 doc.body.appendChild(host); 215 216 class TestElement extends HTMLElement {}; 217 registry.define(name, TestElement); 218 219 assert_false(node instanceof TestElement); 220 }, 'Adding definition to scoped registry should not upgrade nodes in constructed documents'); 221 222 test(t => { 223 const name = nextCustomElementName(); 224 225 const iframe = createIFrameForTest(t); 226 const registry = new CustomElementRegistry; 227 const host = iframe.contentDocument.createElement('div'); 228 const shadow = host.attachShadow({mode: 'open', registry}); 229 const node = insertNewElementIntoShadow(shadow, name); 230 iframe.contentDocument.body.appendChild(host); 231 232 iframe.remove(); 233 234 class TestElement extends HTMLElement {}; 235 registry.define(name, TestElement); 236 237 assert_false(node instanceof TestElement); 238 }, 'Adding definition to scoped registry should not upgrade nodes in detached frames'); 239 240 promise_test(async t => { 241 const name = nextCustomElementName(); 242 243 const newWindow = window.open('about:blank'); 244 t.add_cleanup(() => newWindow.close()); 245 246 const host = newWindow.document.createElement('div'); 247 const shadow = host.attachShadow({mode: 'open', registry: window.customElements}); 248 const node = insertNewElementIntoShadow(shadow, name); 249 newWindow.document.body.appendChild(host); 250 251 newWindow.close(); 252 253 // `window.close()` is async. Wait a while until it's fully closed 254 await new Promise(resolve => t.step_timeout(resolve, 500)); 255 256 class TestElement extends HTMLElement {}; 257 window.customElements.define(name, TestElement); 258 259 assert_false(node instanceof TestElement); 260 }, 'Adding definition to scoped registry should not upgrade nodes in closed windows'); 261 </script>