Node-cloneNode.html (9126B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title>Custom Elements: Upgrading</title> 5 <meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> 6 <meta name="assert" content="Node.prototype.cloneNode should upgrade a custom element"> 7 <link rel="help" href="https://html.spec.whatwg.org/#upgrades"> 8 <script src="/resources/testharness.js"></script> 9 <script src="/resources/testharnessreport.js"></script> 10 <script src="../resources/custom-elements-helpers.js"></script> 11 </head> 12 <body> 13 <div id="log"></div> 14 <script> 15 setup({allow_uncaught_exception:true}); 16 17 test(function () { 18 class MyCustomElement extends HTMLElement {} 19 customElements.define('my-custom-element', MyCustomElement); 20 21 var instance = document.createElement('my-custom-element'); 22 assert_true(instance instanceof HTMLElement); 23 assert_true(instance instanceof MyCustomElement); 24 25 var clone = instance.cloneNode(false); 26 assert_not_equals(instance, clone); 27 assert_true(clone instanceof HTMLElement, 28 'A cloned custom element must be an instance of HTMLElement'); 29 assert_true(clone instanceof MyCustomElement, 30 'A cloned custom element must be an instance of the custom element'); 31 }, 'Node.prototype.cloneNode(false) must be able to clone a custom element'); 32 33 test(function () { 34 class AutonomousCustomElement extends HTMLElement {}; 35 class IsCustomElement extends HTMLElement {}; 36 37 customElements.define('autonomous-custom-element', AutonomousCustomElement); 38 customElements.define('is-custom-element', IsCustomElement); 39 40 var instance = document.createElement('autonomous-custom-element', { is: "is-custom-element"}); 41 assert_true(instance instanceof HTMLElement); 42 assert_true(instance instanceof AutonomousCustomElement); 43 44 var clone = instance.cloneNode(false); 45 assert_not_equals(instance, clone); 46 assert_true(clone instanceof HTMLElement, 47 'A cloned custom element must be an instance of HTMLElement'); 48 assert_true(clone instanceof AutonomousCustomElement, 49 'A cloned custom element must be an instance of the custom element'); 50 }, 'Node.prototype.cloneNode(false) must be able to clone as a autonomous custom element when it contains is attribute'); 51 52 test_with_window(function (contentWindow) { 53 var contentDocument = contentWindow.document; 54 class MyCustomElement extends contentWindow.HTMLElement {} 55 contentWindow.customElements.define('my-custom-element', MyCustomElement); 56 57 var instance = contentDocument.createElement('my-custom-element'); 58 assert_true(instance instanceof contentWindow.HTMLElement); 59 assert_true(instance instanceof MyCustomElement); 60 61 var clone = instance.cloneNode(false); 62 assert_not_equals(instance, clone); 63 assert_true(clone instanceof contentWindow.HTMLElement, 64 'A cloned custom element must be an instance of HTMLElement'); 65 assert_true(clone instanceof MyCustomElement, 66 'A cloned custom element must be an instance of the custom element'); 67 }, 'Node.prototype.cloneNode(false) must be able to clone a custom element inside an iframe'); 68 69 test_with_window(function (contentWindow) { 70 var contentDocument = contentWindow.document; 71 class MyCustomElement extends contentWindow.HTMLElement { } 72 contentWindow.customElements.define('my-custom-element', MyCustomElement); 73 74 var instance = contentDocument.createElement('my-custom-element'); 75 var container = contentDocument.createElement('div'); 76 container.appendChild(instance); 77 78 var containerClone = container.cloneNode(true); 79 assert_true(containerClone instanceof contentWindow.HTMLDivElement); 80 81 var clone = containerClone.firstChild; 82 assert_not_equals(instance, clone); 83 assert_true(clone instanceof contentWindow.HTMLElement, 84 'A cloned custom element must be an instance of HTMLElement'); 85 assert_true(clone instanceof MyCustomElement, 86 'A cloned custom element must be an instance of the custom element'); 87 }, 'Node.prototype.cloneNode(true) must be able to clone a descendent custom element'); 88 89 test_with_window(function (contentWindow) { 90 var parentNodeInConstructor; 91 var previousSiblingInConstructor; 92 var nextSiblingInConstructor; 93 class MyCustomElement extends contentWindow.HTMLElement { 94 constructor() { 95 super(); 96 parentNodeInConstructor = this.parentNode; 97 previousSiblingInConstructor = this.previousSibling; 98 nextSiblingInConstructor = this.nextSibling; 99 } 100 } 101 contentWindow.customElements.define('my-custom-element', MyCustomElement); 102 103 var contentDocument = contentWindow.document; 104 var instance = contentDocument.createElement('my-custom-element'); 105 var siblingBeforeInstance = contentDocument.createElement('b'); 106 var siblingAfterInstance = contentDocument.createElement('a'); 107 var container = contentDocument.createElement('div'); 108 container.appendChild(siblingBeforeInstance); 109 container.appendChild(instance); 110 container.appendChild(siblingAfterInstance); 111 112 var containerClone = container.cloneNode(true); 113 114 assert_equals(parentNodeInConstructor, containerClone, 115 'An upgraded element must have its parentNode set before the custom element constructor is called'); 116 assert_equals(previousSiblingInConstructor, containerClone.firstChild, 117 'An upgraded element must have its previousSibling set before the custom element constructor is called'); 118 assert_equals(nextSiblingInConstructor, containerClone.lastChild, 119 'An upgraded element must have its nextSibling set before the custom element constructor is called'); 120 }, 'Node.prototype.cloneNode(true) must set parentNode, previousSibling, and nextSibling before upgrading custom elements'); 121 122 // The error reporting isn't clear yet when multiple globals involved in custom 123 // element, see w3c/webcomponents#635, so using test_with_window is not a good 124 // idea here. 125 test(function () { 126 class MyCustomElement extends HTMLElement { 127 constructor(doNotCreateItself) { 128 super(); 129 if (!doNotCreateItself) 130 new MyCustomElement(true); 131 } 132 } 133 customElements.define('my-custom-element-constructed-after-super', MyCustomElement); 134 135 var instance = new MyCustomElement(false); 136 var uncaughtError; 137 window.onerror = function (message, url, lineNumber, columnNumber, error) { uncaughtError = error; return true; } 138 instance.cloneNode(false); 139 assert_equals(uncaughtError.name, 'TypeError'); 140 }, 'HTMLElement constructor must throw an TypeError when the top of the construction stack is marked AlreadyConstructed' 141 + ' due to a custom element constructor constructing itself after super() call'); 142 143 test(function () { 144 class MyCustomElement extends HTMLElement { 145 constructor(doNotCreateItself) { 146 if (!doNotCreateItself) 147 new MyCustomElement(true); 148 super(); 149 } 150 } 151 customElements.define('my-custom-element-constructed-before-super', MyCustomElement); 152 153 var instance = new MyCustomElement(false); 154 var uncaughtError; 155 window.onerror = function (message, url, lineNumber, columnNumber, error) { uncaughtError = error; return true; } 156 instance.cloneNode(false); 157 assert_equals(uncaughtError.name, 'TypeError'); 158 }, 'HTMLElement constructor must throw an TypeError when the top of the construction stack is marked AlreadyConstructed' 159 + ' due to a custom element constructor constructing itself before super() call'); 160 161 test(function () { 162 var returnSpan = false; 163 class MyCustomElement extends HTMLElement { 164 constructor() { 165 super(); 166 if (returnSpan) 167 return document.createElement('span'); 168 } 169 } 170 customElements.define('my-custom-element-return-another', MyCustomElement); 171 172 var instance = new MyCustomElement(false); 173 returnSpan = true; 174 var uncaughtError; 175 window.onerror = function (message, url, lineNumber, columnNumber, error) { uncaughtError = error; return true; } 176 instance.cloneNode(false); 177 assert_equals(uncaughtError.name, 'TypeError'); 178 }, 'Upgrading a custom element must throw TypeError when the custom element\'s constructor returns another element'); 179 180 test(function () { 181 var instance = document.createElement('my-custom-element-throw-exception'); 182 document.body.appendChild(instance); 183 184 var calls = []; 185 class MyCustomElement extends HTMLElement { 186 constructor() { 187 super(); 188 calls.push(this); 189 throw 'bad'; 190 } 191 } 192 193 var uncaughtError; 194 window.onerror = function (message, url, lineNumber, columnNumber, error) { uncaughtError = error; return true; } 195 customElements.define('my-custom-element-throw-exception', MyCustomElement); 196 assert_equals(uncaughtError, 'bad'); 197 198 assert_array_equals(calls, [instance]); 199 document.body.removeChild(instance); 200 document.body.appendChild(instance); 201 assert_array_equals(calls, [instance]); 202 }, 'Inserting an element must not try to upgrade a custom element when it had already failed to upgrade once'); 203 204 </script> 205 </body> 206 </html>