Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 <?xml version="1.0"?>
2 <!--
3 Any copyright is dedicated to the Public Domain.
4 http://creativecommons.org/publicdomain/zero/1.0/
5 -->
6 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
7 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
9 <window title="DOMRequestHelper Test"
10 xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
11 onload="start();">
13 <script type="application/javascript"
14 src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
16 <script type="application/javascript">
17 <![CDATA[
18 const Cc = Components.classes;
19 const Cu = Components.utils;
20 const Ci = Components.interfaces;
21 Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
22 let obs = Cc["@mozilla.org/observer-service;1"].
23 getService(Ci.nsIObserverService);
25 function DummyHelperSubclass() {
26 this.onuninit = null;
27 }
28 DummyHelperSubclass.prototype = {
29 __proto__: DOMRequestIpcHelper.prototype,
30 uninit: function() {
31 if (typeof this.onuninit === "function") {
32 this.onuninit();
33 }
34 this.onuninit = null;
35 }
36 };
38 var dummy = new DummyHelperSubclass();
40 /**
41 * Init & destroy.
42 */
43 function initDOMRequestHelperTest(aMessages) {
44 is(dummy._requests, undefined, "Request is undefined");
45 is(dummy._messages, undefined, "Messages is undefined");
46 is(dummy._window, undefined, "Window is undefined");
48 dummy.initDOMRequestHelper(window, aMessages);
50 ok(dummy._window, "Window exists");
51 is(dummy._window, window, "Correct window");
52 if (aMessages) {
53 is(typeof dummy._listeners, "object", "Listeners is an object");
54 }
55 }
57 function destroyDOMRequestHelperTest() {
58 dummy.destroyDOMRequestHelper();
60 is(dummy._requests, undefined, "Request is undefined");
61 is(dummy._messages, undefined, "Messages is undefined");
62 is(dummy._window, undefined, "Window is undefined");
63 }
65 /**
66 * Message listeners.
67 */
68 function checkMessageListeners(aExpectedListeners, aCount) {
69 ok(true, "Checking message listeners\n" + "Expected listeners " +
70 JSON.stringify(aExpectedListeners) + " \nExpected count " + aCount);
71 let count = 0;
72 Object.keys(dummy._listeners).forEach(function(name) {
73 count++;
74 is(aExpectedListeners[name], dummy._listeners[name],
75 "Message found " + name + " - Same listeners");
76 });
77 is(aCount, count, "Correct number of listeners");
78 }
80 function addMessageListenersTest(aMessages, aExpectedListeners, aCount) {
81 dummy.addMessageListeners(aMessages);
82 ok(true, JSON.stringify(dummy._listeners));
83 checkMessageListeners(aExpectedListeners, aCount);
84 }
86 function removeMessageListenersTest(aMessages, aExpectedListeners, aCount) {
87 dummy.removeMessageListeners(aMessages);
88 checkMessageListeners(aExpectedListeners, aCount);
89 }
91 /**
92 * Utility function to test window destruction behavior. In general this
93 * function does the following:
94 *
95 * 1) Create a new iframe
96 * 2) Create a new DOMRequestHelper
97 * 3) initDOMRequestHelper(), optionally with weak or strong listeners
98 * 4) Optionally force a garbage collection to reap weak references
99 * 5) Destroy the iframe triggering an inner-window-destroyed event
100 * 6) Callback with a boolean indicating if DOMRequestHelper.uninit() was
101 * called.
102 *
103 * Example usage:
104 *
105 * checkWindowDestruction({ messages: ["foo"], gc: true },
106 * function(uninitCalled) {
107 * // expect uninitCalled === false since GC with only weak refs
108 * });
109 */
110 const TOPIC = "inner-window-destroyed";
111 function checkWindowDestruction(aOptions, aCallback) {
112 aOptions = aOptions || {};
113 aOptions.messages = aOptions.messages || [];
114 aOptions.gc = !!aOptions.gc;
116 if (typeof aCallback !== "function") {
117 aCallback = function() { };
118 }
120 let uninitCalled = false;
122 // Use a secondary observer so we know when to expect the uninit(). We
123 // can then reasonably expect uninitCalled to be set properly on the
124 // next tick.
125 let observer = {
126 observe: function(aSubject, aTopic, aData) {
127 if (aTopic !== TOPIC) {
128 return;
129 }
130 obs.removeObserver(observer, TOPIC);
131 setTimeout(function() {
132 aCallback(uninitCalled);
133 });
134 }
135 };
137 let frame = document.createElement("iframe");
138 frame.onload = function() {
139 obs.addObserver(observer, TOPIC, false);
140 // Create dummy DOMRequestHelper specific to checkWindowDestruction()
141 let cwdDummy = new DummyHelperSubclass();
142 cwdDummy.onuninit = function() {
143 uninitCalled = true;
144 };
145 cwdDummy.initDOMRequestHelper(frame.contentWindow, aOptions.messages);
146 // Make sure to clear our strong ref here so that we can test our
147 // weak reference listeners and observer.
148 cwdDummy = null;
149 if (aOptions.gc) {
150 Cu.schedulePreciseGC(function() {
151 SpecialPowers.DOMWindowUtils.cycleCollect();
152 SpecialPowers.DOMWindowUtils.garbageCollect();
153 SpecialPowers.DOMWindowUtils.garbageCollect();
154 document.documentElement.removeChild(frame);
155 });
156 return;
157 }
158 document.documentElement.removeChild(frame);
159 };
160 document.documentElement.appendChild(frame);
161 }
163 /**
164 * Test steps.
165 */
166 var tests = [
167 function() {
168 ok(true, "== InitDOMRequestHelper no messages");
169 initDOMRequestHelperTest(null);
170 next();
171 },
172 function() {
173 ok(true, "== DestroyDOMRequestHelper");
174 destroyDOMRequestHelperTest();
175 next();
176 },
177 function() {
178 ok(true, "== InitDOMRequestHelper empty array");
179 initDOMRequestHelperTest([]);
180 checkMessageListeners({}, 0);
181 next();
182 },
183 function() {
184 ok(true, "== DestroyDOMRequestHelper");
185 destroyDOMRequestHelperTest();
186 next();
187 },
188 function() {
189 ok(true, "== InitDOMRequestHelper with strings array");
190 initDOMRequestHelperTest(["name1", "nameN"]);
191 checkMessageListeners({"name1": false, "nameN": false}, 2);
192 next();
193 },
194 function() {
195 ok(true, "== DestroyDOMRequestHelper");
196 destroyDOMRequestHelperTest();
197 next();
198 },
199 function() {
200 ok(true, "== InitDOMRequestHelper with objects array");
201 initDOMRequestHelperTest([{
202 name: "name1",
203 weakRef: false
204 }, {
205 name: "nameN",
206 weakRef: true
207 }]);
208 checkMessageListeners({"name1": false, "nameN": true}, 2);
209 next();
210 },
211 function() {
212 ok(true, "== AddMessageListeners empty array");
213 addMessageListenersTest([], {"name1": false, "nameN": true}, 2);
214 next();
215 },
216 function() {
217 ok(true, "== AddMessageListeners null");
218 addMessageListenersTest(null, {"name1": false, "nameN": true}, 2);
219 next();
220 },
221 function() {
222 ok(true, "== AddMessageListeners new listener, string only");
223 addMessageListenersTest("name2", {
224 "name1": false,
225 "name2": false,
226 "nameN": true
227 }, 3);
228 next();
229 },
230 function() {
231 ok(true, "== AddMessageListeners new listeners, strings array");
232 addMessageListenersTest(["name3", "name4"], {
233 "name1": false,
234 "name2": false,
235 "name3": false,
236 "name4": false,
237 "nameN": true
238 }, 5);
239 next();
240 },
241 function() {
242 ok(true, "== AddMessageListeners new listeners, objects array");
243 addMessageListenersTest([{
244 name: "name5",
245 weakRef: true
246 }, {
247 name: "name6",
248 weakRef: false
249 }], {
250 "name1": false,
251 "name2": false,
252 "name3": false,
253 "name4": false,
254 "name5": true,
255 "name6": false,
256 "nameN": true
257 }, 7);
258 next();
259 },
260 function() {
261 ok(true, "== RemoveMessageListeners, null");
262 removeMessageListenersTest(null, {
263 "name1": false,
264 "name2": false,
265 "name3": false,
266 "name4": false,
267 "name5": true,
268 "name6": false,
269 "nameN": true
270 }, 7);
271 next();
272 },
273 function() {
274 ok(true, "== RemoveMessageListeners, one message");
275 removeMessageListenersTest("name1", {
276 "name2": false,
277 "name3": false,
278 "name4": false,
279 "name5": true,
280 "name6": false,
281 "nameN": true
282 }, 6);
283 next();
284 },
285 function() {
286 ok(true, "== RemoveMessageListeners, array of messages");
287 removeMessageListenersTest(["name2", "name3"], {
288 "name4": false,
289 "name5": true,
290 "name6": false,
291 "nameN": true
292 }, 4);
293 next();
294 },
295 function() {
296 ok(true, "== RemoveMessageListeners, unknown message");
297 removeMessageListenersTest("unknown", {
298 "name4": false,
299 "name5": true,
300 "name6": false,
301 "nameN": true
302 }, 4);
303 next();
304 },
305 function() {
306 try {
307 ok(true, "== AddMessageListeners, same message, same kind");
308 addMessageListenersTest("name4", {
309 "name4": false,
310 "name5": true,
311 "name6": false,
312 "nameN": true
313 }, 4);
314 next();
315 } catch (ex) {
316 ok(false, "Unexpected exception " + ex);
317 }
318 },
319 function() {
320 ok(true, "== AddMessageListeners, same message, different kind");
321 try {
322 addMessageListenersTest({name: "name4", weakRef: true}, {
323 "name4": true,
324 "name5": true,
325 "name6": false,
326 "nameN": true
327 }, 4);
328 ok(false, "Should have thrown an exception");
329 } catch (ex) {
330 ok(true, "Expected exception");
331 next();
332 }
333 },
334 function() {
335 ok(true, "== Test createRequest()");
336 ok(DOMRequest, "DOMRequest object exists");
337 var req = dummy.createRequest();
338 ok(req instanceof DOMRequest, "Returned a DOMRequest");
339 next();
340 },
341 function() {
342 ok(true, "== Test getRequestId(), removeRequest() and getRequest()");
343 var req = dummy.createRequest();
344 var id = dummy.getRequestId(req);
345 is(typeof id, "string", "id is a string");
346 var req_ = dummy.getRequest(id);
347 is(req, req_, "Got correct request");
348 dummy.removeRequest(id);
349 req = dummy.getRequest(id);
350 is(req, null, "No request");
351 next();
352 },
353 function() {
354 ok(true, "== Test createPromise()");
355 ok(Promise, "Promise object exists");
356 var promise = dummy.createPromise(function(resolve, reject) {
357 resolve(true);
358 });
359 ok(promise instanceof Promise, "Returned a Promise");
360 promise.then(next);
361 },
362 function() {
363 ok(true, "== Test getResolver()");
364 var id;
365 var resolver;
366 var promise = dummy.createPromise(function(resolve, reject) {
367 var r = { resolve: resolve, reject: reject };
368 id = dummy.getPromiseResolverId(r);
369 resolver = r;
370 ok(typeof id === "string", "id is a string");
371 r.resolve(true);
372 }).then(function(unused) {
373 var r = dummy.getPromiseResolver(id);
374 ok(resolver === r, "Get succeeded");
375 next();
376 });
377 },
378 function() {
379 ok(true, "== Test removeResolver");
380 var id;
381 var promise = dummy.createPromise(function(resolve, reject) {
382 var r = { resolve: resolve, reject: reject };
383 id = dummy.getPromiseResolverId(r);
384 ok(typeof id === "string", "id is a string");
386 var resolver = dummy.getPromiseResolver(id);
387 ok(true, "Got resolver " + JSON.stringify(resolver));
388 ok(resolver === r, "Resolver get succeeded");
390 r.resolve(true);
391 }).then(function(unused) {
392 dummy.removePromiseResolver(id);
393 var resolver = dummy.getPromiseResolver(id);
394 ok(resolver === undefined, "removeResolver: get failed");
395 next();
396 });
397 },
398 function() {
399 ok(true, "== Test takeResolver");
400 var id;
401 var resolver;
402 var promise = dummy.createPromise(function(resolve, reject) {
403 var r = { resolve: resolve, reject: reject };
404 id = dummy.getPromiseResolverId(r);
405 resolver = r;
406 ok(typeof id === "string", "id is a string");
408 var gotR = dummy.getPromiseResolver(id);
409 ok(gotR === r, "resolver get succeeded");
411 r.resolve(true);
412 }).then(function(unused) {
413 var r = dummy.takePromiseResolver(id);
414 ok(resolver === r, "take should succeed");
416 r = dummy.getPromiseResolver(id);
417 ok(r === undefined, "takeResolver: get failed");
418 next();
419 });
420 },
421 function() {
422 ok(true, "== Test window destroyed without messages and without GC");
423 checkWindowDestruction({ gc: false }, function(uninitCalled) {
424 ok(uninitCalled, "uninit() should have been called");
425 next();
426 });
427 },
428 function() {
429 ok(true, "== Test window destroyed without messages and with GC");
430 checkWindowDestruction({ gc: true }, function(uninitCalled) {
431 ok(!uninitCalled, "uninit() should NOT have been called");
432 next();
433 });
434 },
435 function() {
436 ok(true, "== Test window destroyed with weak messages and without GC");
437 checkWindowDestruction({ messages: [{ name: "foo", weakRef: true }],
438 gc: false }, function(uninitCalled) {
439 ok(uninitCalled, "uninit() should have been called");
440 next();
441 });
442 },
443 function() {
444 ok(true, "== Test window destroyed with weak messages and with GC");
445 checkWindowDestruction({ messages: [{ name: "foo", weakRef: true }],
446 gc: true }, function(uninitCalled) {
447 ok(!uninitCalled, "uninit() should NOT have been called");
448 next();
449 });
450 },
451 function() {
452 ok(true, "== Test window destroyed with strong messages and without GC");
453 checkWindowDestruction({ messages: [{ name: "foo", weakRef: false }],
454 gc: false }, function(uninitCalled) {
455 ok(uninitCalled, "uninit() should have been called");
456 next();
457 });
458 },
459 function() {
460 ok(true, "== Test window destroyed with strong messages and with GC");
461 checkWindowDestruction({ messages: [{ name: "foo", weakRef: false }],
462 gc: true }, function(uninitCalled) {
463 ok(uninitCalled, "uninit() should have been called");
464 next();
465 });
466 }
467 ];
469 function next() {
470 if (!tests.length) {
471 SimpleTest.finish();
472 return;
473 }
475 var test = tests.shift();
476 test();
477 }
479 function start() {
480 SimpleTest.waitForExplicitFinish();
481 next();
482 }
483 ]]>
484 </script>
486 <body xmlns="http://www.w3.org/1999/xhtml">
487 <p id="display"></p>
488 <div id="content" style="display: none"></div>
489 <pre id="test"></pre>
490 </body>
491 </window>