summaryrefslogtreecommitdiffstats
path: root/dom/base/test/chrome/cpows_parent.xul
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base/test/chrome/cpows_parent.xul')
-rw-r--r--dom/base/test/chrome/cpows_parent.xul493
1 files changed, 493 insertions, 0 deletions
diff --git a/dom/base/test/chrome/cpows_parent.xul b/dom/base/test/chrome/cpows_parent.xul
new file mode 100644
index 000000000..f633f0a79
--- /dev/null
+++ b/dom/base/test/chrome/cpows_parent.xul
@@ -0,0 +1,493 @@
+<?xml version="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="MessageManager CPOW tests"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="start()">
+
+ <!-- test results are displayed in the html:body -->
+ <label value="CPOWs"/>
+
+ <script type="application/javascript"><![CDATA[
+ var test_state = "remote";
+ var test_node = null;
+ var reentered = false;
+ var savedMM = null;
+ const Cu = Components.utils;
+
+ function info(message) {
+ return opener.wrappedJSObject.info(message);
+ }
+
+ function ok(condition, message) {
+ return opener.wrappedJSObject.ok(condition, message);
+ }
+
+ function is(v1, v2, message) {
+ return opener.wrappedJSObject.is(v1, v2, message);
+ }
+
+ function todo_is(v1, v2, message) {
+ return opener.wrappedJSObject.todo_is(v1, v2, message);
+ }
+
+ // Make sure that an error in this file actually causes the test to fail.
+ var gReceivedErrorProbe = false;
+ window.onerror = function (msg, url, line) {
+ if (/Test Error Probe/.test(msg)) {
+ gReceivedErrorProbe = true;
+ return;
+ }
+ ok(false, "Error while executing: \n" + msg + "\n" + url + ":" + line);
+ };
+
+ function testCpowMessage(message) {
+ ok(message.json.check == "ok", "correct json");
+
+ ok(!Components.utils.isCrossProcessWrapper(message.json), "not everything is a CPOW");
+
+ let data = message.objects.data;
+ let document = message.objects.document;
+ if (test_state == "remote") {
+ ok(Components.utils.isCrossProcessWrapper(data), "got a CPOW");
+ ok(Components.utils.isCrossProcessWrapper(document), "got a CPOW");
+ }
+ ok(data.i === 5, "integer property");
+ ok(data.b === true, "boolean property");
+ ok(data.s === "hello", "string property");
+ ok(data.x.i === 10, "nested property");
+ ok(data.f() === 99, "function call");
+ is(Object.getOwnPropertyDescriptor(data, "doesn't exist"), undefined,
+ "getOwnPropertyDescriptor returns undefined for non-existant properties");
+ ok(Object.getOwnPropertyDescriptor(data, "i").value, 5,
+ "getOwnPropertyDescriptor.value works");
+ let obj = new data.ctor();
+ ok(obj.a === 3, "constructor call");
+ is(document.title, "Hello, Kitty", "document node");
+ is(typeof document.cookie, "string", "can get document.cookie");
+ is(typeof document.defaultView.navigator.userAgent, "string", "can get navigator.userAgent");
+
+ // Don't crash.
+ document.defaultView.screen;
+
+ data.i = 6;
+ data.b = false;
+ data.s = "bye";
+ data.x = null;
+ ok(data.i === 6, "integer property");
+ ok(data.b === false, "boolean property");
+ ok(data.s === "bye", "string property");
+ ok(data.x === null, "nested property");
+
+ let throwing = message.objects.throwing;
+ // Based on the table on:
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
+ let tests = [
+ () => Object.getOwnPropertyDescriptor(throwing, 'test'),
+ () => Object.getOwnPropertyNames(throwing),
+ () => Object.defineProperty(throwing, 'test', {value: 1}),
+ () => delete throwing.test,
+ () => "test" in throwing,
+ () => Object.prototype.hasOwnProperty.call(throwing, 'test'),
+ () => throwing.test,
+ () => { throwing.test = 1 },
+ // () => { for (let prop in throwing) {} }, Bug 783829
+ () => { for (let prop of throwing) {} },
+ () => Object.keys(throwing),
+ () => Function.prototype.call.call(throwing),
+ () => new throwing,
+ () => Object.preventExtensions(throwing),
+ () => Object.freeze(throwing),
+ () => Object.seal(throwing),
+ ]
+
+ for (let test of tests) {
+ let threw = false;
+ try {
+ test()
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw, "proxy operation threw exception");
+ }
+
+ let array = message.objects.array;
+ let i = 1;
+ for (let elt of array) {
+ ok(elt === i, "correct element found");
+ i++;
+ }
+ ok(i === 4, "array has correct length");
+
+ let j = message.objects.for_json;
+ let str = JSON.stringify(j);
+ let j2 = JSON.parse(str);
+ ok(j2.n === 3, "JSON integer property");
+ ok(j2.a[0] === 1, "JSON array index");
+ ok(j2.a[1] === 2, "JSON array index");
+ ok(j2.a[2] === 3, "JSON array index");
+ ok(j2.s === "hello", "JSON string property");
+ ok(j2.o.x === 10, "JSON object property");
+
+ let with_proto = message.objects.with_proto;
+ let proto = Object.getPrototypeOf(with_proto);
+ ok(proto.data == 42, "Object.getPrototypeOf works on CPOW");
+
+ let with_null_proto = message.objects.with_null_proto;
+ proto = Object.getPrototypeOf(with_null_proto);
+ ok(proto === null, "Object.getPrototypeOf works on CPOW (null proto)");
+ }
+
+ function recvAsyncMessage(message) {
+ testCpowMessage(message);
+ savedMM.sendAsyncMessage("cpows:async_done");
+ }
+
+ function recvSyncMessage(message) {
+ testCpowMessage(message);
+ }
+
+ function recvRpcMessage(message) {
+ ok(message.json.check == "ok", "correct json");
+
+ let data = message.objects.data;
+
+ // Sanity check.
+ ok(data.i === 5, "integer property");
+
+ // Check that we re-enter.
+ reentered = false;
+ let result = data.reenter();
+ ok(reentered, "re-entered rpc");
+ ok(result == "ok", "got correct result");
+ }
+
+ function recvReenterMessage(message) {
+ ok(message.objects.data.valid === true, "cpows work");
+ reentered = true;
+ }
+
+ function recvNestedSyncMessage(message) {
+ message.objects.data.reenter();
+ }
+
+ function recvReenterSyncMessage(message) {
+ ok(false, "should not have received re-entered sync message");
+ }
+
+ function recvFailMessage(message) {
+ ok(false, message.json.message);
+ }
+
+ function recvDoneMessage(message) {
+ if (test_state == "remote") {
+ test_node.parentNode.removeChild(test_node);
+ run_tests("inprocess");
+ return;
+ }
+
+ finish();
+ }
+
+ function recvParentTest(message) {
+ let func = message.objects.func;
+ let result = func(n => 2*n);
+ ok(result == 20, "result == 20");
+ function f() {
+ return 101;
+ }
+ let obj = {a:1, __exposedProps__: {"a": "r"}};
+ savedMM.sendAsyncMessage("cpows:from_parent", {}, {obj: obj, func: f});
+ }
+
+ // Make sure errors in this file actually hit window.onerror.
+ function recvErrorReportingTest(message) {
+ throw "Test Error Probe";
+ }
+
+ let savedElement = null;
+ function recvDomTest(message) {
+ savedElement = message.objects.element;
+
+ is(savedElement.QueryInterface(Components.interfaces.nsISupports), savedElement,
+ "QI to nsISupports works");
+ is(savedElement.QueryInterface(Components.interfaces.nsIDOMNode), savedElement,
+ "QI to a random (implemented) interface works");
+
+ function testNoInterface(savedElement, i) {
+ try {
+ savedElement.QueryInterface(i);
+ ok(false, "should have thrown an exception");
+ } catch (e) {
+ is(e.result, Components.results.NS_ERROR_NO_INTERFACE, "threw the right exception");
+ }
+ }
+
+ testNoInterface(savedElement, Components.interfaces.nsIDOMAttr);
+ testNoInterface(savedElement, Components.interfaces.nsIClassInfo);
+
+ // Test to ensure that we don't pass CPOWs to C++-implemented interfaces.
+ // See bug 1072980.
+ if (test_state == "remote") {
+ // This doesn't work because we intercept toString and QueryInterface specially
+ // and don't cache the function pointer.
+ // See bug 1140636.
+ todo_is(savedElement.toString, savedElement.toString, "toString identity works");
+ todo_is(savedElement.QueryInterface, savedElement.QueryInterface, "toString identity works");
+
+ is(Object.prototype.toString.call(savedElement), "[object HTMLDivElement]",
+ "prove that this works (and doesn't leak)");
+
+ is(Object.prototype.toString.call(savedElement), "[object HTMLDivElement]",
+ "prove that this works twice (since we cache it and doesn't leak)");
+
+ // This does work because we create a CPOW for isEqualNode that stays
+ // alive as long as we have a reference to the first CPOW (so as long
+ // as it's detectable).
+ is(savedElement.isEqualNode, savedElement.isEqualNode, "webidl function identity works");
+
+ let walker = Components.classes["@mozilla.org/inspector/deep-tree-walker;1"]
+ .createInstance(Components.interfaces.inIDeepTreeWalker);
+ const SHOW_ELEMENT = Components.interfaces.nsIDOMNodeFilter.SHOW_ELEMENT;
+ walker.showAnonymousContent = true;
+ walker.showSubDocuments = false;
+
+ try {
+ walker.init(savedElement, SHOW_ELEMENT);
+ ok(false, "expected exception passing CPOW to C++");
+ } catch (e) {
+ is(e.result, Components.results.NS_ERROR_XPC_CANT_PASS_CPOW_TO_NATIVE,
+ "got exception when passing CPOW to C++");
+ }
+ }
+ }
+
+ function recvDomTestAfterGC(message) {
+ let id;
+ try {
+ id = savedElement.id;
+ } catch (e) {
+ ok(false, "Got exception using DOM element");
+ }
+ is(id, "it_works", "DOM element has expected ID");
+ }
+
+ function recvXrayTest(message) {
+ let element = message.objects.element;
+ is(element.foo, undefined, "DOM element does not expose content properties");
+ }
+
+ function recvSymbolTest(message) {
+ let object = message.objects.object;
+ is(object[Symbol.iterator], Symbol.iterator, "Should use Symbol.iterator");
+ is(Symbol.keyFor(object[Symbol.for("cpow-test")]), "cpow-test", "Symbols aren't registered correctly");
+ let symbols = Object.getOwnPropertySymbols(object);
+ is(symbols.length, 2, "Object should have two symbol keys");
+ let test = undefined;
+ for (let x of message.objects.test) {
+ test = x;
+ }
+ is(test, "a", "for .. of iteration should work");
+ }
+
+ let systemGlobal = this;
+ function recvCompartmentTest(message) {
+ let getUnprivilegedObject = message.objects.getUnprivilegedObject;
+ let testParentObject = message.objects.testParentObject;
+
+ // Make sure that parent->child CPOWs live in the parent's privileged junk scope.
+ let unprivilegedObject = getUnprivilegedObject();
+ is(Cu.getGlobalForObject(getUnprivilegedObject),
+ Cu.getGlobalForObject(unprivilegedObject),
+ "all parent->child CPOWs should live in the same scope");
+ let cpowLocation = Cu.getCompartmentLocation(getUnprivilegedObject);
+ ok(/Privileged Junk/.test(cpowLocation),
+ "parent->child CPOWs should live in the privileged junk scope: " + cpowLocation);
+
+ // Make sure that parent->child CPOWs point through a privileged scope in the child
+ // (the privileged junk scope, but we don't have a good way to test for that
+ // specifically).
+ is(unprivilegedObject.expando, undefined, "parent->child references should get Xrays");
+ is(unprivilegedObject.wrappedJSObject.expando, 42, "parent->child references should get waivable Xrays");
+
+ // Send an object to the child to let it verify invariants in the other direction.
+ function passMe() { return 42; };
+ passMe.expando = 42;
+ let results = testParentObject(passMe);
+ ok(results.length > 0, "Need results");
+ results.forEach((x) => is(x.result, "PASS", x.message));
+ }
+
+ function recvRegExpTest(message) {
+ let regexp = message.objects.regexp;
+
+ // These work generically.
+ is(regexp.toString(), "/myRegExp/g", "toString works right");
+ ok(regexp.test("I like myRegExp to match"), "No false positives");
+ ok(!regexp.test("asdfsdf"), "No false positives");
+
+ // These go over regexp_toShared.
+ is("filler myRegExp filler".search(regexp), 7, "String.prototype.match works right");
+ var shell = /x/;
+ shell.compile(regexp);
+ is(regexp.toString(), shell.toString(), ".compile works right");
+ }
+
+ function recvPostMessageTest(message) {
+ let win = message.objects.win;
+ win.postMessage('nookery', '*');
+ ok(true, "Didn't crash invoking postMessage over CPOW");
+ }
+
+ let savedWilldieObj;
+ let wontDie = {f:2, __exposedProps__: {"f": "r"}};
+ function recvLifetimeTest1(message) {
+ let obj = message.objects.obj;
+ savedWilldieObj = obj.will_die;
+ ok(savedWilldieObj.f == 1, "limited-lifetime CPOW works at first");
+ obj.wont_die = wontDie;
+ obj = null;
+ return 10;
+ }
+ function recvLifetimeTest2(message) {
+ let threw = false;
+ try {
+ savedWilldieObj.f;
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw, "limited-lifetime CPOW stopped working");
+ wontDie = null;
+ Components.utils.schedulePreciseGC(function() {
+ savedMM.sendAsyncMessage("cpows:lifetime_test_3");
+ });
+ }
+
+ function recvCancelTest(msg) {
+ let failed = false;
+ try {
+ msg.objects.f();
+ } catch (e if /cross-process JS call failed/.test(String(e))) {
+ failed = true;
+ }
+ ok(failed, "CPOW should fail due to cancelation");
+ msg.target.messageManager.sendAsyncMessage("cpows:cancel_test_done");
+ }
+
+ function recvCancelSyncMessage() {
+ return 12;
+ }
+
+ function recvCancelTest2(msg) {
+ let failed = false;
+ try {
+ msg.objects.f();
+ } catch (e if /cross-process JS call failed/.test(String(e))) {
+ failed = true;
+ }
+ ok(failed, "CPOW should fail due to cancelation");
+ msg.target.messageManager.sendAsyncMessage("cpows:cancel_test2_done");
+ }
+
+ function recvUnsafe(msg) {
+ let failed = false;
+
+ const PREF_UNSAFE_FORBIDDEN = "dom.ipc.cpows.forbid-unsafe-from-browser";
+ opener.wrappedJSObject.SpecialPowers.setBoolPref(PREF_UNSAFE_FORBIDDEN, true);
+ try {
+ msg.objects.f();
+ } catch (e if /unsafe CPOW usage forbidden/.test(String(e))) {
+ failed = true;
+ }
+ opener.wrappedJSObject.SpecialPowers.clearUserPref(PREF_UNSAFE_FORBIDDEN);
+ ok(failed, "CPOW should fail when unsafe");
+ msg.target.messageManager.sendAsyncMessage("cpows:unsafe_done");
+ }
+
+ function recvSafe(msg) {
+ const PREF_UNSAFE_FORBIDDEN = "dom.ipc.cpows.forbid-unsafe-from-browser";
+ opener.wrappedJSObject.SpecialPowers.setBoolPref(PREF_UNSAFE_FORBIDDEN, true);
+ try {
+ msg.objects.f();
+ } catch (e if /unsafe CPOW usage forbidden/.test(String(e))) {
+ ok(false, "cpow failed");
+ }
+ opener.wrappedJSObject.SpecialPowers.clearUserPref(PREF_UNSAFE_FORBIDDEN);
+ msg.target.messageManager.sendAsyncMessage("cpows:safe_done");
+ }
+
+ function recvDead(msg) {
+ // Need to do this in a separate turn of the event loop.
+ setTimeout(() => {
+ msg.objects.gcTrigger();
+ try {
+ msg.objects.thing.value;
+ ok(false, "Should have been a dead CPOW");
+ } catch(e if /dead CPOW/.test(String(e))) {
+ ok(true, "Got the expected dead CPOW");
+ ok(e.stack, "The exception has a stack");
+ }
+ msg.target.messageManager.sendAsyncMessage("cpows:dead_done");
+ }, 0);
+ }
+
+ function run_tests(type) {
+ info("Running tests: " + type);
+ var node = document.getElementById('cpowbrowser_' + type);
+
+ test_state = type;
+ test_node = node;
+
+ function recvIsRemote(message) {
+ return type == "remote";
+ }
+
+ var mm = node.messageManager;
+ savedMM = mm;
+ mm.addMessageListener("cpows:is_remote", recvIsRemote);
+ mm.addMessageListener("cpows:async", recvAsyncMessage);
+ mm.addMessageListener("cpows:sync", recvSyncMessage);
+ mm.addMessageListener("cpows:rpc", recvRpcMessage);
+ mm.addMessageListener("cpows:reenter", recvReenterMessage);
+ mm.addMessageListener("cpows:reenter", recvReenterMessage);
+ mm.addMessageListener("cpows:nested_sync", recvNestedSyncMessage);
+ mm.addMessageListener("cpows:reenter_sync", recvReenterSyncMessage);
+ mm.addMessageListener("cpows:done", recvDoneMessage);
+ mm.addMessageListener("cpows:fail", recvFailMessage);
+ mm.addMessageListener("cpows:parent_test", recvParentTest);
+ mm.addMessageListener("cpows:error_reporting_test", recvErrorReportingTest);
+ mm.addMessageListener("cpows:dom_test", recvDomTest);
+ mm.addMessageListener("cpows:dom_test_after_gc", recvDomTestAfterGC);
+ mm.addMessageListener("cpows:xray_test", recvXrayTest);
+ if (typeof Symbol === "function") {
+ mm.addMessageListener("cpows:symbol_test", recvSymbolTest);
+ }
+ mm.addMessageListener("cpows:compartment_test", recvCompartmentTest);
+ mm.addMessageListener("cpows:regexp_test", recvRegExpTest);
+ mm.addMessageListener("cpows:postmessage_test", recvPostMessageTest);
+ mm.addMessageListener("cpows:lifetime_test_1", recvLifetimeTest1);
+ mm.addMessageListener("cpows:lifetime_test_2", recvLifetimeTest2);
+ mm.addMessageListener("cpows:cancel_test", recvCancelTest);
+ mm.addMessageListener("cpows:cancel_sync_message", recvCancelSyncMessage);
+ mm.addMessageListener("cpows:cancel_test2", recvCancelTest2);
+ mm.addMessageListener("cpows:unsafe", recvUnsafe);
+ mm.addMessageListener("cpows:safe", recvSafe);
+ mm.addMessageListener("cpows:dead", recvDead);
+ mm.loadFrameScript("chrome://mochitests/content/chrome/dom/base/test/chrome/cpows_child.js", true);
+ }
+
+ function start() {
+ run_tests('remote');
+ }
+
+ function finish() {
+ ok(gReceivedErrorProbe, "Should have reported error probe");
+ opener.setTimeout("done()", 0);
+ window.close();
+ }
+ ]]></script>
+
+ <browser type="content" src="about:blank" id="cpowbrowser_remote" remote="true"/>
+ <browser type="content" src="about:blank" id="cpowbrowser_inprocess"/>
+</window>