constructor-reentry-with-different-definition.html (4840B)
1 <!DOCTYPE html> 2 <meta name="author" title="Xiaocheng Hu" href="mailto:xiaochengh@chromium.org"> 3 <meta name="assert" content="Custom element constructors can re-enter with different definitions"> 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/969"> 6 <script src="/resources/testharness.js"></script> 7 <script src="/resources/testharnessreport.js"></script> 8 9 <div id='test-container-1'></div> 10 <div id='test-container-2'></div> 11 12 <script> 13 setup({allow_uncaught_exception : true}); 14 15 function createShadowForTest(t, customElementRegistry) { 16 const host = document.createElement('div'); 17 const shadow = host.attachShadow({mode: 'open', customElementRegistry}); 18 document.body.appendChild(host); 19 t.add_cleanup(() => host.remove()); 20 return shadow; 21 } 22 23 test(t => { 24 let needsTest = true; 25 class ReentryBeforeSuper extends HTMLElement { 26 constructor() { 27 if (needsTest) { 28 needsTest = false; 29 document.getElementById('test-container-1').innerHTML = 30 '<test-element-1></test-element-1>'; 31 } 32 super(); 33 } 34 }; 35 window.customElements.define('test-element-1', ReentryBeforeSuper); 36 37 let registry = new CustomElementRegistry; 38 registry.define('shadow-test-element-1', ReentryBeforeSuper); 39 40 let shadow = createShadowForTest(t, registry); 41 shadow.innerHTML = '<shadow-test-element-1></shadow-test-element-1>'; 42 43 let shadowElement = shadow.firstChild; 44 assert_true(shadowElement instanceof ReentryBeforeSuper); 45 assert_equals(shadowElement.localName, 'shadow-test-element-1'); 46 47 let mainDocElement = document.getElementById('test-container-1').firstChild; 48 assert_true(mainDocElement instanceof ReentryBeforeSuper); 49 assert_equals(mainDocElement.localName, 'test-element-1'); 50 }, 'Re-entry via upgrade before calling super()'); 51 52 test(t => { 53 let needsTest = true; 54 class ReentryAfterSuper extends HTMLElement { 55 constructor() { 56 super(); 57 if (needsTest) { 58 needsTest = false; 59 document.getElementById('test-container-2').innerHTML = 60 '<test-element-2></test-element-2>'; 61 } 62 } 63 }; 64 window.customElements.define('test-element-2', ReentryAfterSuper); 65 66 let registry = new CustomElementRegistry; 67 registry.define('shadow-test-element-2', ReentryAfterSuper); 68 69 let shadow = createShadowForTest(t, registry); 70 shadow.innerHTML = '<shadow-test-element-2></shadow-test-element-2>'; 71 72 let shadowElement = shadow.firstChild; 73 assert_true(shadowElement instanceof ReentryAfterSuper); 74 assert_equals(shadowElement.localName, 'shadow-test-element-2'); 75 76 let mainDocElement = document.getElementById('test-container-2').firstChild; 77 assert_true(mainDocElement instanceof ReentryAfterSuper); 78 assert_equals(mainDocElement.localName, 'test-element-2'); 79 }, 'Re-entry via upgrade after calling super()'); 80 81 test(t => { 82 let needsTest = true; 83 let elementByNestedCall; 84 class ReentryByDirectCall extends HTMLElement { 85 constructor() { 86 if (needsTest) { 87 needsTest = false; 88 elementByNestedCall = new ReentryByDirectCall; 89 } 90 super(); 91 } 92 } 93 window.customElements.define('test-element-3', ReentryByDirectCall); 94 95 let registry = new CustomElementRegistry; 96 registry.define('shadow-test-element-3', ReentryByDirectCall); 97 98 let shadow = createShadowForTest(t, registry); 99 shadow.innerHTML = '<shadow-test-element-3></shadow-test-element-3>'; 100 101 let shadowElement = shadow.firstChild; 102 assert_true(shadowElement instanceof ReentryByDirectCall); 103 assert_equals(shadowElement.localName, 'shadow-test-element-3'); 104 105 // Nested constructor call makes the following `super()` fail, and we should 106 // end up creating only one element. 107 assert_equals(elementByNestedCall, shadowElement); 108 }, 'Re-entry via direct constructor call before calling super()'); 109 110 test(t => { 111 let needsTest = true; 112 let elementByNestedCall; 113 class ReentryByDirectCall extends HTMLElement { 114 constructor() { 115 super(); 116 if (needsTest) { 117 needsTest = false; 118 elementByNestedCall = new ReentryByDirectCall; 119 } 120 } 121 } 122 window.customElements.define('test-element-4', ReentryByDirectCall); 123 124 let registry = new CustomElementRegistry; 125 registry.define('shadow-test-element-4', ReentryByDirectCall); 126 127 let shadow = createShadowForTest(t, registry); 128 shadow.innerHTML = '<shadow-test-element-4></shadow-test-element-4>'; 129 130 let shadowElement = shadow.firstChild; 131 assert_true(shadowElement instanceof ReentryByDirectCall); 132 assert_equals(shadowElement.localName, 'shadow-test-element-4'); 133 134 // Nested constructor call should be blocked. 135 assert_false(elementByNestedCall instanceof ReentryByDirectCall); 136 }, 'Re-entry via direct constructor call after calling super()'); 137 </script>