summaryrefslogtreecommitdiffstats
path: root/dom/base/test/chrome/cpows_child.js
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base/test/chrome/cpows_child.js')
-rw-r--r--dom/base/test/chrome/cpows_child.js382
1 files changed, 382 insertions, 0 deletions
diff --git a/dom/base/test/chrome/cpows_child.js b/dom/base/test/chrome/cpows_child.js
new file mode 100644
index 000000000..28ae4d1a7
--- /dev/null
+++ b/dom/base/test/chrome/cpows_child.js
@@ -0,0 +1,382 @@
+dump('loaded child cpow test\n');
+
+var Cu = Components.utils;
+var Ci = Components.interfaces;
+
+(function start() {
+ [is_remote] = sendRpcMessage("cpows:is_remote");
+
+ var tests = [
+ parent_test,
+ error_reporting_test,
+ dom_test,
+ xray_test,
+ symbol_test,
+ compartment_test,
+ regexp_test,
+ postmessage_test,
+ sync_test,
+ async_test,
+ rpc_test,
+ lifetime_test,
+ cancel_test,
+ cancel_test2,
+ dead_test,
+ unsafe_test,
+ ];
+
+ function go() {
+ if (tests.length == 0) {
+ sendRpcMessage("cpows:done", {});
+ return;
+ }
+
+ var test = tests[0];
+ tests.shift();
+ test(function() {
+ go();
+ });
+ }
+
+ go();
+})();
+
+function ok(condition, message) {
+ dump('condition: ' + condition + ', ' + message + '\n');
+ if (!condition) {
+ sendAsyncMessage("cpows:fail", { message: message });
+ throw 'failed check: ' + message;
+ }
+}
+
+var sync_obj;
+var async_obj;
+
+function make_object()
+{
+ let o = { };
+ o.i = 5;
+ o.b = true;
+ o.s = "hello";
+ o.x = { i: 10 };
+ o.f = function () { return 99; };
+ o.ctor = function() { this.a = 3; }
+
+ // Doing anything with this Proxy will throw.
+ var throwing = new Proxy({}, new Proxy({}, {
+ get: function (trap) { throw trap; }
+ }));
+
+ let array = [1, 2, 3];
+
+ let for_json = { "n": 3, "a": array, "s": "hello", o: { "x": 10 } };
+
+ let proto = { data: 42 };
+ let with_proto = Object.create(proto);
+
+ let with_null_proto = Object.create(null);
+
+ content.document.title = "Hello, Kitty";
+ return { "data": o,
+ "throwing": throwing,
+ "document": content.document,
+ "array": array,
+ "for_json": for_json,
+ "with_proto": with_proto,
+ "with_null_proto": with_null_proto
+ };
+}
+
+function make_json()
+{
+ return { check: "ok" };
+}
+
+function parent_test(finish)
+{
+ function f(check_func) {
+ // Make sure this doesn't crash.
+ let array = new Uint32Array(10);
+ content.crypto.getRandomValues(array);
+
+ let result = check_func(10);
+ ok(result == 20, "calling function in parent worked");
+ return result;
+ }
+
+ addMessageListener("cpows:from_parent", (msg) => {
+ let obj = msg.objects.obj;
+ ok(obj.a == 1, "correct value from parent");
+
+ // Test that a CPOW reference to a function in the chrome process
+ // is callable from unprivileged content. Greasemonkey uses this
+ // functionality.
+ let func = msg.objects.func;
+ let sb = Cu.Sandbox('http://www.example.com', {});
+ sb.func = func;
+ ok(sb.eval('func()') == 101, "can call parent's function in child");
+
+ finish();
+ });
+ sendRpcMessage("cpows:parent_test", {}, {func: f});
+}
+
+function error_reporting_test(finish) {
+ sendRpcMessage("cpows:error_reporting_test", {}, {});
+ finish();
+}
+
+function dom_test(finish)
+{
+ let element = content.document.createElement("div");
+ element.id = "it_works";
+ content.document.body.appendChild(element);
+
+ sendRpcMessage("cpows:dom_test", {}, {element: element});
+ Components.utils.schedulePreciseGC(function() {
+ sendRpcMessage("cpows:dom_test_after_gc");
+ finish();
+ });
+}
+
+function xray_test(finish)
+{
+ let element = content.document.createElement("div");
+ element.wrappedJSObject.foo = "hello";
+
+ sendRpcMessage("cpows:xray_test", {}, {element: element});
+ finish();
+}
+
+function symbol_test(finish)
+{
+ let iterator = Symbol.iterator;
+ let named = Symbol.for("cpow-test");
+
+ let object = {
+ [iterator]: iterator,
+ [named]: named,
+ };
+ let test = ['a'];
+ sendRpcMessage("cpows:symbol_test", {}, {object: object, test: test});
+ finish();
+}
+
+// Parent->Child references should go X->parent.privilegedJunkScope->child.privilegedJunkScope->Y
+// Child->Parent references should go X->child.privilegedJunkScope->parent.unprivilegedJunkScope->Y
+function compartment_test(finish)
+{
+ // This test primarily checks various compartment invariants for CPOWs, and
+ // doesn't make sense to run in-process.
+ if (!is_remote) {
+ finish();
+ return;
+ }
+
+ let sb = Cu.Sandbox('http://www.example.com', { wantGlobalProperties: ['XMLHttpRequest'] });
+ sb.eval('function getUnprivilegedObject() { var xhr = new XMLHttpRequest(); xhr.expando = 42; return xhr; }');
+ function testParentObject(obj) {
+ let results = [];
+ function is(a, b, msg) { results.push({ result: a === b ? "PASS" : "FAIL", message: msg }) };
+ function ok(x, msg) { results.push({ result: x ? "PASS" : "FAIL", message: msg }) };
+
+ let cpowLocation = Cu.getCompartmentLocation(obj);
+ ok(/Privileged Junk/.test(cpowLocation),
+ "child->parent CPOWs should live in the privileged junk scope: " + cpowLocation);
+ is(obj(), 42, "child->parent CPOW is invokable");
+ try {
+ obj.expando;
+ ok(false, "child->parent CPOW cannot access properties");
+ } catch (e) {
+ ok(true, "child->parent CPOW cannot access properties");
+ }
+
+ return results;
+ }
+ sendRpcMessage("cpows:compartment_test", {}, { getUnprivilegedObject: sb.getUnprivilegedObject,
+ testParentObject: testParentObject });
+ finish();
+}
+
+function regexp_test(finish)
+{
+ sendRpcMessage("cpows:regexp_test", {}, { regexp: /myRegExp/g });
+ finish();
+}
+
+function postmessage_test(finish)
+{
+ sendRpcMessage("cpows:postmessage_test", {}, { win: content.window });
+ finish();
+}
+
+function sync_test(finish)
+{
+ dump('beginning cpow sync test\n');
+ sync_obj = make_object();
+ sendRpcMessage("cpows:sync",
+ make_json(),
+ make_object());
+ finish();
+}
+
+function async_test(finish)
+{
+ dump('beginning cpow async test\n');
+ async_obj = make_object();
+ sendAsyncMessage("cpows:async",
+ make_json(),
+ async_obj);
+
+ addMessageListener("cpows:async_done", finish);
+}
+
+var rpc_obj;
+
+function rpc_test(finish)
+{
+ dump('beginning cpow rpc test\n');
+ rpc_obj = make_object();
+ rpc_obj.data.reenter = function () {
+ sendRpcMessage("cpows:reenter", { }, { data: { valid: true } });
+ return "ok";
+ }
+ sendRpcMessage("cpows:rpc",
+ make_json(),
+ rpc_obj);
+ finish();
+}
+
+function lifetime_test(finish)
+{
+ if (!is_remote) {
+ // Only run this test when running out-of-process. Otherwise it
+ // will fail, since local CPOWs don't follow the same ownership
+ // rules.
+ finish();
+ return;
+ }
+
+ dump("beginning lifetime test\n");
+ var obj = {"will_die": {"f": 1}};
+ let [result] = sendRpcMessage("cpows:lifetime_test_1", {}, {obj: obj});
+ ok(result == 10, "got sync result");
+ ok(obj.wont_die.f == 2, "got reverse CPOW");
+ obj.will_die = null;
+ Components.utils.schedulePreciseGC(function() {
+ addMessageListener("cpows:lifetime_test_3", (msg) => {
+ ok(obj.wont_die.f == 2, "reverse CPOW still works");
+ finish();
+ });
+ sendRpcMessage("cpows:lifetime_test_2");
+ });
+}
+
+function cancel_test(finish)
+{
+ if (!is_remote) {
+ // No point in doing this in single-process mode.
+ finish();
+ return;
+ }
+
+ let fin1 = false, fin2 = false;
+
+ // CPOW from the parent runs f. When it sends a sync message, the
+ // CPOW is canceled. The parent starts running again immediately
+ // after the CPOW is canceled; f also continues running.
+ function f() {
+ let res = sendSyncMessage("cpows:cancel_sync_message");
+ ok(res[0] == 12, "cancel_sync_message result correct");
+ fin1 = true;
+ if (fin1 && fin2) finish();
+ }
+
+ sendAsyncMessage("cpows:cancel_test", null, {f: f});
+ addMessageListener("cpows:cancel_test_done", msg => {
+ fin2 = true;
+ if (fin1 && fin2) finish();
+ });
+}
+
+function cancel_test2(finish)
+{
+ if (!is_remote) {
+ // No point in doing this in single-process mode.
+ finish();
+ return;
+ }
+
+ let fin1 = false, fin2 = false;
+
+ // CPOW from the parent runs f. When it does a sync XHR, the
+ // CPOW is canceled. The parent starts running again immediately
+ // after the CPOW is canceled; f also continues running.
+ function f() {
+ let req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].
+ createInstance(Components.interfaces.nsIXMLHttpRequest);
+ let fin = false;
+ let reqListener = () => {
+ if (req.readyState != req.DONE) {
+ return;
+ }
+ ok(req.status == 200, "XHR succeeded");
+ fin = true;
+ };
+
+ req.onload = reqListener;
+ req.open("get", "http://example.com", false);
+ req.send(null);
+
+ ok(fin == true, "XHR happened");
+
+ fin1 = true;
+ if (fin1 && fin2) finish();
+ }
+
+ sendAsyncMessage("cpows:cancel_test2", null, {f: f});
+ addMessageListener("cpows:cancel_test2_done", msg => {
+ fin2 = true;
+ if (fin1 && fin2) finish();
+ });
+}
+
+function unsafe_test(finish)
+{
+ if (!is_remote) {
+ // Only run this test when running out-of-process.
+ finish();
+ return;
+ }
+
+ function f() {}
+
+ sendAsyncMessage("cpows:unsafe", null, {f});
+ addMessageListener("cpows:unsafe_done", msg => {
+ sendRpcMessage("cpows:safe", null, {f});
+ addMessageListener("cpows:safe_done", finish);
+ });
+}
+
+function dead_test(finish)
+{
+ if (!is_remote) {
+ // Only run this test when running out-of-process.
+ finish();
+ return;
+ }
+
+ let gcTrigger = function() {
+ // Force the GC to dead-ify the thing.
+ content.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils)
+ .garbageCollect();
+ }
+
+ {
+ let thing = { value: "Gonna croak" };
+ sendAsyncMessage("cpows:dead", null, { thing, gcTrigger });
+ }
+
+ addMessageListener("cpows:dead_done", finish);
+}