gecko-dev/dom/base/test/chrome/cpows_parent.xul

509 строки
18 KiB
XML

<?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;
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(!Cu.isCrossProcessWrapper(message.json), "not everything is a CPOW");
let data = message.objects.data;
let document = message.objects.document;
if (test_state == "remote") {
ok(Cu.isCrossProcessWrapper(data), "got a CPOW");
ok(Cu.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.remove();
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(Ci.nsISupports), savedElement,
"QI to nsISupports works");
function testNoInterface(savedElement, i) {
try {
savedElement.QueryInterface(i);
ok(false, "should have thrown an exception");
} catch (e) {
is(e.result, Cr.NS_ERROR_NO_INTERFACE, "threw the right exception");
}
}
testNoInterface(savedElement, Ci.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");
// We want to test what happens when a CPOW is passed through XPConnect
// as an XPCOM interface. But elements are losing all their XPCOM
// interfaces, so let's use an object that will likely stay an XPCOM
// one.
let docshell = savedElement.ownerGlobal.docShell;
ok(docshell, "We should have a docshell here!");
let secureUI = Cc['@mozilla.org/secure_browser_ui;1']
.createInstance(Ci.nsISecureBrowserUI);
try {
secureUI.init(docshell);
ok(false, "expected exception passing CPOW to C++");
} catch (e) {
is(e.result, Cr.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.getRealmLocation(getUnprivilegedObject);
ok(/shared JSM global/.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;
Cu.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))) {
throw 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))) {
throw 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))) {
throw 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))) {
throw 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))) {
throw 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>