Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 <!DOCTYPE HTML>
2 <html>
3 <!--
4 https://bugzilla.mozilla.org/show_bug.cgi?id=783129
5 -->
6 <head>
7 <title>Test for document.registerElement lifecycle callback</title>
8 <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
9 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
10 </head>
11 <body>
12 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=783129">Bug 783129</a>
13 <div id="container">
14 <x-hello id="hello"></x-hello>
15 <button id="extbutton" is="x-button"></button>
16 </div>
17 <script>
19 var container = document.getElementById("container");
21 // Tests callbacks after registering element type that is already in the document.
22 // create element in document -> register -> remove from document
23 function testRegisterUnresolved() {
24 var helloElem = document.getElementById("hello");
26 var createdCallbackCalled = false;
27 var attachedCallbackCalled = false;
28 var detachedCallbackCalled = false;
30 var p = Object.create(HTMLElement.prototype);
31 p.createdCallback = function() {
32 is(helloElem.__proto__, p, "Prototype should be adjusted just prior to invoking the created callback.");
33 is(createdCallbackCalled, false, "Created callback should only be called once in this tests.");
34 is(this, helloElem, "The 'this' value should be the custom element.");
35 createdCallbackCalled = true;
36 };
38 p.attachedCallback = function() {
39 is(createdCallbackCalled, true, "Created callback should be called before attached");
40 is(attachedCallbackCalled, false, "attached callback should only be called once in this test.");
41 is(this, helloElem, "The 'this' value should be the custom element.");
42 attachedCallbackCalled = true;
43 };
45 p.detachedCallback = function() {
46 is(attachedCallbackCalled, true, "attached callback should be called before detached");
47 is(detachedCallbackCalled, false, "detached callback should only be called once in this test.");
48 detachedCallbackCalled = true;
49 is(this, helloElem, "The 'this' value should be the custom element.");
50 runNextTest();
51 };
53 p.attributeChangedCallback = function(name, oldValue, newValue) {
54 ok(false, "attributeChanged callback should never be called in this test.");
55 };
57 document.registerElement("x-hello", { prototype: p });
58 is(createdCallbackCalled, true, "created callback should be called when control returns to script from user agent code");
60 // Remove element from document to trigger detached callback.
61 container.removeChild(helloElem);
62 }
64 // Tests callbacks after registering an extended element type that is already in the document.
65 // create element in document -> register -> remove from document
66 function testRegisterUnresolvedExtended() {
67 var buttonElem = document.getElementById("extbutton");
69 var createdCallbackCalled = false;
70 var attachedCallbackCalled = false;
71 var detachedCallbackCalled = false;
73 var p = Object.create(HTMLButtonElement.prototype);
74 p.createdCallback = function() {
75 is(buttonElem.__proto__, p, "Prototype should be adjusted just prior to invoking the created callback.");
76 is(createdCallbackCalled, false, "Created callback should only be called once in this tests.");
77 is(this, buttonElem, "The 'this' value should be the custom element.");
78 createdCallbackCalled = true;
79 };
81 p.attachedCallback = function() {
82 is(createdCallbackCalled, true, "Created callback should be called before attached");
83 is(attachedCallbackCalled, false, "attached callback should only be called once in this test.");
84 is(this, buttonElem, "The 'this' value should be the custom element.");
85 attachedCallbackCalled = true;
86 };
88 p.detachedCallback = function() {
89 is(attachedCallbackCalled, true, "attached callback should be called before detached");
90 is(detachedCallbackCalled, false, "detached callback should only be called once in this test.");
91 detachedCallbackCalled = true;
92 is(this, buttonElem, "The 'this' value should be the custom element.");
93 runNextTest();
94 };
96 p.attributeChangedCallback = function(name, oldValue, newValue) {
97 ok(false, "attributeChanged callback should never be called in this test.");
98 };
100 document.registerElement("x-button", { prototype: p, extends: "button" });
101 is(createdCallbackCalled, true, "created callback should be called when control returns to script from user agent code");
103 // Remove element from document to trigger detached callback.
104 container.removeChild(buttonElem);
105 }
107 function testInnerHTML() {
108 var createdCallbackCalled = false;
110 var p = Object.create(HTMLElement.prototype);
111 p.createdCallback = function() {
112 is(createdCallbackCalled, false, "created callback should only be called once in this test.");
113 createdCallbackCalled = true;
114 };
116 document.registerElement("x-inner-html", { prototype: p });
117 var div = document.createElement(div);
118 div.innerHTML = '<x-inner-html></x-inner-html>';
119 is(createdCallbackCalled, true, "created callback should be called after setting innerHTML.");
120 runNextTest();
121 }
123 function testInnerHTMLExtended() {
124 var createdCallbackCalled = false;
126 var p = Object.create(HTMLButtonElement.prototype);
127 p.createdCallback = function() {
128 is(createdCallbackCalled, false, "created callback should only be called once in this test.");
129 createdCallbackCalled = true;
130 };
132 document.registerElement("x-inner-html-extended", { prototype: p, extends: "button" });
133 var div = document.createElement(div);
134 div.innerHTML = '<button is="x-inner-html-extended"></button>';
135 is(createdCallbackCalled, true, "created callback should be called after setting innerHTML.");
136 runNextTest();
137 }
139 function testInnerHTMLUpgrade() {
140 var createdCallbackCalled = false;
142 var div = document.createElement(div);
143 div.innerHTML = '<x-inner-html-upgrade></x-inner-html-upgrade>';
145 var p = Object.create(HTMLElement.prototype);
146 p.createdCallback = function() {
147 is(createdCallbackCalled, false, "created callback should only be called once in this test.");
148 createdCallbackCalled = true;
149 };
151 document.registerElement("x-inner-html-upgrade", { prototype: p });
152 is(createdCallbackCalled, true, "created callback should be called after registering.");
153 runNextTest();
154 }
156 function testInnerHTMLExtendedUpgrade() {
157 var createdCallbackCalled = false;
159 var div = document.createElement(div);
160 div.innerHTML = '<button is="x-inner-html-extended-upgrade"></button>';
162 var p = Object.create(HTMLButtonElement.prototype);
163 p.createdCallback = function() {
164 is(createdCallbackCalled, false, "created callback should only be called once in this test.");
165 createdCallbackCalled = true;
166 };
168 document.registerElement("x-inner-html-extended-upgrade", { prototype: p, extends: "button" });
169 is(createdCallbackCalled, true, "created callback should be called after registering.");
170 runNextTest();
171 }
173 // Test callback when creating element after registering an element type.
174 // register -> create element -> insert into document -> remove from document
175 function testRegisterResolved() {
176 var createdCallbackCalled = false;
177 var attachedCallbackCalled = false;
178 var detachedCallbackCalled = false;
180 var createdCallbackThis;
182 var p = Object.create(HTMLElement.prototype);
183 p.createdCallback = function() {
184 is(createdCallbackCalled, false, "Created callback should only be called once in this test.");
185 createdCallbackThis = this;
186 createdCallbackCalled = true;
187 };
189 p.attachedCallback = function() {
190 is(createdCallbackCalled, true, "created callback should be called before attached callback.");
191 is(attachedCallbackCalled, false, "attached callback should only be called on in this test.");
192 is(this, createdElement, "The 'this' value should be the custom element.");
193 attachedCallbackCalled = true;
194 };
196 p.detachedCallback = function() {
197 is(attachedCallbackCalled, true, "attached callback should be called before detached");
198 is(detachedCallbackCalled, false, "detached callback should only be called once in this test.");
199 is(this, createdElement, "The 'this' value should be the custom element.");
200 detachedCallbackCalled = true;
201 runNextTest();
202 };
204 p.attributeChangedCallback = function() {
205 ok(false, "attributeChanged callback should never be called in this test.");
206 };
208 document.registerElement("x-resolved", { prototype: p });
209 is(createdCallbackCalled, false, "Created callback should not be called when custom element instance has not been created.");
211 var createdElement = document.createElement("x-resolved");
212 is(createdCallbackThis, createdElement, "The 'this' value in the created callback should be the custom element.");
213 is(createdElement.__proto__, p, "Prototype of custom element should be the registered prototype.");
215 // Insert element into document to trigger attached callback.
216 container.appendChild(createdElement);
218 // Remove element from document to trigger detached callback.
219 container.removeChild(createdElement);
220 }
222 // Callbacks should always be the same ones when registered.
223 function testChangingCallback() {
224 var p = Object.create(HTMLElement.prototype);
225 var callbackCalled = false;
226 p.attributeChangedCallback = function(name, oldValue, newValue) {
227 is(callbackCalled, false, "Callback should only be called once in this test.");
228 callbackCalled = true;
229 runNextTest();
230 };
232 document.registerElement("x-test-callback", { prototype: p });
234 p.attributeChangedCallback = function(name, oldValue, newValue) {
235 ok(false, "Only callbacks at registration should be called.");
236 };
238 var elem = document.createElement("x-test-callback");
239 elem.setAttribute("foo", "bar");
240 }
242 function testAttributeChanged() {
243 var createdCallbackCalled = false;
245 var createdElement;
246 var createdCallbackThis;
248 var p = Object.create(HTMLElement.prototype);
249 p.createdCallback = function() {
250 is(createdCallbackCalled, false, "Created callback should only be called once in this test.");
251 createdCallbackThis = this;
252 createdCallbackCalled = true;
253 };
255 // Sequence of callback arguments that we expect from attribute changed callback
256 // after changing attributes values in a specific order.
257 var expectedCallbackArguments = [
258 // [oldValue, newValue]
259 [null, "newvalue"], // Setting the attribute value to "newvalue"
260 ["newvalue", "nextvalue"], // Changing the attribute value from "newvalue" to "nextvalue"
261 ["nextvalue", ""], // Changing the attribute value from "nextvalue" to empty string
262 ["", null], // Removing the attribute.
263 ];
265 p.attributeChangedCallback = function(name, oldValue, newValue) {
266 is(createdCallbackCalled, true, "created callback should be called before attribute changed.");
267 is(this, createdElement, "The 'this' value should be the custom element.");
268 ok(expectedCallbackArguments.length > 0, "Attribute changed callback should not be called more than expected.");
270 is(name, "changeme", "name arugment in attribute changed callback should be the name of the changed attribute.");
272 var expectedArgs = expectedCallbackArguments.shift();
273 is(oldValue, expectedArgs[0], "The old value argument should match the expected value.");
274 is(newValue, expectedArgs[1], "The new value argument should match the expected value.");
276 if (expectedCallbackArguments.length === 0) {
277 // Done with the attribute changed callback test.
278 runNextTest();
279 }
280 };
282 document.registerElement("x-attrchange", { prototype: p });
284 var createdElement = document.createElement("x-attrchange");
285 is(createdCallbackThis, createdElement, "The 'this' value in the created callback should be the custom element.");
286 createdElement.setAttribute("changeme", "newvalue");
287 createdElement.setAttribute("changeme", "nextvalue");
288 createdElement.setAttribute("changeme", "");
289 createdElement.removeAttribute("changeme");
290 }
292 function testAttributeChangedExtended() {
293 var p = Object.create(HTMLButtonElement.prototype);
294 var callbackCalled = false;
295 p.attributeChangedCallback = function(name, oldValue, newValue) {
296 is(callbackCalled, false, "Callback should only be called once in this test.");
297 callbackCalled = true;
298 runNextTest();
299 };
301 document.registerElement("x-extended-attribute-change", { prototype: p, extends: "button" });
303 var elem = document.createElement("button", "x-extended-attribute-change");
304 elem.setAttribute("foo", "bar");
305 }
307 // Creates a custom element that is an upgrade candidate (no registration)
308 // and mutate the element in ways that would call callbacks for registered
309 // elements.
310 function testUpgradeCandidate() {
311 var createdElement = document.createElement("x-upgrade-candidate");
312 container.appendChild(createdElement);
313 createdElement.setAttribute("foo", "bar");
314 container.removeChild(createdElement);
315 ok(true, "Nothing bad should happen when trying to mutating upgrade candidates.");
316 runNextTest();
317 }
319 function testNotInDocEnterLeave() {
320 var p = Object.create(HTMLElement.prototype);
322 p.attached = function() {
323 ok(false, "attached should not be called when not entering the document.");
324 };
326 p.detached = function() {
327 ok(false, "leaveView should not be called when not leaving the document.");
328 };
330 var createdElement = document.createElement("x-destined-for-fragment");
332 document.registerElement("x-destined-for-fragment", { prototype: p });
334 var fragment = new DocumentFragment();
335 fragment.appendChild(createdElement);
336 fragment.removeChild(createdElement);
338 var divNotInDoc = document.createElement("div");
339 divNotInDoc.appendChild(createdElement);
340 divNotInDoc.removeChild(createdElement);
342 runNextTest();
343 }
345 function testEnterLeaveView() {
346 var attachedCallbackCalled = false;
347 var detachedCallbackCalled = false;
349 var p = Object.create(HTMLElement.prototype);
350 p.attachedCallback = function() {
351 is(attachedCallbackCalled, false, "attached callback should only be called on in this test.");
352 attachedCallbackCalled = true;
353 };
355 p.detachedCallback = function() {
356 is(attachedCallbackCalled, true, "attached callback should be called before detached");
357 is(detachedCallbackCalled, false, "detached callback should only be called once in this test.");
358 detachedCallbackCalled = true;
359 runNextTest();
360 };
362 var div = document.createElement("div");
363 document.registerElement("x-element-in-div", { prototype: p });
364 var customElement = document.createElement("x-element-in-div");
365 div.appendChild(customElement);
366 is(attachedCallbackCalled, false, "Appending a custom element to a node that is not in the document should not call the attached callback.");
368 container.appendChild(div);
369 container.removeChild(div);
370 }
372 var testFunctions = [
373 testRegisterUnresolved,
374 testRegisterUnresolvedExtended,
375 testInnerHTML,
376 testInnerHTMLExtended,
377 testInnerHTMLUpgrade,
378 testInnerHTMLExtendedUpgrade,
379 testRegisterResolved,
380 testAttributeChanged,
381 testAttributeChangedExtended,
382 testUpgradeCandidate,
383 testChangingCallback,
384 testNotInDocEnterLeave,
385 testEnterLeaveView,
386 SimpleTest.finish
387 ];
389 function runNextTest() {
390 if (testFunctions.length > 0) {
391 var nextTestFunction = testFunctions.shift();
392 nextTestFunction();
393 }
394 }
396 SimpleTest.waitForExplicitFinish();
398 runNextTest();
400 </script>
401 </body>
402 </html>