gecko/dom/base/test/test_domrequesthelper.xul

553 lines
19 KiB
XML

<?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 type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<script type="application/javascript">
<![CDATA[
const Cc = Components.classes;
const Cu = Components.utils;
const Ci = Components.interfaces;
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
let obs = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"].
getService(Ci.nsIMessageBroadcaster);
function DummyHelperSubclass() {
this.onuninit = null;
}
DummyHelperSubclass.prototype = {
__proto__: DOMRequestIpcHelper.prototype,
uninit: function() {
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: function(aSubject, aTopic, aData) {
if (aTopic !== TOPIC) {
return;
}
obs.removeObserver(observer, TOPIC);
setTimeout(function() {
aCallback(uninitCalled);
});
}
};
let frame = document.createElement("iframe");
frame.onload = function() {
obs.addObserver(observer, TOPIC, false);
// 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(req instanceof DOMRequest, "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: resolve, reject: 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: resolve, reject: 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: resolve, reject: 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>