1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/base/test/test_domrequesthelper.xul Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,491 @@ 1.4 +<?xml version="1.0"?> 1.5 +<!-- 1.6 + Any copyright is dedicated to the Public Domain. 1.7 + http://creativecommons.org/publicdomain/zero/1.0/ 1.8 +--> 1.9 +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> 1.10 +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> 1.11 + 1.12 +<window title="DOMRequestHelper Test" 1.13 + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" 1.14 + onload="start();"> 1.15 + 1.16 + <script type="application/javascript" 1.17 + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> 1.18 + 1.19 + <script type="application/javascript"> 1.20 + <![CDATA[ 1.21 + const Cc = Components.classes; 1.22 + const Cu = Components.utils; 1.23 + const Ci = Components.interfaces; 1.24 + Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); 1.25 + let obs = Cc["@mozilla.org/observer-service;1"]. 1.26 + getService(Ci.nsIObserverService); 1.27 + 1.28 + function DummyHelperSubclass() { 1.29 + this.onuninit = null; 1.30 + } 1.31 + DummyHelperSubclass.prototype = { 1.32 + __proto__: DOMRequestIpcHelper.prototype, 1.33 + uninit: function() { 1.34 + if (typeof this.onuninit === "function") { 1.35 + this.onuninit(); 1.36 + } 1.37 + this.onuninit = null; 1.38 + } 1.39 + }; 1.40 + 1.41 + var dummy = new DummyHelperSubclass(); 1.42 + 1.43 + /** 1.44 + * Init & destroy. 1.45 + */ 1.46 + function initDOMRequestHelperTest(aMessages) { 1.47 + is(dummy._requests, undefined, "Request is undefined"); 1.48 + is(dummy._messages, undefined, "Messages is undefined"); 1.49 + is(dummy._window, undefined, "Window is undefined"); 1.50 + 1.51 + dummy.initDOMRequestHelper(window, aMessages); 1.52 + 1.53 + ok(dummy._window, "Window exists"); 1.54 + is(dummy._window, window, "Correct window"); 1.55 + if (aMessages) { 1.56 + is(typeof dummy._listeners, "object", "Listeners is an object"); 1.57 + } 1.58 + } 1.59 + 1.60 + function destroyDOMRequestHelperTest() { 1.61 + dummy.destroyDOMRequestHelper(); 1.62 + 1.63 + is(dummy._requests, undefined, "Request is undefined"); 1.64 + is(dummy._messages, undefined, "Messages is undefined"); 1.65 + is(dummy._window, undefined, "Window is undefined"); 1.66 + } 1.67 + 1.68 + /** 1.69 + * Message listeners. 1.70 + */ 1.71 + function checkMessageListeners(aExpectedListeners, aCount) { 1.72 + ok(true, "Checking message listeners\n" + "Expected listeners " + 1.73 + JSON.stringify(aExpectedListeners) + " \nExpected count " + aCount); 1.74 + let count = 0; 1.75 + Object.keys(dummy._listeners).forEach(function(name) { 1.76 + count++; 1.77 + is(aExpectedListeners[name], dummy._listeners[name], 1.78 + "Message found " + name + " - Same listeners"); 1.79 + }); 1.80 + is(aCount, count, "Correct number of listeners"); 1.81 + } 1.82 + 1.83 + function addMessageListenersTest(aMessages, aExpectedListeners, aCount) { 1.84 + dummy.addMessageListeners(aMessages); 1.85 + ok(true, JSON.stringify(dummy._listeners)); 1.86 + checkMessageListeners(aExpectedListeners, aCount); 1.87 + } 1.88 + 1.89 + function removeMessageListenersTest(aMessages, aExpectedListeners, aCount) { 1.90 + dummy.removeMessageListeners(aMessages); 1.91 + checkMessageListeners(aExpectedListeners, aCount); 1.92 + } 1.93 + 1.94 + /** 1.95 + * Utility function to test window destruction behavior. In general this 1.96 + * function does the following: 1.97 + * 1.98 + * 1) Create a new iframe 1.99 + * 2) Create a new DOMRequestHelper 1.100 + * 3) initDOMRequestHelper(), optionally with weak or strong listeners 1.101 + * 4) Optionally force a garbage collection to reap weak references 1.102 + * 5) Destroy the iframe triggering an inner-window-destroyed event 1.103 + * 6) Callback with a boolean indicating if DOMRequestHelper.uninit() was 1.104 + * called. 1.105 + * 1.106 + * Example usage: 1.107 + * 1.108 + * checkWindowDestruction({ messages: ["foo"], gc: true }, 1.109 + * function(uninitCalled) { 1.110 + * // expect uninitCalled === false since GC with only weak refs 1.111 + * }); 1.112 + */ 1.113 + const TOPIC = "inner-window-destroyed"; 1.114 + function checkWindowDestruction(aOptions, aCallback) { 1.115 + aOptions = aOptions || {}; 1.116 + aOptions.messages = aOptions.messages || []; 1.117 + aOptions.gc = !!aOptions.gc; 1.118 + 1.119 + if (typeof aCallback !== "function") { 1.120 + aCallback = function() { }; 1.121 + } 1.122 + 1.123 + let uninitCalled = false; 1.124 + 1.125 + // Use a secondary observer so we know when to expect the uninit(). We 1.126 + // can then reasonably expect uninitCalled to be set properly on the 1.127 + // next tick. 1.128 + let observer = { 1.129 + observe: function(aSubject, aTopic, aData) { 1.130 + if (aTopic !== TOPIC) { 1.131 + return; 1.132 + } 1.133 + obs.removeObserver(observer, TOPIC); 1.134 + setTimeout(function() { 1.135 + aCallback(uninitCalled); 1.136 + }); 1.137 + } 1.138 + }; 1.139 + 1.140 + let frame = document.createElement("iframe"); 1.141 + frame.onload = function() { 1.142 + obs.addObserver(observer, TOPIC, false); 1.143 + // Create dummy DOMRequestHelper specific to checkWindowDestruction() 1.144 + let cwdDummy = new DummyHelperSubclass(); 1.145 + cwdDummy.onuninit = function() { 1.146 + uninitCalled = true; 1.147 + }; 1.148 + cwdDummy.initDOMRequestHelper(frame.contentWindow, aOptions.messages); 1.149 + // Make sure to clear our strong ref here so that we can test our 1.150 + // weak reference listeners and observer. 1.151 + cwdDummy = null; 1.152 + if (aOptions.gc) { 1.153 + Cu.schedulePreciseGC(function() { 1.154 + SpecialPowers.DOMWindowUtils.cycleCollect(); 1.155 + SpecialPowers.DOMWindowUtils.garbageCollect(); 1.156 + SpecialPowers.DOMWindowUtils.garbageCollect(); 1.157 + document.documentElement.removeChild(frame); 1.158 + }); 1.159 + return; 1.160 + } 1.161 + document.documentElement.removeChild(frame); 1.162 + }; 1.163 + document.documentElement.appendChild(frame); 1.164 + } 1.165 + 1.166 + /** 1.167 + * Test steps. 1.168 + */ 1.169 + var tests = [ 1.170 + function() { 1.171 + ok(true, "== InitDOMRequestHelper no messages"); 1.172 + initDOMRequestHelperTest(null); 1.173 + next(); 1.174 + }, 1.175 + function() { 1.176 + ok(true, "== DestroyDOMRequestHelper"); 1.177 + destroyDOMRequestHelperTest(); 1.178 + next(); 1.179 + }, 1.180 + function() { 1.181 + ok(true, "== InitDOMRequestHelper empty array"); 1.182 + initDOMRequestHelperTest([]); 1.183 + checkMessageListeners({}, 0); 1.184 + next(); 1.185 + }, 1.186 + function() { 1.187 + ok(true, "== DestroyDOMRequestHelper"); 1.188 + destroyDOMRequestHelperTest(); 1.189 + next(); 1.190 + }, 1.191 + function() { 1.192 + ok(true, "== InitDOMRequestHelper with strings array"); 1.193 + initDOMRequestHelperTest(["name1", "nameN"]); 1.194 + checkMessageListeners({"name1": false, "nameN": false}, 2); 1.195 + next(); 1.196 + }, 1.197 + function() { 1.198 + ok(true, "== DestroyDOMRequestHelper"); 1.199 + destroyDOMRequestHelperTest(); 1.200 + next(); 1.201 + }, 1.202 + function() { 1.203 + ok(true, "== InitDOMRequestHelper with objects array"); 1.204 + initDOMRequestHelperTest([{ 1.205 + name: "name1", 1.206 + weakRef: false 1.207 + }, { 1.208 + name: "nameN", 1.209 + weakRef: true 1.210 + }]); 1.211 + checkMessageListeners({"name1": false, "nameN": true}, 2); 1.212 + next(); 1.213 + }, 1.214 + function() { 1.215 + ok(true, "== AddMessageListeners empty array"); 1.216 + addMessageListenersTest([], {"name1": false, "nameN": true}, 2); 1.217 + next(); 1.218 + }, 1.219 + function() { 1.220 + ok(true, "== AddMessageListeners null"); 1.221 + addMessageListenersTest(null, {"name1": false, "nameN": true}, 2); 1.222 + next(); 1.223 + }, 1.224 + function() { 1.225 + ok(true, "== AddMessageListeners new listener, string only"); 1.226 + addMessageListenersTest("name2", { 1.227 + "name1": false, 1.228 + "name2": false, 1.229 + "nameN": true 1.230 + }, 3); 1.231 + next(); 1.232 + }, 1.233 + function() { 1.234 + ok(true, "== AddMessageListeners new listeners, strings array"); 1.235 + addMessageListenersTest(["name3", "name4"], { 1.236 + "name1": false, 1.237 + "name2": false, 1.238 + "name3": false, 1.239 + "name4": false, 1.240 + "nameN": true 1.241 + }, 5); 1.242 + next(); 1.243 + }, 1.244 + function() { 1.245 + ok(true, "== AddMessageListeners new listeners, objects array"); 1.246 + addMessageListenersTest([{ 1.247 + name: "name5", 1.248 + weakRef: true 1.249 + }, { 1.250 + name: "name6", 1.251 + weakRef: false 1.252 + }], { 1.253 + "name1": false, 1.254 + "name2": false, 1.255 + "name3": false, 1.256 + "name4": false, 1.257 + "name5": true, 1.258 + "name6": false, 1.259 + "nameN": true 1.260 + }, 7); 1.261 + next(); 1.262 + }, 1.263 + function() { 1.264 + ok(true, "== RemoveMessageListeners, null"); 1.265 + removeMessageListenersTest(null, { 1.266 + "name1": false, 1.267 + "name2": false, 1.268 + "name3": false, 1.269 + "name4": false, 1.270 + "name5": true, 1.271 + "name6": false, 1.272 + "nameN": true 1.273 + }, 7); 1.274 + next(); 1.275 + }, 1.276 + function() { 1.277 + ok(true, "== RemoveMessageListeners, one message"); 1.278 + removeMessageListenersTest("name1", { 1.279 + "name2": false, 1.280 + "name3": false, 1.281 + "name4": false, 1.282 + "name5": true, 1.283 + "name6": false, 1.284 + "nameN": true 1.285 + }, 6); 1.286 + next(); 1.287 + }, 1.288 + function() { 1.289 + ok(true, "== RemoveMessageListeners, array of messages"); 1.290 + removeMessageListenersTest(["name2", "name3"], { 1.291 + "name4": false, 1.292 + "name5": true, 1.293 + "name6": false, 1.294 + "nameN": true 1.295 + }, 4); 1.296 + next(); 1.297 + }, 1.298 + function() { 1.299 + ok(true, "== RemoveMessageListeners, unknown message"); 1.300 + removeMessageListenersTest("unknown", { 1.301 + "name4": false, 1.302 + "name5": true, 1.303 + "name6": false, 1.304 + "nameN": true 1.305 + }, 4); 1.306 + next(); 1.307 + }, 1.308 + function() { 1.309 + try { 1.310 + ok(true, "== AddMessageListeners, same message, same kind"); 1.311 + addMessageListenersTest("name4", { 1.312 + "name4": false, 1.313 + "name5": true, 1.314 + "name6": false, 1.315 + "nameN": true 1.316 + }, 4); 1.317 + next(); 1.318 + } catch (ex) { 1.319 + ok(false, "Unexpected exception " + ex); 1.320 + } 1.321 + }, 1.322 + function() { 1.323 + ok(true, "== AddMessageListeners, same message, different kind"); 1.324 + try { 1.325 + addMessageListenersTest({name: "name4", weakRef: true}, { 1.326 + "name4": true, 1.327 + "name5": true, 1.328 + "name6": false, 1.329 + "nameN": true 1.330 + }, 4); 1.331 + ok(false, "Should have thrown an exception"); 1.332 + } catch (ex) { 1.333 + ok(true, "Expected exception"); 1.334 + next(); 1.335 + } 1.336 + }, 1.337 + function() { 1.338 + ok(true, "== Test createRequest()"); 1.339 + ok(DOMRequest, "DOMRequest object exists"); 1.340 + var req = dummy.createRequest(); 1.341 + ok(req instanceof DOMRequest, "Returned a DOMRequest"); 1.342 + next(); 1.343 + }, 1.344 + function() { 1.345 + ok(true, "== Test getRequestId(), removeRequest() and getRequest()"); 1.346 + var req = dummy.createRequest(); 1.347 + var id = dummy.getRequestId(req); 1.348 + is(typeof id, "string", "id is a string"); 1.349 + var req_ = dummy.getRequest(id); 1.350 + is(req, req_, "Got correct request"); 1.351 + dummy.removeRequest(id); 1.352 + req = dummy.getRequest(id); 1.353 + is(req, null, "No request"); 1.354 + next(); 1.355 + }, 1.356 + function() { 1.357 + ok(true, "== Test createPromise()"); 1.358 + ok(Promise, "Promise object exists"); 1.359 + var promise = dummy.createPromise(function(resolve, reject) { 1.360 + resolve(true); 1.361 + }); 1.362 + ok(promise instanceof Promise, "Returned a Promise"); 1.363 + promise.then(next); 1.364 + }, 1.365 + function() { 1.366 + ok(true, "== Test getResolver()"); 1.367 + var id; 1.368 + var resolver; 1.369 + var promise = dummy.createPromise(function(resolve, reject) { 1.370 + var r = { resolve: resolve, reject: reject }; 1.371 + id = dummy.getPromiseResolverId(r); 1.372 + resolver = r; 1.373 + ok(typeof id === "string", "id is a string"); 1.374 + r.resolve(true); 1.375 + }).then(function(unused) { 1.376 + var r = dummy.getPromiseResolver(id); 1.377 + ok(resolver === r, "Get succeeded"); 1.378 + next(); 1.379 + }); 1.380 + }, 1.381 + function() { 1.382 + ok(true, "== Test removeResolver"); 1.383 + var id; 1.384 + var promise = dummy.createPromise(function(resolve, reject) { 1.385 + var r = { resolve: resolve, reject: reject }; 1.386 + id = dummy.getPromiseResolverId(r); 1.387 + ok(typeof id === "string", "id is a string"); 1.388 + 1.389 + var resolver = dummy.getPromiseResolver(id); 1.390 + ok(true, "Got resolver " + JSON.stringify(resolver)); 1.391 + ok(resolver === r, "Resolver get succeeded"); 1.392 + 1.393 + r.resolve(true); 1.394 + }).then(function(unused) { 1.395 + dummy.removePromiseResolver(id); 1.396 + var resolver = dummy.getPromiseResolver(id); 1.397 + ok(resolver === undefined, "removeResolver: get failed"); 1.398 + next(); 1.399 + }); 1.400 + }, 1.401 + function() { 1.402 + ok(true, "== Test takeResolver"); 1.403 + var id; 1.404 + var resolver; 1.405 + var promise = dummy.createPromise(function(resolve, reject) { 1.406 + var r = { resolve: resolve, reject: reject }; 1.407 + id = dummy.getPromiseResolverId(r); 1.408 + resolver = r; 1.409 + ok(typeof id === "string", "id is a string"); 1.410 + 1.411 + var gotR = dummy.getPromiseResolver(id); 1.412 + ok(gotR === r, "resolver get succeeded"); 1.413 + 1.414 + r.resolve(true); 1.415 + }).then(function(unused) { 1.416 + var r = dummy.takePromiseResolver(id); 1.417 + ok(resolver === r, "take should succeed"); 1.418 + 1.419 + r = dummy.getPromiseResolver(id); 1.420 + ok(r === undefined, "takeResolver: get failed"); 1.421 + next(); 1.422 + }); 1.423 + }, 1.424 + function() { 1.425 + ok(true, "== Test window destroyed without messages and without GC"); 1.426 + checkWindowDestruction({ gc: false }, function(uninitCalled) { 1.427 + ok(uninitCalled, "uninit() should have been called"); 1.428 + next(); 1.429 + }); 1.430 + }, 1.431 + function() { 1.432 + ok(true, "== Test window destroyed without messages and with GC"); 1.433 + checkWindowDestruction({ gc: true }, function(uninitCalled) { 1.434 + ok(!uninitCalled, "uninit() should NOT have been called"); 1.435 + next(); 1.436 + }); 1.437 + }, 1.438 + function() { 1.439 + ok(true, "== Test window destroyed with weak messages and without GC"); 1.440 + checkWindowDestruction({ messages: [{ name: "foo", weakRef: true }], 1.441 + gc: false }, function(uninitCalled) { 1.442 + ok(uninitCalled, "uninit() should have been called"); 1.443 + next(); 1.444 + }); 1.445 + }, 1.446 + function() { 1.447 + ok(true, "== Test window destroyed with weak messages and with GC"); 1.448 + checkWindowDestruction({ messages: [{ name: "foo", weakRef: true }], 1.449 + gc: true }, function(uninitCalled) { 1.450 + ok(!uninitCalled, "uninit() should NOT have been called"); 1.451 + next(); 1.452 + }); 1.453 + }, 1.454 + function() { 1.455 + ok(true, "== Test window destroyed with strong messages and without GC"); 1.456 + checkWindowDestruction({ messages: [{ name: "foo", weakRef: false }], 1.457 + gc: false }, function(uninitCalled) { 1.458 + ok(uninitCalled, "uninit() should have been called"); 1.459 + next(); 1.460 + }); 1.461 + }, 1.462 + function() { 1.463 + ok(true, "== Test window destroyed with strong messages and with GC"); 1.464 + checkWindowDestruction({ messages: [{ name: "foo", weakRef: false }], 1.465 + gc: true }, function(uninitCalled) { 1.466 + ok(uninitCalled, "uninit() should have been called"); 1.467 + next(); 1.468 + }); 1.469 + } 1.470 + ]; 1.471 + 1.472 + function next() { 1.473 + if (!tests.length) { 1.474 + SimpleTest.finish(); 1.475 + return; 1.476 + } 1.477 + 1.478 + var test = tests.shift(); 1.479 + test(); 1.480 + } 1.481 + 1.482 + function start() { 1.483 + SimpleTest.waitForExplicitFinish(); 1.484 + next(); 1.485 + } 1.486 + ]]> 1.487 + </script> 1.488 + 1.489 + <body xmlns="http://www.w3.org/1999/xhtml"> 1.490 + <p id="display"></p> 1.491 + <div id="content" style="display: none"></div> 1.492 + <pre id="test"></pre> 1.493 + </body> 1.494 +</window>