summaryrefslogtreecommitdiffstats
path: root/dom/base/test/test_domrequesthelper.xhtml
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base/test/test_domrequesthelper.xhtml')
-rw-r--r--dom/base/test/test_domrequesthelper.xhtml549
1 files changed, 549 insertions, 0 deletions
diff --git a/dom/base/test/test_domrequesthelper.xhtml b/dom/base/test/test_domrequesthelper.xhtml
new file mode 100644
index 0000000000..6b9061b8a2
--- /dev/null
+++ b/dom/base/test/test_domrequesthelper.xhtml
@@ -0,0 +1,549 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="DOMRequestHelper Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="start();">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <script type="application/javascript">
+ <![CDATA[
+ const {DOMRequestIpcHelper} = ChromeUtils.importESModule(
+ "resource://gre/modules/DOMRequestHelper.sys.mjs"
+ );
+ let obs = Cc["@mozilla.org/observer-service;1"].
+ getService(Ci.nsIObserverService);
+ let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"].getService();
+
+ function DummyHelperSubclass() {
+ this.onuninit = null;
+ }
+ DummyHelperSubclass.prototype = {
+ __proto__: DOMRequestIpcHelper.prototype,
+ uninit() {
+ if (typeof this.onuninit === "function") {
+ this.onuninit();
+ }
+ this.onuninit = null;
+ }
+ };
+
+ var dummy = new DummyHelperSubclass();
+ var isDOMRequestHelperDestroyed = false;
+
+ /**
+ * Init & destroy.
+ */
+ function initDOMRequestHelperTest(aMessages) {
+ // If we haven't initialized the DOMRequestHelper object, its private
+ // properties will be undefined, but once destroyDOMRequestHelper is
+ // called, they're set to null.
+ var expectedPrivatePropertyValues =
+ isDOMRequestHelperDestroyed ? null : undefined;
+
+ is(dummy._requests, expectedPrivatePropertyValues, "Request is expected");
+ is(dummy._messages, undefined, "Messages is undefined");
+ is(dummy._window, expectedPrivatePropertyValues, "Window is expected");
+
+ dummy.initDOMRequestHelper(window, aMessages);
+
+ ok(dummy._window, "Window exists");
+ is(dummy._window, window, "Correct window");
+ if (aMessages) {
+ is(typeof dummy._listeners, "object", "Listeners is an object");
+ }
+ }
+
+ function destroyDOMRequestHelperTest() {
+ dummy.destroyDOMRequestHelper();
+ isDOMRequestHelperDestroyed = true;
+
+ is(dummy._requests, null, "Request is null");
+ is(dummy._messages, undefined, "Messages is undefined");
+ is(dummy._window, null, "Window is null");
+ }
+
+ /**
+ * Message listeners.
+ */
+ function checkMessageListeners(aExpectedListeners, aCount) {
+ info("Checking message listeners\n" + "Expected listeners " +
+ JSON.stringify(aExpectedListeners) + " \nExpected count " + aCount);
+ let count = 0;
+ Object.keys(dummy._listeners).forEach(function(name) {
+ count++;
+ is(aExpectedListeners[name].weakRef, dummy._listeners[name].weakRef,
+ "Message found " + name + " - Same weakRef");
+ is(aExpectedListeners[name].count, dummy._listeners[name].count,
+ "Message found " + name + " - Same count");
+ });
+ is(aCount, count, "Correct number of listeners");
+ }
+
+ function addMessageListenersTest(aMessages, aExpectedListeners, aCount) {
+ dummy.addMessageListeners(aMessages);
+ info(JSON.stringify(dummy._listeners));
+ checkMessageListeners(aExpectedListeners, aCount);
+ }
+
+ function removeMessageListenersTest(aMessages, aExpectedListeners, aCount) {
+ dummy.removeMessageListeners(aMessages);
+ checkMessageListeners(aExpectedListeners, aCount);
+ }
+
+ /**
+ * Utility function to test window destruction behavior. In general this
+ * function does the following:
+ *
+ * 1) Create a new iframe
+ * 2) Create a new DOMRequestHelper
+ * 3) initDOMRequestHelper(), optionally with weak or strong listeners
+ * 4) Optionally force a garbage collection to reap weak references
+ * 5) Destroy the iframe triggering an inner-window-destroyed event
+ * 6) Callback with a boolean indicating if DOMRequestHelper.uninit() was
+ * called.
+ *
+ * Example usage:
+ *
+ * checkWindowDestruction({ messages: ["foo"], gc: true },
+ * function(uninitCalled) {
+ * // expect uninitCalled === false since GC with only weak refs
+ * });
+ */
+ const TOPIC = "inner-window-destroyed";
+ function checkWindowDestruction(aOptions, aCallback) {
+ aOptions = aOptions || {};
+ aOptions.messages = aOptions.messages || [];
+ aOptions.gc = !!aOptions.gc;
+
+ if (typeof aCallback !== "function") {
+ aCallback = function() { };
+ }
+
+ let uninitCalled = false;
+
+ // Use a secondary observer so we know when to expect the uninit(). We
+ // can then reasonably expect uninitCalled to be set properly on the
+ // next tick.
+ let observer = {
+ observe(aSubject, aTopic, aData) {
+ if (aTopic !== TOPIC) {
+ return;
+ }
+ obs.removeObserver(observer, TOPIC);
+ setTimeout(function() {
+ aCallback(uninitCalled);
+ });
+ }
+ };
+
+ let frame = document.createXULElement("iframe");
+ frame.onload = function() {
+ obs.addObserver(observer, TOPIC);
+ // Create dummy DOMRequestHelper specific to checkWindowDestruction()
+ let cwdDummy = new DummyHelperSubclass();
+ cwdDummy.onuninit = function() {
+ uninitCalled = true;
+
+ if (!aOptions.messages || !aOptions.messages.length) {
+ return;
+ }
+
+ // If all message listeners are removed, cwdDummy.receiveMessage
+ // should never be called.
+ ppmm.broadcastAsyncMessage(aOptions.messages[0].name);
+ };
+
+ // Test if we still receive messages from ppmm.
+ cwdDummy.receiveMessage = function(aMessage) {
+ ok(false, "cwdDummy.receiveMessage should NOT be called: " + aMessage.name);
+ };
+
+ cwdDummy.initDOMRequestHelper(frame.contentWindow, aOptions.messages);
+ // Make sure to clear our strong ref here so that we can test our
+ // weak reference listeners and observer.
+ cwdDummy = null;
+ if (aOptions.gc) {
+ Cu.schedulePreciseGC(function() {
+ SpecialPowers.DOMWindowUtils.cycleCollect();
+ SpecialPowers.DOMWindowUtils.garbageCollect();
+ SpecialPowers.DOMWindowUtils.garbageCollect();
+ document.documentElement.removeChild(frame);
+ });
+ return;
+ }
+ document.documentElement.removeChild(frame);
+ };
+ document.documentElement.appendChild(frame);
+ }
+
+ /**
+ * Test steps.
+ */
+ var tests = [
+ function() {
+ info("== InitDOMRequestHelper no messages");
+ initDOMRequestHelperTest(null);
+ next();
+ },
+ function() {
+ info("== DestroyDOMRequestHelper");
+ destroyDOMRequestHelperTest();
+ next();
+ },
+ function() {
+ info("== InitDOMRequestHelper empty array");
+ initDOMRequestHelperTest([]);
+ checkMessageListeners({}, 0);
+ next();
+ },
+ function() {
+ info("== DestroyDOMRequestHelper");
+ destroyDOMRequestHelperTest();
+ next();
+ },
+ function() {
+ info("== InitDOMRequestHelper with strings array");
+ initDOMRequestHelperTest(["name1", "nameN"]);
+ checkMessageListeners({"name1": {weakRef: false, count: 1},
+ "nameN": {weakRef: false, count: 1}}, 2);
+ next();
+ },
+ function() {
+ info("== DestroyDOMRequestHelper");
+ destroyDOMRequestHelperTest();
+ next();
+ },
+ function() {
+ info("== InitDOMRequestHelper with objects array");
+ initDOMRequestHelperTest([{
+ name: "name1",
+ weakRef: false
+ }, {
+ name: "nameN",
+ weakRef: true
+ }]);
+ checkMessageListeners({
+ "name1": {weakRef: false, count: 1},
+ "nameN": {weakRef: true, count: 1}
+ }, 2);
+ next();
+ },
+ function() {
+ info("== AddMessageListeners empty array");
+ addMessageListenersTest([], {
+ "name1": {weakRef: false, count: 1},
+ "nameN": {weakRef: true, count: 1}
+ }, 2);
+ next();
+ },
+ function() {
+ info("== AddMessageListeners null");
+ addMessageListenersTest(null, {
+ "name1": {weakRef: false, count: 1},
+ "nameN": {weakRef: true, count: 1}
+ }, 2);
+ next();
+ },
+ function() {
+ info("== AddMessageListeners new listener, string only");
+ addMessageListenersTest("name2", {
+ "name1": {weakRef: false, count: 1},
+ "name2": {weakRef: false, count: 1},
+ "nameN": {weakRef: true, count: 1}
+ }, 3);
+ next();
+ },
+ function() {
+ info("== AddMessageListeners new listeners, strings array");
+ addMessageListenersTest(["name3", "name4"], {
+ "name1": {weakRef: false, count: 1},
+ "name2": {weakRef: false, count: 1},
+ "name3": {weakRef: false, count: 1},
+ "name4": {weakRef: false, count: 1},
+ "nameN": {weakRef: true, count: 1}
+ }, 5);
+ next();
+ },
+ function() {
+ info("== AddMessageListeners new listeners, objects array");
+ addMessageListenersTest([{
+ name: "name5",
+ weakRef: true
+ }, {
+ name: "name6",
+ weakRef: false
+ }], {
+ "name1": {weakRef: false, count: 1},
+ "name2": {weakRef: false, count: 1},
+ "name3": {weakRef: false, count: 1},
+ "name4": {weakRef: false, count: 1},
+ "name5": {weakRef: true, count: 1},
+ "name6": {weakRef: false, count: 1},
+ "nameN": {weakRef: true, count: 1}
+ }, 7);
+ next();
+ },
+ function() {
+ info("== RemoveMessageListeners, null");
+ removeMessageListenersTest(null, {
+ "name1": {weakRef: false, count: 1},
+ "name2": {weakRef: false, count: 1},
+ "name3": {weakRef: false, count: 1},
+ "name4": {weakRef: false, count: 1},
+ "name5": {weakRef: true, count: 1},
+ "name6": {weakRef: false, count: 1},
+ "nameN": {weakRef: true, count: 1}
+ }, 7);
+ next();
+ },
+ function() {
+ info("== RemoveMessageListeners, one message");
+ removeMessageListenersTest("name1", {
+ "name2": {weakRef: false, count: 1},
+ "name3": {weakRef: false, count: 1},
+ "name4": {weakRef: false, count: 1},
+ "name5": {weakRef: true, count: 1},
+ "name6": {weakRef: false, count: 1},
+ "nameN": {weakRef: true, count: 1}
+ }, 6);
+ next();
+ },
+ function() {
+ info("== RemoveMessageListeners, array of messages");
+ removeMessageListenersTest(["name2", "name3"], {
+ "name4": {weakRef: false, count: 1},
+ "name5": {weakRef: true, count: 1},
+ "name6": {weakRef: false, count: 1},
+ "nameN": {weakRef: true, count: 1}
+ }, 4);
+ next();
+ },
+ function() {
+ info("== RemoveMessageListeners, unknown message");
+ removeMessageListenersTest("unknown", {
+ "name4": {weakRef: false, count: 1},
+ "name5": {weakRef: true, count: 1},
+ "name6": {weakRef: false, count: 1},
+ "nameN": {weakRef: true, count: 1}
+ }, 4);
+ next();
+ },
+ function() {
+ try {
+ info("== AddMessageListeners, same message, same kind");
+ addMessageListenersTest("name4", {
+ "name4": {weakRef: false, count: 2},
+ "name5": {weakRef: true, count: 1},
+ "name6": {weakRef: false, count: 1},
+ "nameN": {weakRef: true, count: 1}
+ }, 4);
+ next();
+ } catch (ex) {
+ ok(false, "Unexpected exception " + ex);
+ }
+ },
+ function() {
+ info("== AddMessageListeners, same message, different kind");
+ try {
+ addMessageListenersTest({name: "name4", weakRef: true}, {
+ "name4": {weakRef: false, count: 2},
+ "name5": {weakRef: true, count: 1},
+ "name6": {weakRef: false, count: 1},
+ "nameN": {weakRef: true, count: 1}
+ }, 4);
+ ok(false, "Should have thrown an exception");
+ } catch (ex) {
+ ok(true, "Expected exception");
+ next();
+ }
+ },
+ function() {
+ info("== RemoveMessageListeners, message with two listeners");
+ try {
+ removeMessageListenersTest(["name4", "name5"], {
+ "name4": {weakRef: false, count: 1},
+ "name6": {weakRef: false, count: 1},
+ "nameN": {weakRef: true, count: 1}
+ }, 3);
+ next();
+ } catch (ex) {
+ ok(false, "Unexpected exception " + ex);
+ }
+ },
+ function() {
+ info("== Test createRequest()");
+ ok(DOMRequest, "DOMRequest object exists");
+ var req = dummy.createRequest();
+ ok(DOMRequest.isInstance(req), "Returned a DOMRequest");
+ next();
+ },
+ function() {
+ info("== Test getRequestId(), removeRequest() and getRequest()");
+ var req = dummy.createRequest();
+ var id = dummy.getRequestId(req);
+ is(typeof id, "string", "id is a string");
+ var req_ = dummy.getRequest(id);
+ is(req, req_, "Got correct request");
+ dummy.removeRequest(id);
+ req = dummy.getRequest(id);
+ is(req, undefined, "No request");
+ next();
+ },
+ function() {
+ info("== Test createPromise()");
+ ok(Promise, "Promise object exists");
+ var promise = dummy.createPromise(function(resolve, reject) {
+ resolve(true);
+ });
+ ok(promise instanceof Promise, "Returned a Promise");
+ promise.then(next);
+ },
+ function() {
+ info("== Test createPromiseWithId()");
+ var _resolverId;
+ var promise = dummy.createPromiseWithId(function(resolverId) {
+ _resolverId = resolverId;
+ });
+ var resolver = dummy.getPromiseResolver(_resolverId);
+ ok(promise instanceof Promise, "Returned a Promise");
+ ok(typeof _resolverId === "string", "resolverId is a string");
+ ok(resolver != null, "resolverId is a valid id");
+ next();
+ },
+ function() {
+ info("== Test getResolver()");
+ var id;
+ var resolver;
+ var promise = dummy.createPromise(function(resolve, reject) {
+ var r = { resolve, reject };
+ id = dummy.getPromiseResolverId(r);
+ resolver = r;
+ ok(typeof id === "string", "id is a string");
+ r.resolve(true);
+ }).then(function(unused) {
+ var r = dummy.getPromiseResolver(id);
+ ok(resolver === r, "Get succeeded");
+ next();
+ });
+ },
+ function() {
+ info("== Test removeResolver");
+ var id;
+ var promise = dummy.createPromise(function(resolve, reject) {
+ var r = { resolve, reject };
+ id = dummy.getPromiseResolverId(r);
+ ok(typeof id === "string", "id is a string");
+
+ var resolver = dummy.getPromiseResolver(id);
+ info("Got resolver " + JSON.stringify(resolver));
+ ok(resolver === r, "Resolver get succeeded");
+
+ r.resolve(true);
+ }).then(function(unused) {
+ dummy.removePromiseResolver(id);
+ var resolver = dummy.getPromiseResolver(id);
+ ok(resolver === undefined, "removeResolver: get failed");
+ next();
+ });
+ },
+ function() {
+ info("== Test takeResolver");
+ var id;
+ var resolver;
+ var promise = dummy.createPromise(function(resolve, reject) {
+ var r = { resolve, reject };
+ id = dummy.getPromiseResolverId(r);
+ resolver = r;
+ ok(typeof id === "string", "id is a string");
+
+ var gotR = dummy.getPromiseResolver(id);
+ ok(gotR === r, "resolver get succeeded");
+
+ r.resolve(true);
+ }).then(function(unused) {
+ var r = dummy.takePromiseResolver(id);
+ ok(resolver === r, "take should succeed");
+
+ r = dummy.getPromiseResolver(id);
+ ok(r === undefined, "takeResolver: get failed");
+ next();
+ });
+ },
+ function() {
+ info("== Test window destroyed without messages and without GC");
+ checkWindowDestruction({ gc: false }, function(uninitCalled) {
+ ok(uninitCalled, "uninit() should have been called");
+ next();
+ });
+ },
+ function() {
+ info("== Test window destroyed without messages and with GC");
+ checkWindowDestruction({ gc: true }, function(uninitCalled) {
+ ok(!uninitCalled, "uninit() should NOT have been called");
+ next();
+ });
+ },
+ function() {
+ info("== Test window destroyed with weak messages and without GC");
+ checkWindowDestruction({ messages: [{ name: "foo", weakRef: true }],
+ gc: false }, function(uninitCalled) {
+ ok(uninitCalled, "uninit() should have been called");
+ next();
+ });
+ },
+ function() {
+ info("== Test window destroyed with weak messages and with GC");
+ checkWindowDestruction({ messages: [{ name: "foo", weakRef: true }],
+ gc: true }, function(uninitCalled) {
+ ok(!uninitCalled, "uninit() should NOT have been called");
+ next();
+ });
+ },
+ function() {
+ info("== Test window destroyed with strong messages and without GC");
+ checkWindowDestruction({ messages: [{ name: "foo", weakRef: false }],
+ gc: false }, function(uninitCalled) {
+ ok(uninitCalled, "uninit() should have been called");
+ next();
+ });
+ },
+ function() {
+ info("== Test window destroyed with strong messages and with GC");
+ checkWindowDestruction({ messages: [{ name: "foo", weakRef: false }],
+ gc: true }, function(uninitCalled) {
+ ok(uninitCalled, "uninit() should have been called");
+ next();
+ });
+ }
+ ];
+
+ function next() {
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ var test = tests.shift();
+ test();
+ }
+
+ function start() {
+ SimpleTest.waitForExplicitFinish();
+ next();
+ }
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p id="display"></p>
+ <div id="content" style="display: none"></div>
+ <pre id="test"></pre>
+ </body>
+</window>