diff options
Diffstat (limited to 'dom/base/test/chrome/cpows_parent.xul')
-rw-r--r-- | dom/base/test/chrome/cpows_parent.xul | 493 |
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> |