test_xul_custom_element.xhtml (19589B)
1 <?xml version="1.0"?> 2 <?xml-stylesheet href="chrome://global/skin" type="text/css"?> 3 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> 4 5 <window title="XUL Custom Elements" 6 xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" 7 onload="runTest();"> 8 <title>XUL Custom Elements</title> 9 10 <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> 11 12 <script type="application/javascript"> 13 <![CDATA[ 14 SimpleTest.waitForExplicitFinish(); 15 16 var gXULDOMParser = new DOMParser(); 17 gXULDOMParser.forceEnableXULXBL(); 18 19 function parseXULToFragment(str) { 20 let doc = gXULDOMParser.parseFromSafeString(` 21 <box xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">${str}</box>`, 22 "application/xml"); 23 // We use a range here so that we don't access the inner DOM elements from 24 // JavaScript before they are imported and inserted into a document. 25 let range = doc.createRange(); 26 range.selectNodeContents(doc.firstChild); 27 return range.extractContents(); 28 } 29 30 class TestCustomElement extends XULElement { 31 constructor() { 32 super(); 33 34 this.attachShadow({mode: "open"}); 35 } 36 37 connectedCallback() { 38 this.textContent = "foo"; 39 } 40 } 41 42 customElements.define("test-custom-element", TestCustomElement); 43 44 class TestWithoutDash extends XULElement { } 45 customElements.define("testwithoutdash", TestWithoutDash); 46 47 class TestWithoutDashExtended extends TestWithoutDash { 48 constructor() { 49 super(); 50 } 51 52 connectedCallback() { 53 this.textContent = "quux"; 54 } 55 } 56 customElements.define("testwithoutdash-extended", TestWithoutDashExtended, { extends: "testwithoutdash" }); 57 58 class TestCustomBuiltInElement extends XULElement { 59 constructor() { 60 super(); 61 } 62 63 connectedCallback() { 64 this.textContent = "baz"; 65 } 66 } 67 customElements.define("test-built-in-element", 68 TestCustomBuiltInElement, { extends: "axulelement" }); 69 70 class TestPopupExtendElement extends XULPopupElement { 71 constructor() { 72 super(); 73 } 74 75 connectedCallback() { 76 this.textContent = "quuz"; 77 } 78 } 79 customElements.define("test-popup-extend", 80 TestPopupExtendElement, { extends: "menupopup" }); 81 82 class TestCustomElement3 extends XULElement { } 83 84 customElements.setElementCreationCallback( 85 "test-custom-element-3", () => customElements.define("test-custom-element-3", TestCustomElement3)); 86 87 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; 88 89 function basicCustomElementCreate() { 90 let element = document.createElementNS(XUL_NS, "test-custom-element"); 91 ok(element.shadowRoot, "Shadow DOM works even with pref off"); 92 document.querySelector("#content").appendChild(element); 93 is(element.textContent, "foo", "Should have set the textContent"); 94 ok(element instanceof TestCustomElement, "Should be an instance of TestCustomElement"); 95 96 let element2 = element.cloneNode(false); 97 is(element2.textContent, "", "Shouldn't have cloned the textContent"); 98 document.querySelector("#content").appendChild(element2); 99 is(element2.textContent, "foo", "Should have set the textContent"); 100 ok(element2 instanceof TestCustomElement, "Should be an instance of TestCustomElement"); 101 102 let element3 = new TestCustomElement(); 103 is(element3.localName, "test-custom-element", "Should see the right tag"); 104 is(element3.textContent, "", "Shouldn't have been inserted yet"); 105 is(element3.namespaceURI, XUL_NS, "Should have set the right namespace"); 106 document.querySelector("#content").appendChild(element3); 107 is(element3.textContent, "foo", "Should have set the textContent"); 108 ok(element3 instanceof TestCustomElement, "Should be an instance of TestCustomElement"); 109 110 document.querySelector("#content").appendChild(parseXULToFragment(`<test-custom-element />`)); 111 let element4 = document.querySelector("#content").lastChild; 112 is(element4.localName, "test-custom-element", "Should see the right tag"); 113 is(element4.namespaceURI, XUL_NS, "Should have set the right namespace"); 114 is(element4.textContent, "foo", "Should have set the textContent"); 115 ok(element4 instanceof TestCustomElement, "Should be an instance of TestCustomElement"); 116 } 117 118 function parserBasicElementUpgrade() { 119 let element = document.getElementById("element4"); 120 is(element.textContent, "foo", 121 "Parser should have instantiated the custom element."); 122 ok(element instanceof TestCustomElement, "Should be an instance of TestCustomElement"); 123 } 124 125 function tagNameWithoutDash() { 126 let element = document.getElementById("element5"); 127 ok(element instanceof TestWithoutDash, "Should be an instance of TestWithoutDash"); 128 } 129 130 function upgradeAfterDefine() { 131 class TestCustomElement1 extends XULElement { 132 constructor() { 133 super(); 134 } 135 136 connectedCallback() { 137 this.textContent = "bar"; 138 } 139 } 140 141 let element = document.createElementNS(XUL_NS, "test-custom-element-1"); 142 ok(!(element instanceof TestCustomElement1), "Should not be an instance of TestCustomElement1"); 143 customElements.define("test-custom-element-1", TestCustomElement1); 144 ok(!(element instanceof TestCustomElement1), "Should not be an instance of TestCustomElement1"); 145 document.querySelector("#content").appendChild(element); 146 ok(element instanceof TestCustomElement1, "Should be upgraded to an instance of TestCustomElement1"); 147 is(element.textContent, "bar", "Should have set the textContent"); 148 } 149 150 function basicElementCreateBuiltIn() { 151 let element = document.createElementNS(XUL_NS, "axulelement", { is: "test-built-in-element" }); 152 ok(element instanceof TestCustomBuiltInElement, "Should be an instance of TestCustomBuiltInElement"); 153 is(element.getAttribute("is"), null, "The |is| attribute of the created element should not be the extended type."); 154 document.querySelector("#content").appendChild(element); 155 is(element.textContent, "baz", "Should have set the textContent"); 156 157 let element2 = element.cloneNode(false); 158 is(element2.localName, "axulelement", "Should see the right tag"); 159 is(element2.getAttribute("is"), null, "The |is| attribute of the created element should not be the extended type."); 160 is(element2.textContent, "", "Shouldn't have cloned the textContent"); 161 document.querySelector("#content").appendChild(element2); 162 is(element2.textContent, "baz", "Should have set the textContent"); 163 ok(element2 instanceof TestCustomBuiltInElement, "Should be an instance of TestCustomBuiltInElement"); 164 165 let element3 = new TestCustomBuiltInElement(); 166 is(element3.localName, "axulelement", "Should see the right tag"); 167 is(element3.textContent, "", "Shouldn't have been inserted yet"); 168 is(element3.namespaceURI, XUL_NS, "Should have set the right namespace"); 169 document.querySelector("#content").appendChild(element3); 170 is(element3.textContent, "baz", "Should have set the textContent"); 171 ok(element3 instanceof TestCustomBuiltInElement, "Should be an instance of TestCustomBuiltInElement"); 172 173 document.querySelector("#content").appendChild(parseXULToFragment(`<axulelement is="test-built-in-element" />`)) 174 let element4 = document.querySelector("#content").lastChild; 175 is(element4.localName, "axulelement", "Should see the right tag"); 176 is(element4.namespaceURI, XUL_NS, "Should have set the right namespace"); 177 is(element4.textContent, "baz", "Should have set the textContent"); 178 ok(element4 instanceof TestCustomBuiltInElement, "Should be an instance of TestCustomBuiltInElement"); 179 } 180 181 function parserBasicElementUpgradeBuiltIn() { 182 let element = document.getElementById("element6"); 183 is(element.textContent, "baz", 184 "Parser should have instantiated the custom element."); 185 ok(element instanceof TestCustomBuiltInElement, "Should be an instance of TestCustomBuiltInElement"); 186 } 187 188 function subclassElementCreateBuiltIn() { 189 let element = document.createElementNS(XUL_NS, "menupopup", { is: "test-popup-extend" }); 190 ok(element instanceof TestPopupExtendElement, "Should be an instance of TestPopupExtendElement"); 191 is(element.getAttribute("is"), null, "The |is| attribute of the created element should not be the extended type."); 192 document.querySelector("#content").appendChild(element); 193 is(element.textContent, "quuz", "Should have set the textContent"); 194 195 let element2 = element.cloneNode(false); 196 is(element2.localName, "menupopup", "Should see the right tag"); 197 is(element2.getAttribute("is"), null, "The |is| attribute of the created element should not be the extended type."); 198 is(element2.textContent, "", "Shouldn't have cloned the textContent"); 199 document.querySelector("#content").appendChild(element2); 200 is(element2.textContent, "quuz", "Should have set the textContent"); 201 ok(element2 instanceof TestPopupExtendElement, "Should be an instance of TestPopupExtendElement"); 202 203 let element3 = new TestPopupExtendElement(); 204 is(element3.localName, "menupopup", "Should see the right tag"); 205 is(element3.textContent, "", "Shouldn't have been inserted yet"); 206 is(element3.namespaceURI, XUL_NS, "Should have set the right namespace"); 207 document.querySelector("#content").appendChild(element3); 208 is(element3.textContent, "quuz", "Should have set the textContent"); 209 ok(element3 instanceof TestPopupExtendElement, "Should be an instance of TestPopupExtendElement"); 210 211 document.querySelector("#content").appendChild(parseXULToFragment(`<menupopup is="test-popup-extend" />`)) 212 let element4 = document.querySelector("#content").lastChild; 213 is(element4.localName, "menupopup", "Should see the right tag"); 214 is(element4.namespaceURI, XUL_NS, "Should have set the right namespace"); 215 is(element4.textContent, "quuz", "Should have set the textContent"); 216 ok(element4 instanceof TestPopupExtendElement, "Should be an instance of TestPopupExtendElement"); 217 } 218 219 function parserSubclassElementUpgradeBuiltIn() { 220 let element = document.getElementById("element7"); 221 is(element.textContent, "quuz", 222 "Parser should have instantiated the custom element."); 223 ok(element instanceof TestPopupExtendElement, "Should be an instance of TestPopupExtendElement"); 224 } 225 226 function upgradeAfterDefineBuiltIn() { 227 class TestCustomBuiltInElement1 extends XULElement { 228 constructor() { 229 super(); 230 } 231 232 connectedCallback() { 233 this.textContent = "qux"; 234 } 235 } 236 let element = document.createElementNS(XUL_NS, "axulelement", { is: "test-built-in-element-1" }); 237 ok(!(element instanceof TestCustomBuiltInElement1), "Should not be an instance of TestCustomBuiltInElement1"); 238 customElements.define("test-built-in-element-1", 239 TestCustomBuiltInElement1, { extends: "axulelement" }); 240 ok(!(element instanceof TestCustomBuiltInElement1), "Should not be an instance of TestCustomBuiltInElement1"); 241 document.querySelector("#content").appendChild(element); 242 ok(element instanceof TestCustomBuiltInElement1, "Should be upgraded to an instance of TestCustomBuiltInElement1"); 243 is(element.textContent, "qux", "Should have set the textContent"); 244 } 245 246 function throwForInvalidBuiltInName() { 247 try { 248 // <axulelement is="testwithoutdashbuiltin" /> is not allowed; 249 // built-in type names need dashes. 250 customElements.define( 251 "testwithoutdashbuiltin", class extends XULElement {}, { extends: "axulelement" }); 252 ok(false, "Built-in type name without dash should be rejected."); 253 } catch (e) { 254 ok(true, "Built-in type name without dash is rejected."); 255 } 256 try { 257 // <test-built-in-element-2 is="test-custom-element-2" /> is not allowed; 258 // built-in type tag names forbid dashes 259 customElements.define( 260 "test-built-in-element-2", class extends XULElement {}, { extends: "test-custom-element-2" }); 261 ok(false, "Extending from a name with dash should be rejected."); 262 } catch (e) { 263 ok(true, "Extending from a name with dash is rejected."); 264 } 265 } 266 267 function extendingWithoutDashCustomElement() { 268 let element = document.createElementNS(XUL_NS, "testwithoutdash", { is: "testwithoutdash-extended" }); 269 ok(element instanceof TestWithoutDashExtended, "Should be an instance of TestWithoutDashExtended"); 270 ok(element instanceof TestWithoutDash, "Should be an instance of TestWithoutDash"); 271 is(element.getAttribute("is"), null, "The |is| attribute of the created element should not be the extended type."); 272 document.querySelector("#content").appendChild(element); 273 is(element.textContent, "quux", "Should have set the textContent"); 274 275 let element2 = element.cloneNode(false); 276 is(element2.localName, "testwithoutdash", "Should see the right tag"); 277 is(element2.getAttribute("is"), null, "The |is| attribute of the created element should not be the extended type."); 278 is(element2.textContent, "", "Shouldn't have cloned the textContent"); 279 document.querySelector("#content").appendChild(element2); 280 is(element2.textContent, "quux", "Should have set the textContent"); 281 ok(element2 instanceof TestWithoutDashExtended, "Should be an instance of TestWithoutDashExtended"); 282 ok(element2 instanceof TestWithoutDash, "Should be an instance of TestWithoutDash"); 283 284 let element3 = new TestWithoutDashExtended(); 285 is(element3.localName, "testwithoutdash", "Should see the right tag"); 286 is(element3.textContent, "", "Shouldn't have been inserted yet"); 287 is(element3.namespaceURI, XUL_NS, "Should have set the right namespace"); 288 document.querySelector("#content").appendChild(element3); 289 is(element3.textContent, "quux", "Should have set the textContent"); 290 ok(element3 instanceof TestWithoutDashExtended, "Should be an instance of TestWithoutDashExtended"); 291 ok(element3 instanceof TestWithoutDash, "Should be an instance of TestWithoutDash"); 292 293 document.querySelector("#content").appendChild(parseXULToFragment(`<testwithoutdash is="testwithoutdash-extended" />`)) 294 let element4 = document.querySelector("#content").lastChild; 295 is(element4.localName, "testwithoutdash", "Should see the right tag"); 296 is(element4.namespaceURI, XUL_NS, "Should have set the right namespace"); 297 is(element4.textContent, "quux", "Should have set the textContent"); 298 ok(element4 instanceof TestWithoutDashExtended, "Should be an instance of TestWithoutDashExtended"); 299 ok(element4 instanceof TestWithoutDash, "Should be an instance of TestWithoutDash"); 300 } 301 302 function nonCustomElementCreate() { 303 // All of these should be created as plain XUL elements without hitting 304 // any assertions. 305 let elements = [ 306 document.createElementNS(XUL_NS, "axulelement", { is: "test-custom-element" }), 307 document.createElementNS(XUL_NS, "axulelement", { is: "testwithoutdash" }), 308 document.createElementNS(XUL_NS, "axulelement", { is: "test-custom-element-1" }), 309 document.createElementNS(XUL_NS, "name-with-dash", { is: "name-with-dash" }), 310 document.createElementNS(XUL_NS, "name-with-dash", { is: "another-name-with-dash" }), 311 document.createElementNS(XUL_NS, "testwithoutdash-extended"), 312 document.createElementNS(XUL_NS, "test-built-in-element"), 313 document.createElementNS(XUL_NS, "test-popup-extend"), 314 document.createElementNS(XUL_NS, "test-built-in-element-1")]; 315 316 for (let element of elements) { 317 is(Object.getPrototypeOf(element), XULElement.prototype, 318 `<${element.localName} is="${element.getAttribute("is")}" /> should not be a custom element.`); 319 } 320 } 321 322 function testSetElementCreationballbackInDocument() { 323 let element = document.getElementById("element8"); 324 ok(element instanceof TestCustomElement3, "Should be an instance of TestCustomElement3"); 325 } 326 327 function setElementCreationCallbackCreate() { 328 class TestCustomElement4 extends XULElement {} 329 customElements.setElementCreationCallback( 330 "test-custom-element-4", () => customElements.define("test-custom-element-4", TestCustomElement4)); 331 332 let element = document.createElementNS(XUL_NS, "test-custom-element-4"); 333 ok(element instanceof TestCustomElement4, "Should be an instance of TestCustomElement4"); 334 335 class TestCustomElement5 extends XULElement {} 336 customElements.setElementCreationCallback( 337 "test-custom-element-5", () => { 338 ok(true, "test-custom-element-5 callback called"); 339 customElements.define("test-custom-element-5", TestCustomElement5); 340 }); 341 342 document.querySelector("#content").appendChild(parseXULToFragment(`<test-custom-element-5 />`)); 343 let element1 = document.querySelector("#content").lastChild; 344 ok(element1 instanceof TestCustomElement5, "Should be an instance of TestCustomElement5"); 345 } 346 347 function testSetElementCreationCallbackExistsBeforeCall() { 348 class TestCustomElement6 extends XULElement {} 349 document.querySelector("#content").appendChild(parseXULToFragment(`<test-custom-element-6 />`)); 350 351 let element = document.querySelector("#content").lastChild; 352 ok(!(element instanceof TestCustomElement6), "Is not a TestCustomElement6 when DOM parsed"); 353 354 customElements.setElementCreationCallback( 355 "test-custom-element-6", 356 () => customElements.define("test-custom-element-6", TestCustomElement6) 357 ); 358 ok(element instanceof TestCustomElement6, "setElementCreationCallback called automatically to upgrade candidate component"); 359 } 360 361 function runTest() { 362 basicCustomElementCreate(); 363 parserBasicElementUpgrade(); 364 365 tagNameWithoutDash(); 366 upgradeAfterDefine(); 367 368 basicElementCreateBuiltIn(); 369 parserBasicElementUpgradeBuiltIn(); 370 371 subclassElementCreateBuiltIn(); 372 parserSubclassElementUpgradeBuiltIn(); 373 374 upgradeAfterDefineBuiltIn(); 375 376 throwForInvalidBuiltInName(); 377 extendingWithoutDashCustomElement(); 378 379 nonCustomElementCreate(); 380 381 testSetElementCreationballbackInDocument(); 382 setElementCreationCallbackCreate(); 383 384 testSetElementCreationCallbackExistsBeforeCall(); 385 386 SimpleTest.finish(); 387 } 388 ]]> 389 </script> 390 391 <body xmlns="http://www.w3.org/1999/xhtml"> 392 <p id="display"></p> 393 <div id="content" style="display: none"> 394 <test-custom-element id="element4" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/> 395 <testwithoutdash id="element5" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/> 396 <axulelement id="element6" is="test-built-in-element" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/> 397 <menupopup id="element7" is="test-popup-extend" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"/> 398 <test-custom-element-3 id="element8" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"></test-custom-element-3> 399 </div> 400 <pre id="test"></pre> 401 </body> 402 </window>