diff options
Diffstat (limited to 'js/xpconnect/tests/unit')
118 files changed, 4489 insertions, 0 deletions
diff --git a/js/xpconnect/tests/unit/CatRegistrationComponents.manifest b/js/xpconnect/tests/unit/CatRegistrationComponents.manifest new file mode 100644 index 000000000..11646b028 --- /dev/null +++ b/js/xpconnect/tests/unit/CatRegistrationComponents.manifest @@ -0,0 +1,2 @@ +category test-cat CatRegisteredComponent @unit.test.com/cat-registered-component;1
+category test-cat CatAppRegisteredComponent @unit.test.com/cat-app-registered-component;1 application={adb42a9a-0d19-4849-bf4d-627614ca19be}
diff --git a/js/xpconnect/tests/unit/bogus_element_type.jsm b/js/xpconnect/tests/unit/bogus_element_type.jsm new file mode 100644 index 000000000..ba6583bba --- /dev/null +++ b/js/xpconnect/tests/unit/bogus_element_type.jsm @@ -0,0 +1 @@ +this.EXPORTED_SYMBOLS = [{}]; diff --git a/js/xpconnect/tests/unit/bogus_exports_type.jsm b/js/xpconnect/tests/unit/bogus_exports_type.jsm new file mode 100644 index 000000000..4ec23b215 --- /dev/null +++ b/js/xpconnect/tests/unit/bogus_exports_type.jsm @@ -0,0 +1 @@ +this.EXPORTED_SYMBOLS = "not an array"; diff --git a/js/xpconnect/tests/unit/bug451678_subscript.js b/js/xpconnect/tests/unit/bug451678_subscript.js new file mode 100644 index 000000000..72ff49a2d --- /dev/null +++ b/js/xpconnect/tests/unit/bug451678_subscript.js @@ -0,0 +1,5 @@ +var tags = [];
+function makeTags() {}
+
+// This will be the return value of the script.
+42
diff --git a/js/xpconnect/tests/unit/component-blob.js b/js/xpconnect/tests/unit/component-blob.js new file mode 100644 index 000000000..7c50f7c92 --- /dev/null +++ b/js/xpconnect/tests/unit/component-blob.js @@ -0,0 +1,79 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); +Components.utils.importGlobalProperties(['Blob', 'File']); + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +function do_check_true(cond, text) { + // we don't have the test harness' utilities in this scope, so we need this + // little helper. In the failure case, the exception is propagated to the + // caller in the main run_test() function, and the test fails. + if (!cond) + throw "Failed check: " + text; +} + +function BlobComponent() { + this.wrappedJSObject = this; +} +BlobComponent.prototype = +{ + doTest: function() { + // throw if anything goes wrong + let testContent = "<a id=\"a\"><b id=\"b\">hey!<\/b><\/a>"; + // should be able to construct a file + var f1 = new Blob([testContent], {"type" : "text/xml"}); + + // do some tests + do_check_true(f1 instanceof Ci.nsIDOMBlob, "Should be a DOM Blob"); + + do_check_true(!(f1 instanceof File), "Should not be a DOM File"); + + do_check_true(f1.type == "text/xml", "Wrong type"); + + do_check_true(f1.size == testContent.length, "Wrong content size"); + + var f2 = new Blob(); + do_check_true(f2.size == 0, "Wrong size"); + do_check_true(f2.type == "", "Wrong type"); + + var threw = false; + try { + // Needs a valid ctor argument + var f2 = new Blob(Date(132131532)); + } catch (e) { + threw = true; + } + do_check_true(threw, "Passing a random object should fail"); + + return true; + }, + + // nsIClassInfo + information for XPCOM registration code in XPCOMUtils.jsm + classDescription: "Blob in components scope code", + classID: Components.ID("{06215993-a3c2-41e3-bdfd-0a3a2cc0b65c}"), + contractID: "@mozilla.org/tests/component-blob;1", + + // nsIClassInfo + flags: 0, + + getInterfaces: function getInterfaces(aCount) { + var interfaces = [Components.interfaces.nsIClassInfo]; + aCount.value = interfaces.length; + return interfaces; + }, + + getScriptableHelper: function getScriptableHelper() { + return null; + }, + + // nsISupports + QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIClassInfo]) +}; + +var gComponentsArray = [BlobComponent]; +this.NSGetFactory = XPCOMUtils.generateNSGetFactory(gComponentsArray); diff --git a/js/xpconnect/tests/unit/component-blob.manifest b/js/xpconnect/tests/unit/component-blob.manifest new file mode 100644 index 000000000..ac264c06d --- /dev/null +++ b/js/xpconnect/tests/unit/component-blob.manifest @@ -0,0 +1,2 @@ +component {06215993-a3c2-41e3-bdfd-0a3a2cc0b65c} component-blob.js +contract @mozilla.org/tests/component-blob;1 {06215993-a3c2-41e3-bdfd-0a3a2cc0b65c} diff --git a/js/xpconnect/tests/unit/component-file.js b/js/xpconnect/tests/unit/component-file.js new file mode 100644 index 000000000..a05ae94e2 --- /dev/null +++ b/js/xpconnect/tests/unit/component-file.js @@ -0,0 +1,104 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); +Components.utils.importGlobalProperties(['File']); + + +const Ci = Components.interfaces; + +function do_check_true(cond, text) { + // we don't have the test harness' utilities in this scope, so we need this + // little helper. In the failure case, the exception is propagated to the + // caller in the main run_test() function, and the test fails. + if (!cond) + throw "Failed check: " + text; +} + +function FileComponent() { + this.wrappedJSObject = this; +} +FileComponent.prototype = +{ + doTest: function() { + // throw if anything goes wrong + + // find the current directory path + var file = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIProperties) + .get("CurWorkD", Ci.nsIFile); + file.append("xpcshell.ini"); + + // should be able to construct a file + var f1 = File.createFromFileName(file.path); + // and with nsIFiles + var f2 = File.createFromNsIFile(file); + + // do some tests + do_check_true(f1 instanceof File, "Should be a DOM File"); + do_check_true(f2 instanceof File, "Should be a DOM File"); + + do_check_true(f1.name == "xpcshell.ini", "Should be the right file"); + do_check_true(f2.name == "xpcshell.ini", "Should be the right file"); + + do_check_true(f1.type == "", "Should be the right type"); + do_check_true(f2.type == "", "Should be the right type"); + + var threw = false; + try { + // Needs a ctor argument + var f7 = new File(); + } catch (e) { + threw = true; + } + do_check_true(threw, "No ctor arguments should throw"); + + var threw = false; + try { + // Needs a valid ctor argument + var f7 = new File(Date(132131532)); + } catch (e) { + threw = true; + } + do_check_true(threw, "Passing a random object should fail"); + + var threw = false + try { + // Directories fail + var dir = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIProperties) + .get("CurWorkD", Ci.nsIFile); + var f7 = File.createFromNsIFile(dir) + } catch (e) { + threw = true; + } + do_check_true(threw, "Can't create a File object for a directory"); + + return true; + }, + + // nsIClassInfo + information for XPCOM registration code in XPCOMUtils.jsm + classDescription: "File in components scope code", + classID: Components.ID("{da332370-91d4-464f-a730-018e14769cab}"), + contractID: "@mozilla.org/tests/component-file;1", + + // nsIClassInfo + flags: 0, + + getInterfaces: function getInterfaces(aCount) { + var interfaces = [Components.interfaces.nsIClassInfo]; + aCount.value = interfaces.length; + return interfaces; + }, + + getScriptableHelper: function getScriptableHelper() { + return null; + }, + + // nsISupports + QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIClassInfo]) +}; + +var gComponentsArray = [FileComponent]; +this.NSGetFactory = XPCOMUtils.generateNSGetFactory(gComponentsArray); diff --git a/js/xpconnect/tests/unit/component-file.manifest b/js/xpconnect/tests/unit/component-file.manifest new file mode 100644 index 000000000..f865f333d --- /dev/null +++ b/js/xpconnect/tests/unit/component-file.manifest @@ -0,0 +1,2 @@ +component {da332370-91d4-464f-a730-018e14769cab} component-file.js +contract @mozilla.org/tests/component-file;1 {da332370-91d4-464f-a730-018e14769cab}
\ No newline at end of file diff --git a/js/xpconnect/tests/unit/component_import.js b/js/xpconnect/tests/unit/component_import.js new file mode 100644 index 000000000..8d7e3d0cf --- /dev/null +++ b/js/xpconnect/tests/unit/component_import.js @@ -0,0 +1,86 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +function FooComponent() { + this.wrappedJSObject = this; +} +FooComponent.prototype = +{ + // nsIClassInfo + information for XPCOM registration code in XPCOMUtils.jsm + classDescription: "Foo Component", + classID: Components.ID("{6b933fe6-6eba-4622-ac86-e4f654f1b474}"), + contractID: "@mozilla.org/tests/module-importer;1", + + // nsIClassInfo + flags: 0, + + getInterfaces: function getInterfaces(aCount) { + var interfaces = [Components.interfaces.nsIClassInfo]; + aCount.value = interfaces.length; + + // Guerilla test for line numbers hiding in this method + var threw = true; + try { + thereIsNoSuchIdentifier; + threw = false; + } catch (ex) { + do_check_true(ex.lineNumber == 27); + } + do_check_true(threw); + + return interfaces; + }, + + getScriptableHelper: function getScriptableHelper() { + return null; + }, + + // nsISupports + QueryInterface: function QueryInterface(aIID) { + if (aIID.equals(Components.interfaces.nsIClassInfo) || + aIID.equals(Components.interfaces.nsISupports)) + return this; + + throw Components.results.NS_ERROR_NO_INTERFACE; + } +}; + +function BarComponent() { +} +BarComponent.prototype = +{ + // nsIClassInfo + information for XPCOM registration code in XPCOMUtils.jsm + classDescription: "Module importer test 2", + classID: Components.ID("{708a896a-b48d-4bff-906e-fc2fd134b296}"), + contractID: "@mozilla.org/tests/module-importer;2", + + // nsIClassInfo + flags: 0, + + getInterfaces: function getInterfaces(aCount) { + var interfaces = [Components.interfaces.nsIClassInfo]; + aCount.value = interfaces.length; + return interfaces; + }, + + getScriptableHelper: function getScriptableHelper() { + return null; + }, + + // nsISupports + QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIClassInfo]) +}; + +function do_check_true(cond, text) { + // we don't have the test harness' utilities in this scope, so we need this + // little helper. In the failure case, the exception is propagated to the + // caller in the main run_test() function, and the test fails. + if (!cond) + throw "Failed check: " + text; +} + +var gComponentsArray = [FooComponent, BarComponent]; +this.NSGetFactory = XPCOMUtils.generateNSGetFactory(gComponentsArray); diff --git a/js/xpconnect/tests/unit/component_import.manifest b/js/xpconnect/tests/unit/component_import.manifest new file mode 100644 index 000000000..952c2dd4d --- /dev/null +++ b/js/xpconnect/tests/unit/component_import.manifest @@ -0,0 +1,5 @@ +component {6b933fe6-6eba-4622-ac86-e4f654f1b474} component_import.js
+contract @mozilla.org/tests/module-importer;1 {6b933fe6-6eba-4622-ac86-e4f654f1b474}
+
+component {708a896a-b48d-4bff-906e-fc2fd134b296} component_import.js
+contract @mozilla.org/tests/module-importer;2 {708a896a-b48d-4bff-906e-fc2fd134b296}
diff --git a/js/xpconnect/tests/unit/head_ongc.js b/js/xpconnect/tests/unit/head_ongc.js new file mode 100644 index 000000000..85989f1d9 --- /dev/null +++ b/js/xpconnect/tests/unit/head_ongc.js @@ -0,0 +1,41 @@ +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/jsdebugger.jsm"); + +const testingFunctions = Cu.getJSTestingFunctions(); +const systemPrincipal = Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal); + +function addTestingFunctionsToGlobal(global) { + for (let k in testingFunctions) { + global[k] = testingFunctions[k]; + } + global.print = do_print; + global.newGlobal = newGlobal; + addDebuggerToGlobal(global); +} + +function newGlobal() { + const global = new Cu.Sandbox(systemPrincipal, { freshZone: true }); + addTestingFunctionsToGlobal(global); + return global; +} + +addTestingFunctionsToGlobal(this); + +function executeSoon(f) { + Services.tm.mainThread.dispatch({ run: f }, + Ci.nsIThread.DISPATCH_NORMAL); +} + +// The onGarbageCollection tests don't play well gczeal settings and lead to +// intermittents. +if (typeof gczeal == "function") { + gczeal(0); +} + +// Make sure to GC before we start the test, so that no zones are scheduled for +// GC before we start testing onGarbageCollection hooks. +gc(); diff --git a/js/xpconnect/tests/unit/head_watchdog.js b/js/xpconnect/tests/unit/head_watchdog.js new file mode 100644 index 000000000..cf006303a --- /dev/null +++ b/js/xpconnect/tests/unit/head_watchdog.js @@ -0,0 +1,112 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// +// Pref management. +// + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://testing-common/PromiseTestUtils.jsm"); + +/////////////////// +// +// Whitelisting these tests. +// As part of bug 1077403, the shutdown crash should be fixed. +// +// These tests may crash intermittently on shutdown if the DOM Promise uncaught +// rejection observers are still registered when the watchdog operates. +PromiseTestUtils.thisTestLeaksUncaughtRejectionsAndShouldBeFixed(); + +var gPrefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); + +function setWatchdogEnabled(enabled) { + gPrefs.setBoolPref("dom.use_watchdog", enabled); +} + +function isWatchdogEnabled() { + return gPrefs.getBoolPref("dom.use_watchdog"); +} + +function setScriptTimeout(seconds) { + var oldTimeout = gPrefs.getIntPref("dom.max_script_run_time"); + gPrefs.setIntPref("dom.max_script_run_time", seconds); + return oldTimeout; +} + +// +// Utilities. +// + +function busyWait(ms) { + var start = new Date(); + while ((new Date()) - start < ms) {} +} + +function do_log_info(aMessage) +{ + print("TEST-INFO | " + _TEST_FILE + " | " + aMessage); +} + +// We don't use do_execute_soon, because that inserts a +// do_test_{pending,finished} pair that gets screwed up when we terminate scripts +// from the operation callback. +function executeSoon(fn) { + var tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager); + tm.mainThread.dispatch({run: fn}, Ci.nsIThread.DISPATCH_NORMAL); +} + +// +// Asynchronous watchdog diagnostics. +// +// When running, the watchdog wakes up every second, and fires the operation +// callback if the script has been running for >= the minimum script timeout. +// As such, if the script timeout is 1 second, a script should never be able to +// run for two seconds or longer without servicing the operation callback. +// We wait 3 seconds, just to be safe. +// + +function checkWatchdog(expectInterrupt, continuation) { + var oldTimeout = setScriptTimeout(1); + var lastWatchdogWakeup = Cu.getWatchdogTimestamp("WatchdogWakeup"); + setInterruptCallback(function() { + // If the watchdog didn't actually trigger the operation callback, ignore + // this call. This allows us to test the actual watchdog behavior without + // interference from other sites where we trigger the operation callback. + if (lastWatchdogWakeup == Cu.getWatchdogTimestamp("WatchdogWakeup")) { + return true; + } + do_check_true(expectInterrupt); + setInterruptCallback(undefined); + setScriptTimeout(oldTimeout); + // Schedule our continuation before we kill this script. + executeSoon(continuation); + return false; + }); + executeSoon(function() { + busyWait(3000); + do_check_true(!expectInterrupt); + setInterruptCallback(undefined); + setScriptTimeout(oldTimeout); + continuation(); + }); +} + +var gGenerator; +function continueTest() { + gGenerator.next(); +} + +function run_test() { + + // Run async. + do_test_pending(); + + // Instantiate the generator and kick it off. + gGenerator = testBody(); + gGenerator.next(); +} + diff --git a/js/xpconnect/tests/unit/importer.jsm b/js/xpconnect/tests/unit/importer.jsm new file mode 100644 index 000000000..f5f40d30b --- /dev/null +++ b/js/xpconnect/tests/unit/importer.jsm @@ -0,0 +1 @@ +Components.utils.import("resource://test/syntax_error.jsm");
\ No newline at end of file diff --git a/js/xpconnect/tests/unit/recursive_importA.jsm b/js/xpconnect/tests/unit/recursive_importA.jsm new file mode 100644 index 000000000..ebd9a2ddd --- /dev/null +++ b/js/xpconnect/tests/unit/recursive_importA.jsm @@ -0,0 +1,12 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +this.EXPORTED_SYMBOLS = ["foo", "bar"]; + +function foo() { + return "foo"; +} + +var bar = {} +Components.utils.import("resource://test/recursive_importB.jsm", bar); diff --git a/js/xpconnect/tests/unit/recursive_importB.jsm b/js/xpconnect/tests/unit/recursive_importB.jsm new file mode 100644 index 000000000..6ef2b8644 --- /dev/null +++ b/js/xpconnect/tests/unit/recursive_importB.jsm @@ -0,0 +1,13 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +this.EXPORTED_SYMBOLS = ["baz", "qux"]; + +function baz() { + return "baz"; +} + +var qux = {} +Components.utils.import("resource://test/recursive_importA.jsm", qux); + diff --git a/js/xpconnect/tests/unit/subScriptWithEarlyError.js b/js/xpconnect/tests/unit/subScriptWithEarlyError.js new file mode 100644 index 000000000..45b904d1f --- /dev/null +++ b/js/xpconnect/tests/unit/subScriptWithEarlyError.js @@ -0,0 +1 @@ +var ; diff --git a/js/xpconnect/tests/unit/syntax_error.jsm b/js/xpconnect/tests/unit/syntax_error.jsm new file mode 100644 index 000000000..fca785bcd --- /dev/null +++ b/js/xpconnect/tests/unit/syntax_error.jsm @@ -0,0 +1 @@ +bogusjs)( diff --git a/js/xpconnect/tests/unit/test_URLSearchParams.js b/js/xpconnect/tests/unit/test_URLSearchParams.js new file mode 100644 index 000000000..0031a5ab6 --- /dev/null +++ b/js/xpconnect/tests/unit/test_URLSearchParams.js @@ -0,0 +1,13 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +function run_test() { + var Cu = Components.utils; + var sb = new Cu.Sandbox('http://www.example.com', + { wantGlobalProperties: ["URLSearchParams"] }); + sb.do_check_eq = do_check_eq; + Cu.evalInSandbox('do_check_eq(new URLSearchParams("one=1&two=2").get("one"), "1");', + sb); + Cu.importGlobalProperties(["URLSearchParams"]); + do_check_eq(new URLSearchParams("one=1&two=2").get("one"), "1"); +} diff --git a/js/xpconnect/tests/unit/test_allowWaivers.js b/js/xpconnect/tests/unit/test_allowWaivers.js new file mode 100644 index 000000000..66f70694a --- /dev/null +++ b/js/xpconnect/tests/unit/test_allowWaivers.js @@ -0,0 +1,30 @@ +const Cu = Components.utils; +function checkWaivers(from, allowed) { + var sb = new Cu.Sandbox('http://example.com'); + from.test = sb.eval('var o = {prop: 2, f: function() {return 42;}}; o'); + + // Make sure that |from| has Xrays to sb. + do_check_eq(from.eval('test.prop'), 2); + do_check_eq(from.eval('test.f'), undefined); + + // Make sure that waivability works as expected. + do_check_eq(from.eval('!!test.wrappedJSObject'), allowed); + do_check_eq(from.eval('XPCNativeWrapper.unwrap(test) !== test'), allowed); + + // Make a sandbox with the same principal as |from|, but without any waiver + // restrictions, and make sure that the waiver does not transfer. + var friend = new Cu.Sandbox(Cu.getObjectPrincipal(from)); + friend.test = from.test; + friend.eval('var waived = test.wrappedJSObject;'); + do_check_true(friend.eval('waived.f()'), 42); + friend.from = from; + friend.eval('from.waived = waived'); + do_check_eq(from.eval('!!waived.f'), allowed); +} + +function run_test() { + checkWaivers(new Cu.Sandbox('http://example.com'), true); + checkWaivers(new Cu.Sandbox('http://example.com', {allowWaivers: false}), false); + checkWaivers(new Cu.Sandbox(['http://example.com']), true); + checkWaivers(new Cu.Sandbox(['http://example.com'], {allowWaivers: false}), false); +} diff --git a/js/xpconnect/tests/unit/test_allowedDomains.js b/js/xpconnect/tests/unit/test_allowedDomains.js new file mode 100644 index 000000000..a85d9d75a --- /dev/null +++ b/js/xpconnect/tests/unit/test_allowedDomains.js @@ -0,0 +1,47 @@ +function run_test() { + var cu = Components.utils; + var sbMaster = cu.Sandbox(["http://www.a.com", + "http://www.b.com", + "http://www.d.com"]); + var sbSubset = cu.Sandbox(["http://www.d.com", + "http://www.a.com"]); + + var sbA = cu.Sandbox("http://www.a.com"); + var sbB = cu.Sandbox("http://www.b.com"); + var sbC = cu.Sandbox("http://www.c.com"); + + sbMaster.objA = cu.evalInSandbox("var obj = {prop1:200}; obj", sbA); + sbMaster.objB = cu.evalInSandbox("var obj = {prop1:200}; obj", sbB); + sbMaster.objC = cu.evalInSandbox("var obj = {prop1:200}; obj", sbC); + sbMaster.objOwn = cu.evalInSandbox("var obj = {prop1:200}; obj", sbMaster); + + sbMaster.objSubset = cu.evalInSandbox("var obj = {prop1:200}; obj", sbSubset); + sbA.objMaster = cu.evalInSandbox("var obj = {prop1:200}; obj", sbMaster); + sbSubset.objMaster = cu.evalInSandbox("var obj = {prop1:200}; obj", sbMaster); + + var ret; + ret = cu.evalInSandbox("objA.prop1", sbMaster); + do_check_eq(ret, 200); + ret = cu.evalInSandbox("objB.prop1", sbMaster); + do_check_eq(ret, 200); + ret = cu.evalInSandbox("objSubset.prop1", sbMaster); + do_check_eq(ret, 200); + + function evalAndCatch(str, sb) { + try { + ret = cu.evalInSandbox(str, sb); + do_check_true(false, "unexpected pass") + } catch (e) { + do_check_true(e.message && e.message.indexOf("Permission denied to access property") != -1); + } + } + + evalAndCatch("objC.prop1", sbMaster); + evalAndCatch("objMaster.prop1", sbA); + evalAndCatch("objMaster.prop1", sbSubset); + + // Bug 777705: + sbMaster.Components = cu.getComponentsForScope(sbMaster); + Components.utils.evalInSandbox("Components.interfaces", sbMaster); + do_check_true(true); +} diff --git a/js/xpconnect/tests/unit/test_allowedDomainsXHR.js b/js/xpconnect/tests/unit/test_allowedDomainsXHR.js new file mode 100644 index 000000000..71bfe942b --- /dev/null +++ b/js/xpconnect/tests/unit/test_allowedDomainsXHR.js @@ -0,0 +1,136 @@ + +var cu = Components.utils; +cu.import("resource://testing-common/httpd.js"); + +var httpserver = new HttpServer(); +var httpserver2 = new HttpServer(); +var httpserver3 = new HttpServer(); +var testpath = "/simple"; +var redirectpath = "/redirect"; +var negativetestpath = "/negative"; +var httpbody = "<?xml version='1.0' ?><root>0123456789</root>"; + +var sb = cu.Sandbox(["http://www.example.com", + "http://localhost:4444/redirect", + "http://localhost:4444/simple", + "http://localhost:4446/redirect"], + { wantGlobalProperties: ["XMLHttpRequest"] }); + +function createXHR(loc, async) +{ + var xhr = new XMLHttpRequest(); + xhr.open("GET", "http://localhost:" + loc, async); + return xhr; +} + +function checkResults(xhr) +{ + if (xhr.readyState != 4) + return false; + + do_check_eq(xhr.status, 200); + do_check_eq(xhr.responseText, httpbody); + + var root_node = xhr.responseXML.getElementsByTagName('root').item(0); + do_check_eq(root_node.firstChild.data, "0123456789"); + return true; +} + +var httpServersClosed = 0; +function finishIfDone() +{ + if (++httpServersClosed == 3) + do_test_finished(); +} + +function run_test() +{ + do_test_pending(); + + httpserver.registerPathHandler(testpath, serverHandler); + httpserver.registerPathHandler(redirectpath, redirectHandler1); + httpserver.start(4444); + + httpserver2.registerPathHandler(negativetestpath, serverHandler); + httpserver2.start(4445); + + httpserver3.registerPathHandler(redirectpath, redirectHandler2); + httpserver3.start(4446); + + // Test sync XHR sending + cu.evalInSandbox('var createXHR = ' + createXHR.toString(), sb); + var res = cu.evalInSandbox('var sync = createXHR("4444/simple"); sync.send(null); sync', sb); + do_check_true(checkResults(res)); + + var principal = res.responseXML.nodePrincipal; + do_check_true(principal.isCodebasePrincipal); + var requestURL = "http://localhost:4444/simple"; + do_check_eq(principal.URI.spec, requestURL); + + // negative test sync XHR sending (to ensure that the xhr do not have chrome caps, see bug 779821) + try { + cu.evalInSandbox('var createXHR = ' + createXHR.toString(), sb); + var res = cu.evalInSandbox('var sync = createXHR("4445/negative"); sync.send(null); sync', sb); + do_check_false(true, "XHR created from sandbox should not have chrome caps"); + } catch (e) { + do_check_true(true); + } + + // Test redirect handling. + // This request bounces to server 2 and then back to server 1. Neither of + // these servers support CORS, but if the expanded principal is used as the + // triggering principal, this should work. + cu.evalInSandbox('var createXHR = ' + createXHR.toString(), sb); + var res = cu.evalInSandbox('var sync = createXHR("4444/redirect"); sync.send(null); sync', sb); + do_check_true(checkResults(res)); + + var principal = res.responseXML.nodePrincipal; + do_check_true(principal.isCodebasePrincipal); + var requestURL = "http://localhost:4444/simple"; + do_check_eq(principal.URI.spec, requestURL); + + httpserver2.stop(finishIfDone); + httpserver3.stop(finishIfDone); + + // Test async XHR sending + sb.finish = function(){ + httpserver.stop(finishIfDone); + } + + // We want to execute checkResults from the scope of the sandbox as well to + // make sure that there are no permission errors related to nsEP. For that + // we need to clone the function into the sandbox and make a few things + // available for it. + cu.evalInSandbox('var checkResults = ' + checkResults.toSource(), sb); + sb.do_check_eq = do_check_eq; + sb.httpbody = httpbody; + + function changeListener(event) { + if (checkResults(async)) + finish(); + } + + var async = cu.evalInSandbox('var async = createXHR("4444/simple", true);' + + 'async.addEventListener("readystatechange", ' + + changeListener.toString() + ', false);' + + 'async', sb); + async.send(null); +} + +function serverHandler(request, response) +{ + response.setHeader("Content-Type", "text/xml", false); + response.bodyOutputStream.write(httpbody, httpbody.length); +} + +function redirectHandler1(request, response) +{ + response.setStatusLine(request.httpVersion, 302, "Found"); + response.setHeader("Location", "http://localhost:4446/redirect", false); +} + +function redirectHandler2(request, response) +{ + response.setStatusLine(request.httpVersion, 302, "Found"); + response.setHeader("Location", "http://localhost:4444/simple", false); +} diff --git a/js/xpconnect/tests/unit/test_asyncLoadSubScriptError.js b/js/xpconnect/tests/unit/test_asyncLoadSubScriptError.js new file mode 100644 index 000000000..0c27392d8 --- /dev/null +++ b/js/xpconnect/tests/unit/test_asyncLoadSubScriptError.js @@ -0,0 +1,33 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const Cc = Components.classes; +const Ci = Components.interfaces; + +var srvScope = {}; + +function success(result) { + ok(false, "script should not have loaded successfully"); + do_test_finished(); +} + +function error(err) { + ok(err instanceof SyntaxError, "loading script with early error asynchronously resulted in error"); + do_test_finished(); +} + +function run_test() { + do_test_pending(); + + var file = do_get_file("subScriptWithEarlyError.js"); + var ios = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); + var uri = ios.newFileURI(file); + var scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"] + .getService(Ci.mozIJSSubScriptLoader); + var p = scriptLoader.loadSubScriptWithOptions(uri.spec, + { target: srvScope, + async: true }); + p.then(success, error); +} diff --git a/js/xpconnect/tests/unit/test_attributes.js b/js/xpconnect/tests/unit/test_attributes.js new file mode 100644 index 000000000..52dbb59e6 --- /dev/null +++ b/js/xpconnect/tests/unit/test_attributes.js @@ -0,0 +1,81 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const Cc = Components.classes; +const Ci = Components.interfaces; + +function run_test() { + + // Load the component manifests. + registerAppManifest(do_get_file('../components/native/chrome.manifest')); + registerAppManifest(do_get_file('../components/js/xpctest.manifest')); + + // Test for each component. + test_component_readwrite("@mozilla.org/js/xpc/test/native/ObjectReadWrite;1"); + test_component_readwrite("@mozilla.org/js/xpc/test/js/ObjectReadWrite;1"); + test_component_readonly("@mozilla.org/js/xpc/test/native/ObjectReadOnly;1"); + test_component_readonly("@mozilla.org/js/xpc/test/js/ObjectReadOnly;1"); +} + +function test_component_readwrite(contractid) { + + // Instantiate the object. + var o = Cc[contractid].createInstance(Ci["nsIXPCTestObjectReadWrite"]); + + // Test the initial values. + do_check_eq("XPConnect Read-Writable String", o.stringProperty); + do_check_eq(true, o.booleanProperty); + do_check_eq(32767, o.shortProperty); + do_check_eq(2147483647, o.longProperty); + do_check_true(5.25 < o.floatProperty && 5.75 > o.floatProperty); + do_check_eq("X", o.charProperty); + do_check_eq(-1, o.timeProperty); + + // Write new values. + o.stringProperty = "another string"; + o.booleanProperty = false; + o.shortProperty = -12345; + o.longProperty = 1234567890; + o.floatProperty = 10.2; + o.charProperty = "Z"; + o.timeProperty = 1; + + // Test the new values. + do_check_eq("another string", o.stringProperty); + do_check_eq(false, o.booleanProperty); + do_check_eq(-12345, o.shortProperty); + do_check_eq(1234567890, o.longProperty); + do_check_true(10.15 < o.floatProperty && 10.25 > o.floatProperty); + do_check_eq("Z", o.charProperty); + do_check_eq(1, o.timeProperty); + + // Assign values that differ from the expected type to verify conversion. + + function SetAndTestBooleanProperty(newValue, expectedValue) { + o.booleanProperty = newValue; + do_check_eq(expectedValue, o.booleanProperty); + }; + SetAndTestBooleanProperty(false, false); + SetAndTestBooleanProperty(1, true); + SetAndTestBooleanProperty(null, false); + SetAndTestBooleanProperty("A", true); + SetAndTestBooleanProperty(undefined, false); + SetAndTestBooleanProperty([], true); + SetAndTestBooleanProperty({}, true); +} + +function test_component_readonly(contractid) { + + // Instantiate the object. + var o = Cc[contractid].createInstance(Ci["nsIXPCTestObjectReadOnly"]); + + // Test the initial values. + do_check_eq("XPConnect Read-Only String", o.strReadOnly); + do_check_eq(true, o.boolReadOnly); + do_check_eq(32767, o.shortReadOnly); + do_check_eq(2147483647, o.longReadOnly); + do_check_true(5.25 < o.floatReadOnly && 5.75 > o.floatReadOnly); + do_check_eq("X", o.charReadOnly); + do_check_eq(-1, o.timeReadOnly); +} diff --git a/js/xpconnect/tests/unit/test_blob.js b/js/xpconnect/tests/unit/test_blob.js new file mode 100644 index 000000000..039f45102 --- /dev/null +++ b/js/xpconnect/tests/unit/test_blob.js @@ -0,0 +1,16 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function run_test() { + do_load_manifest("component-blob.manifest"); + const contractID = "@mozilla.org/tests/component-blob;1"; + do_check_true(contractID in Components.classes); + var foo = Components.classes[contractID] + .createInstance(Components.interfaces.nsIClassInfo); + do_check_true(Boolean(foo)); + do_check_true(foo.contractID == contractID); + do_check_true(!!foo.wrappedJSObject); + do_check_true(foo.wrappedJSObject.doTest()); + +} diff --git a/js/xpconnect/tests/unit/test_blob2.js b/js/xpconnect/tests/unit/test_blob2.js new file mode 100644 index 000000000..e031d0813 --- /dev/null +++ b/js/xpconnect/tests/unit/test_blob2.js @@ -0,0 +1,36 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +Components.utils.importGlobalProperties(['Blob', 'File']); + +const Ci = Components.interfaces; + +function run_test() { + // throw if anything goes wrong + let testContent = "<a id=\"a\"><b id=\"b\">hey!<\/b><\/a>"; + // should be able to construct a file + var f1 = new Blob([testContent], {"type" : "text/xml"}); + + // do some tests + do_check_true(f1 instanceof Ci.nsIDOMBlob, "Should be a DOM Blob"); + + do_check_true(!(f1 instanceof File), "Should not be a DOM File"); + + do_check_true(f1.type == "text/xml", "Wrong type"); + + do_check_true(f1.size == testContent.length, "Wrong content size"); + + var f2 = new Blob(); + do_check_true(f2.size == 0, "Wrong size"); + do_check_true(f2.type == "", "Wrong type"); + + var threw = false; + try { + // Needs a valid ctor argument + var f2 = new Blob(Date(132131532)); + } catch (e) { + threw = true; + } + do_check_true(threw, "Passing a random object should fail"); +} diff --git a/js/xpconnect/tests/unit/test_bogus_files.js b/js/xpconnect/tests/unit/test_bogus_files.js new file mode 100644 index 000000000..dc24a5fbd --- /dev/null +++ b/js/xpconnect/tests/unit/test_bogus_files.js @@ -0,0 +1,42 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function test_BrokenFile(path, shouldThrow, expectedName) { + var didThrow = false; + try { + Components.utils.import(path); + } catch (ex) { + var exceptionName = ex.name; + print("ex: " + ex + "; name = " + ex.name); + didThrow = true; + } + + do_check_eq(didThrow, shouldThrow); + if (didThrow) + do_check_eq(exceptionName, expectedName); +} + +function run_test() { + test_BrokenFile("resource://test/bogus_exports_type.jsm", true, "Error"); + + test_BrokenFile("resource://test/bogus_element_type.jsm", true, "Error"); + + test_BrokenFile("resource://test/non_existing.jsm", + true, + "NS_ERROR_FILE_NOT_FOUND"); + + test_BrokenFile("chrome://test/content/test.jsm", + true, + "NS_ERROR_FILE_NOT_FOUND"); + + // check that we can access modules' global objects even if + // EXPORTED_SYMBOLS is missing or ill-formed: + do_check_eq(typeof(Components.utils.import("resource://test/bogus_exports_type.jsm", + null)), + "object"); + + do_check_eq(typeof(Components.utils.import("resource://test/bogus_element_type.jsm", + null)), + "object"); +} diff --git a/js/xpconnect/tests/unit/test_bug1001094.js b/js/xpconnect/tests/unit/test_bug1001094.js new file mode 100644 index 000000000..f324e6b3f --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug1001094.js @@ -0,0 +1,4 @@ +function run_test() { + // Make sure nsJSID implements classinfo. + do_check_eq(Components.ID("{a6e2a27f-5521-4b35-8b52-99799a744aee}").equals, Components.ID("{daa47351-7d2e-44a7-b8e3-281802a1eab7}").equals); +} diff --git a/js/xpconnect/tests/unit/test_bug1021312.js b/js/xpconnect/tests/unit/test_bug1021312.js new file mode 100644 index 000000000..90009ed14 --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug1021312.js @@ -0,0 +1,16 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const Cu = Components.utils; +function run_test() { + let sb = new Cu.Sandbox(this); + var called = false; + + Cu.exportFunction(function(str) { do_check_true(/someString/.test(str)); called = true; }, + sb, { defineAs: "func" }); + // Do something weird with the string to make sure that it doesn't get interned. + Cu.evalInSandbox("var str = 'someString'; for (var i = 0; i < 10; ++i) str += i;", sb); + Cu.evalInSandbox("func(str);", sb); + do_check_true(called); +} diff --git a/js/xpconnect/tests/unit/test_bug1033253.js b/js/xpconnect/tests/unit/test_bug1033253.js new file mode 100644 index 000000000..9bae251ba --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug1033253.js @@ -0,0 +1,6 @@ +const Cu = Components.utils; +function run_test() { + var sb = Cu.Sandbox('http://www.example.com'); + var f = Cu.evalInSandbox('var f = function() {}; f;', sb); + do_check_eq(f.name, ""); +} diff --git a/js/xpconnect/tests/unit/test_bug1033920.js b/js/xpconnect/tests/unit/test_bug1033920.js new file mode 100644 index 000000000..dc3834ecb --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug1033920.js @@ -0,0 +1,7 @@ +const Cu = Components.utils; +function run_test() { + var sb = Cu.Sandbox('http://www.example.com'); + var o = new sb.Object(); + o.__proto__ = null; + do_check_eq(Object.getPrototypeOf(o), null); +} diff --git a/js/xpconnect/tests/unit/test_bug1033927.js b/js/xpconnect/tests/unit/test_bug1033927.js new file mode 100644 index 000000000..8c3ce7c26 --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug1033927.js @@ -0,0 +1,8 @@ +const Cu = Components.utils; +function run_test() { + var sb = Cu.Sandbox('http://www.example.com', { wantGlobalProperties: ['XMLHttpRequest']}); + var xhr = Cu.evalInSandbox('new XMLHttpRequest()', sb); + do_check_eq(xhr.toString(), '[object XMLHttpRequest]'); + do_check_eq((new sb.Object()).toString(), '[object Object]'); + do_check_eq(sb.Object.prototype.toString.call(new sb.Uint16Array()), '[object Uint16Array]'); +} diff --git a/js/xpconnect/tests/unit/test_bug1034262.js b/js/xpconnect/tests/unit/test_bug1034262.js new file mode 100644 index 000000000..a77df56a9 --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug1034262.js @@ -0,0 +1,9 @@ +const Cu = Components.utils; +function run_test() { + var sb1 = Cu.Sandbox('http://www.example.com', { wantXrays: true }); + var sb2 = Cu.Sandbox('http://www.example.com', { wantXrays: false }); + sb2.f = Cu.evalInSandbox('x => typeof x', sb1); + do_check_eq(Cu.evalInSandbox('f(dump)', sb2), 'function'); + do_check_eq(Cu.evalInSandbox('f.call(null, dump)', sb2), 'function'); + do_check_eq(Cu.evalInSandbox('f.apply(null, [dump])', sb2), 'function'); +} diff --git a/js/xpconnect/tests/unit/test_bug1081990.js b/js/xpconnect/tests/unit/test_bug1081990.js new file mode 100644 index 000000000..30f7a80d9 --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug1081990.js @@ -0,0 +1,10 @@ +const Cu = Components.utils; +function run_test() { + var sb = new Cu.Sandbox('http://www.example.com'); + sb.obj = {}; + sb.arr = []; + sb.fun = function() {}; + do_check_true(sb.eval('Object.getPrototypeOf(obj) == null')); + do_check_true(sb.eval('Object.getPrototypeOf(arr) == null')); + do_check_true(sb.eval('Object.getPrototypeOf(fun) == null')); +} diff --git a/js/xpconnect/tests/unit/test_bug1082450.js b/js/xpconnect/tests/unit/test_bug1082450.js new file mode 100644 index 000000000..07f45f06b --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug1082450.js @@ -0,0 +1,40 @@ +const Cu = Components.utils; +function run_test() { + + var sb = new Cu.Sandbox('http://www.example.com'); + function checkThrows(str, rgxp) { + try { + sb.eval(str); + do_check_true(false); + } catch (e) { + do_check_true(rgxp.test(e)); + } + } + + sb.exposed = { + get getterProp() { return 42; }, + set setterProp(x) { }, + get getterSetterProp() { return 42; }, + set getterSetterProp(x) { }, + simpleValueProp: 42, + objectValueProp: { val: 42, __exposedProps__: { val: 'r' } }, + contentCallableValueProp: new sb.Function('return 42'), + chromeCallableValueProp: function() {}, + __exposedProps__: { getterProp : 'r', + setterProp : 'w', + getterSetterProp: 'rw', + simpleValueProp: 'r', + objectValueProp: 'r', + contentCallableValueProp: 'r', + chromeCallableValueProp: 'r' } + }; + + do_check_eq(sb.eval('exposed.simpleValueProp'), 42); + do_check_eq(sb.eval('exposed.objectValueProp.val'), 42); + checkThrows('exposed.getterProp;', /privileged accessor/i); + checkThrows('exposed.setterProp = 42;', /privileged accessor/i); + checkThrows('exposed.getterSetterProp;', /privileged accessor/i); + checkThrows('exposed.getterSetterProp = 42;', /privileged accessor/i); + do_check_eq(sb.eval('exposed.contentCallableValueProp()'), 42); + checkThrows('exposed.chromeCallableValueProp();', /privileged or cross-origin callable/i); +} diff --git a/js/xpconnect/tests/unit/test_bug1110546.js b/js/xpconnect/tests/unit/test_bug1110546.js new file mode 100644 index 000000000..02687296d --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug1110546.js @@ -0,0 +1,5 @@ +const Cu = Components.utils; +function run_test() { + var sb = new Cu.Sandbox(null); + do_check_true(Cu.getObjectPrincipal(sb).isNullPrincipal); +} diff --git a/js/xpconnect/tests/unit/test_bug1131707.js b/js/xpconnect/tests/unit/test_bug1131707.js new file mode 100644 index 000000000..4ae4404a0 --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug1131707.js @@ -0,0 +1,22 @@ +const Cu = Components.utils; + +function testStrict(sb) { + "use strict"; + do_check_eq(sb.eval("typeof wrappedCtor()"), "string"); + do_check_eq(sb.eval("typeof new wrappedCtor()"), "object"); +} + +function run_test() { + var sb = new Cu.Sandbox(null); + var dateCtor = sb.Date; + sb.wrappedCtor = Cu.exportFunction(function wrapper(val) { + "use strict"; + var constructing = this.constructor == wrapper; + return constructing ? new dateCtor(val) : dateCtor(val); + }, sb); + do_check_eq(typeof Date(), "string"); + do_check_eq(typeof new Date(), "object"); + do_check_eq(sb.eval("typeof wrappedCtor()"), "string"); + do_check_eq(sb.eval("typeof new wrappedCtor()"), "object"); + testStrict(sb); +} diff --git a/js/xpconnect/tests/unit/test_bug1150106.js b/js/xpconnect/tests/unit/test_bug1150106.js new file mode 100644 index 000000000..0a93fb588 --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug1150106.js @@ -0,0 +1,35 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const Cc = Components.classes; +const Ci = Components.interfaces; + +var srvScope = {}; + +function success(result) { + equal(result, 42, "Result of script is correct"); + ok('makeTags' in srvScope && srvScope.makeTags instanceof Function, + "makeTags is a function."); + do_test_finished(); +} + +function error() { + ok(false, "error loading the script asynchronously."); + do_test_finished(); +} + +function run_test() { + do_test_pending(); + + var file = do_get_file("bug451678_subscript.js"); + var ios = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); + var uri = ios.newFileURI(file); + var scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"] + .getService(Ci.mozIJSSubScriptLoader); + var p = scriptLoader.loadSubScriptWithOptions(uri.spec, + { target: srvScope, + async: true }); + p.then(success, error); +} diff --git a/js/xpconnect/tests/unit/test_bug1150771.js b/js/xpconnect/tests/unit/test_bug1150771.js new file mode 100644 index 000000000..8387b2cdf --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug1150771.js @@ -0,0 +1,13 @@ +function run_test() { +let Cu = Components.utils; +let sandbox1 = new Cu.Sandbox(null); +let sandbox2 = new Cu.Sandbox(null); +let arg = Cu.evalInSandbox('({ buf: new ArrayBuffer(2) })', sandbox1); + +let clonedArg = Cu.cloneInto(Cu.waiveXrays(arg), sandbox2); +do_check_eq(typeof Cu.waiveXrays(clonedArg).buf, "object"); + +clonedArg = Cu.cloneInto(arg, sandbox2); +do_check_eq(typeof Cu.waiveXrays(clonedArg).buf, "object"); +do_check_eq(Cu.waiveXrays(clonedArg).buf.constructor.name, "ArrayBuffer"); +} diff --git a/js/xpconnect/tests/unit/test_bug1151385.js b/js/xpconnect/tests/unit/test_bug1151385.js new file mode 100644 index 000000000..fdf979fe0 --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug1151385.js @@ -0,0 +1,9 @@ +function run_test() +{ + try { + var sandbox = new Components.utils.Sandbox(null, {"sandboxPrototype" : {}}); + do_check_true(false); + } catch (e) { + do_check_true(/must subsume sandboxPrototype/.test(e)); + } +} diff --git a/js/xpconnect/tests/unit/test_bug1170311.js b/js/xpconnect/tests/unit/test_bug1170311.js new file mode 100644 index 000000000..0c58c75d7 --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug1170311.js @@ -0,0 +1,5 @@ +const Cu = Components.utils; +function run_test() { + do_check_throws_nsIException(() => Cu.getObjectPrincipal({}).equals(null), "NS_ERROR_ILLEGAL_VALUE"); + do_check_throws_nsIException(() => Cu.getObjectPrincipal({}).subsumes(null), "NS_ERROR_ILLEGAL_VALUE"); +} diff --git a/js/xpconnect/tests/unit/test_bug1244222.js b/js/xpconnect/tests/unit/test_bug1244222.js new file mode 100644 index 000000000..a5cc39a41 --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug1244222.js @@ -0,0 +1,27 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +function run_test() { + registerAppManifest(do_get_file('../components/js/xpctest.manifest')); + + // Generate a CCW to a function. + var sb = new Cu.Sandbox(this); + sb.eval('function fun(x) { return x; }'); + do_check_eq(sb.fun("foo"), "foo"); + + // Double-wrap the CCW. + var utils = Cc["@mozilla.org/js/xpc/test/js/TestUtils;1"].createInstance(Ci.nsIXPCTestUtils); + var doubleWrapped = utils.doubleWrapFunction(sb.fun); + do_check_eq(doubleWrapped.echo("foo"), "foo"); + + // GC. + Cu.forceGC(); + + // Make sure it still works. + do_check_eq(doubleWrapped.echo("foo"), "foo"); +} diff --git a/js/xpconnect/tests/unit/test_bug408412.js b/js/xpconnect/tests/unit/test_bug408412.js new file mode 100644 index 000000000..d3d60cd23 --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug408412.js @@ -0,0 +1,17 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function run_test() { + var file = do_get_file("syntax_error.jsm"); + var ios = Components.classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService); + var uri = ios.newFileURI(file); + + try { + Components.utils.import(uri.spec); + do_throw("Failed to report any error at all"); + } catch (e) { + do_check_neq(/^SyntaxError:/.exec(e + ''), null); + } +} diff --git a/js/xpconnect/tests/unit/test_bug451678.js b/js/xpconnect/tests/unit/test_bug451678.js new file mode 100644 index 000000000..a9454a26b --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug451678.js @@ -0,0 +1,18 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const Cc = Components.classes; +const Ci = Components.interfaces; + +function run_test() { + var file = do_get_file("bug451678_subscript.js"); + var ios = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); + var uri = ios.newFileURI(file); + var scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"] + .getService(Ci.mozIJSSubScriptLoader); + var srvScope = {}; + scriptLoader.loadSubScript(uri.spec, srvScope); + do_check_true('makeTags' in srvScope && srvScope.makeTags instanceof Function); +} diff --git a/js/xpconnect/tests/unit/test_bug604362.js b/js/xpconnect/tests/unit/test_bug604362.js new file mode 100644 index 000000000..f158d9497 --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug604362.js @@ -0,0 +1,12 @@ +function run_test() { + var Cc = Components.classes; + var Ci = Components.interfaces; + var sp = Cc["@mozilla.org/systemprincipal;1"]. + createInstance(Ci.nsIPrincipal); + var s = Components.utils.Sandbox(sp); + s.a = []; + s.Cu = Components.utils; + s.C = Components; + s.do_check_neq = do_check_neq; + Components.utils.evalInSandbox("do_check_neq(Cu.import, undefined);", s); +} diff --git a/js/xpconnect/tests/unit/test_bug677864.js b/js/xpconnect/tests/unit/test_bug677864.js new file mode 100644 index 000000000..a90a7b137 --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug677864.js @@ -0,0 +1,11 @@ +function check_cl(iface, desc) { + do_check_eq(iface.QueryInterface(Components.interfaces.nsIClassInfo).classDescription, desc); +} + +function run_test() { + check_cl(Components.interfaces, 'XPCComponents_Interfaces'); + check_cl(Components.interfacesByID, 'XPCComponents_InterfacesByID'); + check_cl(Components.classes, 'XPCComponents_Classes'); + check_cl(Components.classesByID, 'XPCComponents_ClassesByID'); + check_cl(Components.results, 'XPCComponents_Results'); +} diff --git a/js/xpconnect/tests/unit/test_bug711404.js b/js/xpconnect/tests/unit/test_bug711404.js new file mode 100644 index 000000000..77a5ce40a --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug711404.js @@ -0,0 +1,10 @@ +const Cc = Components.classes; +const Ci = Components.interfaces; + +function run_test() +{ + var p = Cc["@mozilla.org/hash-property-bag;1"]. + createInstance(Ci.nsIWritablePropertyBag2); + p.setPropertyAsInt64("a", -4000); + do_check_neq(p.getPropertyAsUint64("a"), -4000); +} diff --git a/js/xpconnect/tests/unit/test_bug742444.js b/js/xpconnect/tests/unit/test_bug742444.js new file mode 100644 index 000000000..8d8b5643a --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug742444.js @@ -0,0 +1,17 @@ +const Cu = Components.utils; +function run_test() { + let sb1A = Cu.Sandbox('http://www.example.com'); + let sb1B = Cu.Sandbox('http://www.example.com'); + let sb2 = Cu.Sandbox('http://www.example.org'); + let sbChrome = Cu.Sandbox(this); + let obj = new sb1A.Object(); + sb1B.obj = obj; + sb1B.waived = Cu.waiveXrays(obj); + sb2.obj = obj; + sb2.waived = Cu.waiveXrays(obj); + sbChrome.obj = obj; + sbChrome.waived = Cu.waiveXrays(obj); + do_check_true(Cu.evalInSandbox('obj === waived', sb1B)); + do_check_true(Cu.evalInSandbox('obj === waived', sb2)); + do_check_true(Cu.evalInSandbox('obj !== waived', sbChrome)); +} diff --git a/js/xpconnect/tests/unit/test_bug778409.js b/js/xpconnect/tests/unit/test_bug778409.js new file mode 100644 index 000000000..4a905ad0f --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug778409.js @@ -0,0 +1,11 @@ +function run_test() { + var Cu = Components.utils; + var sb1 = Cu.Sandbox('http://example.com'); + var sb2 = Cu.Sandbox('http://example.org'); + var chromeObj = {foo: 2}; + var sb1obj = Cu.evalInSandbox('new Object()', sb1); + chromeObj.__proto__ = sb1obj; + sb2.wrapMe = chromeObj; + do_check_true(true, "Didn't crash"); + do_check_eq(sb2.wrapMe.__proto__, sb1obj, 'proto set correctly'); +} diff --git a/js/xpconnect/tests/unit/test_bug780370.js b/js/xpconnect/tests/unit/test_bug780370.js new file mode 100644 index 000000000..40d6f9748 --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug780370.js @@ -0,0 +1,23 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* See https://bugzilla.mozilla.org/show_bug.cgi?id=780370 */ + +const Cu = Components.utils; + +// Use a COW to expose a function from a standard prototype, and make we deny +// access to it. + +function run_test() +{ + var sb = Cu.Sandbox("http://www.example.com"); + sb.obj = { foo: 42, __exposedProps__: { hasOwnProperty: 'r' } }; + do_check_eq(Cu.evalInSandbox('typeof obj.foo', sb), 'undefined', "COW works as expected"); + try { + Cu.evalInSandbox('obj.hasOwnProperty', sb); + do_check_true(false); + } catch (e) { + do_check_true(/privileged or cross-origin callable/i.test(e)); + } +} diff --git a/js/xpconnect/tests/unit/test_bug809652.js b/js/xpconnect/tests/unit/test_bug809652.js new file mode 100644 index 000000000..66dd6d550 --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug809652.js @@ -0,0 +1,63 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* See https://bugzilla.mozilla.org/show_bug.cgi?id=813901 */ + +const Cu = Components.utils; +const TypedArrays = [ Int8Array, Uint8Array, Int16Array, Uint16Array, + Int32Array, Uint32Array, Float32Array, Float64Array, + Uint8ClampedArray ]; + +// Make sure that the correct nativecall-y stuff is denied on security wrappers. + +function run_test() { + + var sb = new Cu.Sandbox('http://www.example.org'); + sb.obj = {foo: 2}; + + /* Set up some typed arrays. */ + sb.ab = new ArrayBuffer(8); + for (var i = 0; i < 8; ++i) + new Uint8Array(sb.ab)[i] = i * 10; + sb.ta = []; + TypedArrays.forEach(f => sb.ta.push(new f(sb.ab))); + sb.dv = new DataView(sb.ab); + + /* Things that should throw. */ + checkThrows("Object.prototype.__lookupSetter__('__proto__').call(obj, {});", sb); + sb.re = /f/; + checkThrows("RegExp.prototype.exec.call(re, 'abcdefg').index", sb); + sb.d = new Date(); + checkThrows("Date.prototype.setYear.call(d, 2011)", sb); + sb.m = new Map(); + checkThrows("(new Map()).clear.call(m)", sb); + checkThrows("ArrayBuffer.prototype.__lookupGetter__('byteLength').call(ab);", sb); + checkThrows("ArrayBuffer.prototype.slice.call(ab, 0);", sb); + checkThrows("DataView.prototype.getInt8.call(dv, 0);", sb); + + /* Now that Date is on Xrays, these should all throw. */ + checkThrows("Date.prototype.getYear.call(d)", sb); + checkThrows("Date.prototype.valueOf.call(d)", sb); + checkThrows("d.valueOf()", sb); + checkThrows("d.toString()", sb); + + /* Typed arrays. */ + function testForTypedArray(t) { + sb.curr = t; + sb.currName = t.constructor.name; + checkThrows("this[currName].prototype.subarray.call(curr, 0)[0]", sb); + checkThrows("(new this[currName]).__lookupGetter__('length').call(curr)", sb); + checkThrows("(new this[currName]).__lookupGetter__('buffer').call(curr)", sb); + checkThrows("(new this[currName]).__lookupGetter__('byteOffset').call(curr)", sb); + checkThrows("(new this[currName]).__lookupGetter__('byteLength').call(curr)", sb); + } + sb.ta.forEach(testForTypedArray); +} + +function checkThrows(expression, sb) { + var result = Cu.evalInSandbox('(function() { try { ' + expression + '; return "allowed"; } catch (e) { return e.toString(); }})();', sb); + dump('result: ' + result + '\n\n\n'); + do_check_true(!!/denied/.exec(result)); +} + diff --git a/js/xpconnect/tests/unit/test_bug809674.js b/js/xpconnect/tests/unit/test_bug809674.js new file mode 100644 index 000000000..76bfdf742 --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug809674.js @@ -0,0 +1,31 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const Cc = Components.classes; +const Ci = Components.interfaces; + +function run_test() { + + // Load the component manifest. + Components.manager.autoRegister(do_get_file('../components/js/xpctest.manifest')); + + // Test for each component. + test_property_throws("@mozilla.org/js/xpc/test/js/Bug809674;1"); +} + +function test_property_throws(contractid) { + + // Instantiate the object. + var o = Cc[contractid].createInstance(Ci["nsIXPCTestBug809674"]); + + // Test the initial values. + try { + o.jsvalProperty; + do_check_true(false); + } catch (e) { + do_check_true(true); + do_check_true(/implicit_jscontext/.test(e)) + } + +} diff --git a/js/xpconnect/tests/unit/test_bug813901.js b/js/xpconnect/tests/unit/test_bug813901.js new file mode 100644 index 000000000..42f981581 --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug813901.js @@ -0,0 +1,25 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* See https://bugzilla.mozilla.org/show_bug.cgi?id=813901 */ + +const Cu = Components.utils; + +// Make sure that we can't inject __exposedProps__ via the proto of a COW-ed object. + +function checkThrows(expression, sb, regexp) { + var result = Cu.evalInSandbox('(function() { try { ' + expression + '; return "allowed"; } catch (e) { return e.toString(); }})();', sb); + dump('result: ' + result + '\n\n\n'); + do_check_true(!!regexp.exec(result)); +} + +function run_test() { + + var sb = new Cu.Sandbox('http://www.example.org'); + sb.obj = {foo: 2}; + checkThrows('obj.foo = 3;', sb, /denied/); + Cu.evalInSandbox("var p = {__exposedProps__: {foo: 'rw'}};", sb); + sb.obj.__proto__ = sb.p; + checkThrows('obj.foo = 4;', sb, /__exposedProps__/); +} diff --git a/js/xpconnect/tests/unit/test_bug845201.js b/js/xpconnect/tests/unit/test_bug845201.js new file mode 100644 index 000000000..8b0d143be --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug845201.js @@ -0,0 +1,18 @@ +function sbTest() { + var threw = false; + try { + for (var x in Components) { } + do_check_true(false, "Shouldn't be able to enumerate Components"); + } catch(e) { + do_check_true(true, "Threw appropriately"); + threw = true; + } + do_check_true(threw, "Shouldn't have thrown uncatchable exception"); +} + +function run_test() { + var sb = Components.utils.Sandbox('http://www.example.com', { wantComponents: true }); + sb.do_check_true = do_check_true; + Components.utils.evalInSandbox(sbTest.toSource(), sb); + Components.utils.evalInSandbox('sbTest();', sb); +} diff --git a/js/xpconnect/tests/unit/test_bug845862.js b/js/xpconnect/tests/unit/test_bug845862.js new file mode 100644 index 000000000..650bdf3cd --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug845862.js @@ -0,0 +1,13 @@ +const Cu = Components.utils; + +function run_test() { + // We rely on the crazy "wantXrays:false also causes values return from the + // sandbox to be waived" behavior, because it's the simplest way to get + // waivers out of the sandbox (which has no native objects). :-( + var sb = new Cu.Sandbox('http://www.example.com', {wantXrays: false}); + Cu.evalInSandbox("this.foo = {}; Object.defineProperty(foo, 'bar', {get: function() {return {};}});", sb); + do_check_true(sb.foo != XPCNativeWrapper(sb.foo), "sb.foo is waived"); + var desc = Object.getOwnPropertyDescriptor(sb.foo, 'bar'); + var b = desc.get(); + do_check_true(b != XPCNativeWrapper(b), "results from accessor descriptors are waived"); +} diff --git a/js/xpconnect/tests/unit/test_bug849730.js b/js/xpconnect/tests/unit/test_bug849730.js new file mode 100644 index 000000000..8622b5bca --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug849730.js @@ -0,0 +1,7 @@ +const Cu = Components.utils; + +function run_test() { + var sb = new Cu.Sandbox('http://www.example.com'); + sb.arr = [3, 4]; + do_check_true(Cu.evalInSandbox('!Array.isArray(arr);', sb)); +} diff --git a/js/xpconnect/tests/unit/test_bug851895.js b/js/xpconnect/tests/unit/test_bug851895.js new file mode 100644 index 000000000..7cab5b12d --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug851895.js @@ -0,0 +1,11 @@ +const Cu = Components.utils; + +function run_test() { + // Make sure Components.utils gets its |this| fixed up. + var isXrayWrapper = Components.utils.isXrayWrapper; + do_check_true(!isXrayWrapper({}), "Didn't throw"); + + // Even for classes without |this| fixup, make sure that we don't crash. + var isSuccessCode = Components.isSuccessCode; + try { isSuccessCode(Components.results.NS_OK); } catch (e) {}; +} diff --git a/js/xpconnect/tests/unit/test_bug853709.js b/js/xpconnect/tests/unit/test_bug853709.js new file mode 100644 index 000000000..c7e51757d --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug853709.js @@ -0,0 +1,32 @@ +const Cu = Components.utils; + +function setupChromeSandbox() { + this.chromeObj = {a: 2, __exposedProps__: {a: "rw", b: "rw"} }; + this.chromeArr = [4, 2, 1]; +} + +function checkDefineThrows(sb, obj, prop, desc) { + var result = Cu.evalInSandbox('(function() { try { Object.defineProperty(' + obj + ', "' + prop + '", ' + desc.toSource() + '); return "nothrow"; } catch (e) { return e.toString(); }})();', sb); + do_check_neq(result, 'nothrow'); + do_check_true(!!/denied/.exec(result)); + do_check_true(result.indexOf(prop) != -1); // Make sure the prop name is in the error message. +} + +function run_test() { + var chromeSB = new Cu.Sandbox(this); + var contentSB = new Cu.Sandbox('http://www.example.org'); + Cu.evalInSandbox('(' + setupChromeSandbox.toSource() + ')()', chromeSB); + contentSB.chromeObj = chromeSB.chromeObj; + contentSB.chromeArr = chromeSB.chromeArr; + + do_check_eq(Cu.evalInSandbox('chromeObj.a', contentSB), 2); + try { + Cu.evalInSandbox('chromeArr[1]', contentSB); + do_check_true(false); + } catch (e) { do_check_true(/denied|insecure/.test(e)); } + + checkDefineThrows(contentSB, 'chromeObj', 'a', {get: function() { return 2; }}); + checkDefineThrows(contentSB, 'chromeObj', 'a', {configurable: true, get: function() { return 2; }}); + checkDefineThrows(contentSB, 'chromeObj', 'b', {configurable: true, get: function() { return 2; }, set: function() {}}); + checkDefineThrows(contentSB, 'chromeArr', '1', {configurable: true, get: function() { return 2; }}); +} diff --git a/js/xpconnect/tests/unit/test_bug854558.js b/js/xpconnect/tests/unit/test_bug854558.js new file mode 100644 index 000000000..d60d23a5b --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug854558.js @@ -0,0 +1,11 @@ +const Cu = Components.utils; +function run_test() { + + var chromeSB = new Cu.Sandbox(this); + var contentSB = new Cu.Sandbox('http://www.example.com'); + Cu.evalInSandbox('this.foo = {a: 2}', chromeSB); + contentSB.foo = chromeSB.foo; + do_check_eq(Cu.evalInSandbox('foo.a', contentSB), undefined, "Default deny with no __exposedProps__"); + Cu.evalInSandbox('this.foo.__exposedProps__ = {a: "r"}', chromeSB); + do_check_eq(Cu.evalInSandbox('foo.a', contentSB), 2, "works with __exposedProps__"); +} diff --git a/js/xpconnect/tests/unit/test_bug856067.js b/js/xpconnect/tests/unit/test_bug856067.js new file mode 100644 index 000000000..b23cd45fb --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug856067.js @@ -0,0 +1,10 @@ +const Cu = Components.utils; + +function run_test() { + var sb = new Cu.Sandbox('http://www.example.com'); + let w = Cu.evalInSandbox('var w = new WeakMap(); w.__proto__ = new Set(); w.foopy = 12; w', sb); + do_check_eq(Object.getPrototypeOf(w), sb.Object.prototype); + do_check_eq(Object.getOwnPropertyNames(w).length, 0); + do_check_eq(w.wrappedJSObject.foopy, 12); + do_check_eq(w.foopy, undefined); +} diff --git a/js/xpconnect/tests/unit/test_bug867486.js b/js/xpconnect/tests/unit/test_bug867486.js new file mode 100644 index 000000000..a807fa44e --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug867486.js @@ -0,0 +1,10 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const Cu = Components.utils; + +function run_test() { + var sb = new Cu.Sandbox('http://www.example.com', { wantComponents: true } ); + do_check_false(Cu.evalInSandbox('"Components" in this', sb)); +} diff --git a/js/xpconnect/tests/unit/test_bug868675.js b/js/xpconnect/tests/unit/test_bug868675.js new file mode 100644 index 000000000..5aac9c7d6 --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug868675.js @@ -0,0 +1,25 @@ +const Cu = Components.utils; +function run_test() { + + // Make sure we don't throw for primitive values. + var result = "threw"; + try { result = XPCNativeWrapper.unwrap(2); } catch (e) {} + do_check_eq(result, 2); + result = "threw"; + try { result = XPCNativeWrapper(2); } catch (e) {} + do_check_eq(result, 2); + + // Make sure that we can waive on a non-Xrayable object, and that we preserve + // transitive waiving behavior. + var sb = new Cu.Sandbox('http://www.example.com', { wantGlobalProperties: ["XMLHttpRequest"] }); + Cu.evalInSandbox('this.xhr = new XMLHttpRequest();', sb); + Cu.evalInSandbox('this.jsobj = {mynative: xhr};', sb); + do_check_true(!Cu.isXrayWrapper(XPCNativeWrapper.unwrap(sb.xhr))); + do_check_true(Cu.isXrayWrapper(sb.jsobj.mynative)); + do_check_true(!Cu.isXrayWrapper(XPCNativeWrapper.unwrap(sb.jsobj).mynative)); + + // Test the new Cu API. + var waived = Cu.waiveXrays(sb.xhr); + do_check_true(!Cu.isXrayWrapper(waived)); + do_check_true(Cu.isXrayWrapper(Cu.unwaiveXrays(waived))); +} diff --git a/js/xpconnect/tests/unit/test_bug872772.js b/js/xpconnect/tests/unit/test_bug872772.js new file mode 100644 index 000000000..dc2e0db29 --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug872772.js @@ -0,0 +1,43 @@ +const Cu = Components.utils; +function run_test() { + + // Make a content sandbox with an Xrayable object. + // NB: We use an nsEP here so that we can have access to Components, but still + // have Xray behavior from this scope. + var contentSB = new Cu.Sandbox(['http://www.google.com'], + { wantGlobalProperties: ["XMLHttpRequest"], wantComponents: true }); + + // Make an XHR in the content sandbox. + Cu.evalInSandbox('xhr = new XMLHttpRequest();', contentSB); + + // Make sure that waivers can be set as Xray expandos. + var xhr = contentSB.xhr; + do_check_true(Cu.isXrayWrapper(xhr)); + xhr.unwaivedExpando = xhr; + do_check_true(Cu.isXrayWrapper(xhr.unwaivedExpando)); + var waived = xhr.wrappedJSObject; + do_check_true(!Cu.isXrayWrapper(waived)); + xhr.waivedExpando = waived; + do_check_true(!Cu.isXrayWrapper(xhr.waivedExpando)); + + // Try the same thing for getters/setters, even though that's kind of + // contrived. + Cu.evalInSandbox('function f() {}', contentSB); + var f = contentSB.f; + var fWaiver = Cu.waiveXrays(f); + do_check_true(f != fWaiver); + do_check_true(Cu.unwaiveXrays(fWaiver) === f); + Object.defineProperty(xhr, 'waivedAccessors', {get: fWaiver, set: fWaiver}); + var desc = Object.getOwnPropertyDescriptor(xhr, 'waivedAccessors'); + do_check_true(desc.get === fWaiver); + do_check_true(desc.set === fWaiver); + + // Make sure we correctly handle same-compartment security wrappers. + var unwaivedC = contentSB.Components; + do_check_true(Cu.isXrayWrapper(unwaivedC)); + var waivedC = unwaivedC.wrappedJSObject; + do_check_true(waivedC && unwaivedC && (waivedC != unwaivedC)); + xhr.waivedC = waivedC; + do_check_true(xhr.waivedC === waivedC); + do_check_true(Cu.unwaiveXrays(xhr.waivedC) === unwaivedC); +} diff --git a/js/xpconnect/tests/unit/test_bug885800.js b/js/xpconnect/tests/unit/test_bug885800.js new file mode 100644 index 000000000..bb859141d --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug885800.js @@ -0,0 +1,13 @@ +const Cu = Components.utils; + +function run_test() { + var sb = new Cu.Sandbox('http://www.example.com'); + var obj = Cu.evalInSandbox('this.obj = {foo: 2}; obj', sb); + var chromeSb = new Cu.Sandbox(this); + chromeSb.objRef = obj; + do_check_eq(Cu.evalInSandbox('objRef.foo', chromeSb), 2); + Cu.nukeSandbox(sb); + do_check_true(Cu.isDeadWrapper(obj)); + // CCWs to nuked wrappers should be considered dead. + do_check_true(Cu.isDeadWrapper(chromeSb.objRef)); +} diff --git a/js/xpconnect/tests/unit/test_bug930091.js b/js/xpconnect/tests/unit/test_bug930091.js new file mode 100644 index 000000000..aa11d5db2 --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug930091.js @@ -0,0 +1,29 @@ +const Cu = Components.utils; + +function checkThrows(fn) { + try { + fn(); + ok(false, "Should have thrown"); + } catch (e) { + do_check_true(/denied|insecure/.test(e)); + } +} + +function run_test() { + var xosb = new Cu.Sandbox('http://www.example.org'); + var sb = new Cu.Sandbox('http://www.example.com'); + sb.do_check_true = do_check_true; + sb.fun = function() { ok(false, "Shouldn't ever reach me"); }; + sb.cow = { foopy: 2, __exposedProps__: { foopy: 'rw' } }; + sb.payload = Cu.evalInSandbox('new Object()', xosb); + Cu.evalInSandbox(checkThrows.toSource(), sb); + Cu.evalInSandbox('checkThrows(function() { fun(payload); });', sb); + Cu.evalInSandbox('checkThrows(function() { Function.prototype.call.call(fun, payload); });', sb); + Cu.evalInSandbox('checkThrows(function() { Function.prototype.call.call(fun, null, payload); });', sb); + Cu.evalInSandbox('checkThrows(function() { new fun(payload); });', sb); + Cu.evalInSandbox('checkThrows(function() { cow.foopy = payload; });', sb); + Cu.evalInSandbox('checkThrows(function() { Object.defineProperty(cow, "foopy", { value: payload }); });', sb); + // These fail for a different reason, .bind can't access the length/name property on the function. + Cu.evalInSandbox('checkThrows(function() { Function.bind.call(fun, null, payload); });', sb); + Cu.evalInSandbox('checkThrows(function() { Function.bind.call(fun, payload); });', sb); +} diff --git a/js/xpconnect/tests/unit/test_bug976151.js b/js/xpconnect/tests/unit/test_bug976151.js new file mode 100644 index 000000000..af4a22d3e --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug976151.js @@ -0,0 +1,24 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const Cu = Components.utils; +function run_test() { + let unprivilegedSb = new Cu.Sandbox('http://www.example.com'); + function checkOpaqueWrapper(val) { + unprivilegedSb.prop = val; + try { + Cu.evalInSandbox('prop();', sb); + } catch (e) { + do_check_true(/denied|insecure|/.test(e)); + } + } + let xoSb = new Cu.Sandbox('http://www.example.net'); + let epSb = new Cu.Sandbox(['http://www.example.com']); + checkOpaqueWrapper(eval); + checkOpaqueWrapper(xoSb.eval); + checkOpaqueWrapper(epSb.eval); + checkOpaqueWrapper(Function); + checkOpaqueWrapper(xoSb.Function); + checkOpaqueWrapper(epSb.Function); +} diff --git a/js/xpconnect/tests/unit/test_bug_442086.js b/js/xpconnect/tests/unit/test_bug_442086.js new file mode 100644 index 000000000..8de098121 --- /dev/null +++ b/js/xpconnect/tests/unit/test_bug_442086.js @@ -0,0 +1,36 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Bug 442086 - XPConnect creates doubles without checking for +// the INT_FITS_IN_JSVAL case + +var types = [ + 'PRUint8', + 'PRUint16', + 'PRUint32', + 'PRUint64', + 'PRInt16', + 'PRInt32', + 'PRInt64', + 'float', + 'double' +]; + +function run_test() +{ + var i; + for (i = 0; i < types.length; i++) { + var name = types[i]; + var cls = Components.classes["@mozilla.org/supports-" + name + ";1"]; + var ifname = ("nsISupports" + name.charAt(0).toUpperCase() + + name.substring(1)); + var f = cls.createInstance(Components.interfaces[ifname]); + + f.data = 0; + switch (f.data) { + case 0: /*ok*/ break; + default: do_throw("FAILED - bug 442086 (type=" + name + ")"); + } + } +} diff --git a/js/xpconnect/tests/unit/test_callFunctionWithAsyncStack.js b/js/xpconnect/tests/unit/test_callFunctionWithAsyncStack.js new file mode 100644 index 000000000..aaf6e849b --- /dev/null +++ b/js/xpconnect/tests/unit/test_callFunctionWithAsyncStack.js @@ -0,0 +1,30 @@ +Components.utils.import("resource://gre/modules/Services.jsm"); + +function run_test() { + if (!Services.prefs.getBoolPref("javascript.options.asyncstack")) { + do_print("Async stacks are disabled."); + return; + } + + function getAsyncStack() { + return Components.stack; + } + + // asyncCause may contain non-ASCII characters. + let testAsyncCause = "Tes" + String.fromCharCode(355) + "String"; + + Components.utils.callFunctionWithAsyncStack(function asyncCallback() { + let stack = Components.stack; + + do_check_eq(stack.name, "asyncCallback"); + do_check_eq(stack.caller, null); + do_check_eq(stack.asyncCause, null); + + do_check_eq(stack.asyncCaller.name, "getAsyncStack"); + do_check_eq(stack.asyncCaller.asyncCause, testAsyncCause); + do_check_eq(stack.asyncCaller.asyncCaller, null); + + do_check_eq(stack.asyncCaller.caller.name, "run_test"); + do_check_eq(stack.asyncCaller.caller.asyncCause, null); + }, getAsyncStack(), testAsyncCause); +} diff --git a/js/xpconnect/tests/unit/test_classesByID_instanceof.js b/js/xpconnect/tests/unit/test_classesByID_instanceof.js new file mode 100644 index 000000000..b0acf8016 --- /dev/null +++ b/js/xpconnect/tests/unit/test_classesByID_instanceof.js @@ -0,0 +1,79 @@ +function testActual(SimpleURIClassByID) +{ + var simpleURI = + Components.classes["@mozilla.org/network/simple-uri;1"].createInstance(); + + do_check_eq(simpleURI instanceof SimpleURIClassByID, true); +} + +function testInherited(SimpleURIClassByID) +{ + var simpleURI = + Components.classes["@mozilla.org/network/simple-uri;1"].createInstance(); + + var inherited = Object.create(simpleURI); + + do_check_eq(inherited instanceof SimpleURIClassByID, true); +} + +function testInheritedCrossGlobal(SimpleURIClassByID) +{ + var simpleURI = + Components.classes["@mozilla.org/network/simple-uri;1"].createInstance(); + + var sb = new Components.utils.Sandbox(this, { wantComponents: true }); + var inheritedCross = sb.Object.create(simpleURI); + + do_check_eq(inheritedCross instanceof SimpleURIClassByID, true); +} + +function testCrossGlobalArbitraryGetPrototype(SimpleURIClassByID) +{ + var simpleURI = + Components.classes["@mozilla.org/network/simple-uri;1"].createInstance(); + + var sb = new Components.utils.Sandbox(this, { wantComponents: true }); + var firstLevel = Object.create(simpleURI); + + var obj = { shouldThrow: false }; + var secondLevel = + new sb.Proxy(Object.create(firstLevel), + { + getPrototypeOf: new sb.Function("obj", `return function(t) { + if (obj.shouldThrow) + throw 42; + return Reflect.getPrototypeOf(t); + };`)(obj) + }); + var thirdLevel = Object.create(secondLevel); + + obj.shouldThrow = true; + + var threw = false; + var err; + try + { + void (thirdLevel instanceof SimpleURIClassByID); + } + catch (e) + { + threw = true; + err = e; + } + + do_check_eq(threw, true); + do_check_eq(err, 42); + + obj.shouldThrow = false; + + do_check_eq(thirdLevel instanceof SimpleURIClassByID, true); +} + +function run_test() { + var SimpleURIClassByID = Components.classesByID["{e0da1d70-2f7b-11d3-8cd0-0060b0fc14a3}"]; + + testActual(SimpleURIClassByID); + testInherited(SimpleURIClassByID); + testInheritedCrossGlobal(SimpleURIClassByID); + testCrossGlobalArbitraryGetPrototype(SimpleURIClassByID); +} diff --git a/js/xpconnect/tests/unit/test_components.js b/js/xpconnect/tests/unit/test_components.js new file mode 100644 index 000000000..623a365c0 --- /dev/null +++ b/js/xpconnect/tests/unit/test_components.js @@ -0,0 +1,54 @@ +const Cu = Components.utils; + +function run_test() { + var sb1 = Cu.Sandbox("http://www.blah.com"); + var sb2 = Cu.Sandbox("http://www.blah.com"); + var sb3 = Cu.Sandbox(this); + var sb4 = Cu.Sandbox("http://www.other.com"); + var rv; + + // Components is normally hidden from content on the XBL scope chain, but we + // expose it to content here to make sure that the security wrappers work + // regardless. + [sb1, sb2, sb4].forEach(function(x) { x.Components = Cu.getComponentsForScope(x); }); + + // non-chrome accessing chrome Components + sb1.C = Components; + checkThrows("C.utils", sb1); + checkThrows("C.classes", sb1); + + // non-chrome accessing own Components + do_check_eq(Cu.evalInSandbox("typeof Components.interfaces", sb1), 'object'); + do_check_eq(Cu.evalInSandbox("typeof Components.utils", sb1), 'undefined'); + do_check_eq(Cu.evalInSandbox("typeof Components.classes", sb1), 'undefined'); + + // Make sure an unprivileged Components is benign. + var C2 = Cu.evalInSandbox("Components", sb2); + var whitelist = ['interfaces', 'interfacesByID', 'results', 'isSuccessCode', 'QueryInterface']; + for (var prop in Components) { + do_print("Checking " + prop); + do_check_eq((prop in C2), whitelist.indexOf(prop) != -1); + } + + // non-chrome same origin + sb1.C2 = C2; + do_check_eq(Cu.evalInSandbox("typeof C2.interfaces", sb1), 'object'); + do_check_eq(Cu.evalInSandbox("typeof C2.utils", sb1), 'undefined'); + do_check_eq(Cu.evalInSandbox("typeof C2.classes", sb1), 'undefined'); + + // chrome accessing chrome + sb3.C = Components; + rv = Cu.evalInSandbox("C.utils", sb3); + do_check_eq(rv, Cu); + + // non-chrome cross origin + sb4.C2 = C2; + checkThrows("C2.interfaces", sb4); + checkThrows("C2.utils", sb4); + checkThrows("C2.classes", sb4); +} + +function checkThrows(expression, sb) { + var result = Cu.evalInSandbox('(function() { try { ' + expression + '; return "allowed"; } catch (e) { return e.toString(); }})();', sb); + do_check_true(!!/denied/.exec(result)); +} diff --git a/js/xpconnect/tests/unit/test_crypto.js b/js/xpconnect/tests/unit/test_crypto.js new file mode 100644 index 000000000..90c0f9ed2 --- /dev/null +++ b/js/xpconnect/tests/unit/test_crypto.js @@ -0,0 +1,28 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +function run_test() { + let Cu = Components.utils; + let sb = new Cu.Sandbox('https://www.example.com', + { wantGlobalProperties: + ["crypto", "TextEncoder", "TextDecoder"] + }); + sb.ok = ok; + Cu.evalInSandbox('ok(this.crypto);', sb); + Cu.evalInSandbox('ok(this.crypto.subtle);', sb); + sb.do_check_eq = do_check_eq; + let innerPromise = new Promise(r => (sb.test_done = r)); + Cu.evalInSandbox('crypto.subtle.digest("SHA-256", ' + + ' new TextEncoder("utf-8").encode("abc"))' + + ' .then(h => do_check_eq(new Uint16Array(h)[0], 30906))' + + ' .then(test_done);', sb); + + Cu.importGlobalProperties(["crypto"]); + ok(crypto); + ok(crypto.subtle); + let outerPromise = crypto.subtle.digest("SHA-256", new TextEncoder("utf-8").encode("abc")) + .then(h => do_check_eq(new Uint16Array(h)[0], 30906)); + + do_test_pending(); + Promise.all([innerPromise, outerPromise]).then(() => do_test_finished()); +} diff --git a/js/xpconnect/tests/unit/test_css.js b/js/xpconnect/tests/unit/test_css.js new file mode 100644 index 000000000..5af9d74b6 --- /dev/null +++ b/js/xpconnect/tests/unit/test_css.js @@ -0,0 +1,10 @@ +function run_test() { + var Cu = Components.utils; + var sb = new Cu.Sandbox('http://www.example.com', + { wantGlobalProperties: ["CSS"] }); + sb.do_check_eq = do_check_eq; + Cu.evalInSandbox('do_check_eq(CSS.escape("$"), "\\\\$");', + sb); + Cu.importGlobalProperties(["CSS"]); + do_check_eq(CSS.escape("$"), "\\$"); +} diff --git a/js/xpconnect/tests/unit/test_deepFreezeClone.js b/js/xpconnect/tests/unit/test_deepFreezeClone.js new file mode 100644 index 000000000..67ea4e66d --- /dev/null +++ b/js/xpconnect/tests/unit/test_deepFreezeClone.js @@ -0,0 +1,33 @@ +const Cu = Components.utils; + +function checkThrows(f, rgxp) { try { f(); do_check_false(); } catch (e) { do_check_true(rgxp.test(e)); } } + +var o = { foo: 42, bar : { tick: 'tock' } }; +function checkClone(clone, frozen) { + var waived = Cu.waiveXrays(clone); + function touchFoo() { "use strict"; waived.foo = 12; do_check_eq(waived.foo, 12); } + function touchBar() { "use strict"; waived.bar.tick = 'tack'; do_check_eq(waived.bar.tick, 'tack'); } + function addProp() { "use strict"; waived.newProp = 100; do_check_eq(waived.newProp, 100); } + if (!frozen) { + touchFoo(); + touchBar(); + addProp(); + } else { + checkThrows(touchFoo, /read-only/); + checkThrows(touchBar, /read-only/); + checkThrows(addProp, /extensible/); + } + + var desc = Object.getOwnPropertyDescriptor(waived, 'foo'); + do_check_eq(desc.writable, !frozen); + do_check_eq(desc.configurable, !frozen); + desc = Object.getOwnPropertyDescriptor(waived.bar, 'tick'); + do_check_eq(desc.writable, !frozen); + do_check_eq(desc.configurable, !frozen); +} + +function run_test() { + var sb = new Cu.Sandbox(null); + checkClone(Cu.waiveXrays(Cu.cloneInto(o, sb)), false); + checkClone(Cu.cloneInto(o, sb, { deepFreeze: true }), true); +} diff --git a/js/xpconnect/tests/unit/test_exportFunction.js b/js/xpconnect/tests/unit/test_exportFunction.js new file mode 100644 index 000000000..830816342 --- /dev/null +++ b/js/xpconnect/tests/unit/test_exportFunction.js @@ -0,0 +1,150 @@ +function run_test() { + var Cu = Components.utils; + var epsb = new Cu.Sandbox(["http://example.com", "http://example.org"], { wantExportHelpers: true }); + var subsb = new Cu.Sandbox("http://example.com", { wantGlobalProperties: ["XMLHttpRequest"] }); + var subsb2 = new Cu.Sandbox("http://example.com", { wantGlobalProperties: ["XMLHttpRequest"] }); + var xorigsb = new Cu.Sandbox("http://test.com", { wantGlobalProperties: ["XMLHttpRequest"] }); + + epsb.subsb = subsb; + epsb.xorigsb = xorigsb; + epsb.do_check_true = do_check_true; + epsb.do_check_eq = do_check_eq; + subsb.do_check_true = do_check_true; + + // Exporting should work if prinicipal of the source sandbox + // subsumes the principal of the target sandbox. + Cu.evalInSandbox("(" + function() { + var wasCalled = false; + this.funToExport = function(expectedThis, a, obj, native, mixed, callback) { + do_check_eq(a, 42); + do_check_eq(obj, subsb.tobecloned); + do_check_eq(obj.cloned, "cloned"); + do_check_eq(native, subsb.native); + do_check_eq(expectedThis, this); + do_check_eq(mixed.xrayed, subsb.xrayed); + do_check_eq(mixed.xrayed2, subsb.xrayed2); + if (typeof callback == 'function') { + do_check_eq(typeof subsb.callback, 'function'); + do_check_eq(callback, subsb.callback); + callback(); + } + wasCalled = true; + }; + this.checkIfCalled = function() { + do_check_true(wasCalled); + wasCalled = false; + } + exportFunction(funToExport, subsb, { defineAs: "imported", allowCallbacks: true }); + exportFunction((x) => x, subsb, { defineAs: "echoAllowXO", allowCallbacks: true, allowCrossOriginArguments: true }); + }.toSource() + ")()", epsb); + + subsb.xrayed = Cu.evalInSandbox("(" + function () { + return new XMLHttpRequest(); + }.toSource() + ")()", subsb2); + + // Exported function should be able to be call from the + // target sandbox. Native arguments should be just wrapped + // every other argument should be cloned. + Cu.evalInSandbox("(" + function () { + native = new XMLHttpRequest(); + xrayed2 = XPCNativeWrapper(new XMLHttpRequest()); + mixed = { xrayed: xrayed, xrayed2: xrayed2 }; + tobecloned = { cloned: "cloned" }; + invokedCallback = false; + callback = function() { invokedCallback = true; }; + imported(this, 42, tobecloned, native, mixed, callback); + do_check_true(invokedCallback); + }.toSource() + ")()", subsb); + + // Invoking an exported function with cross-origin arguments should throw. + subsb.xoNative = Cu.evalInSandbox('new XMLHttpRequest()', xorigsb); + try { + Cu.evalInSandbox('imported(this, xoNative)', subsb); + do_check_true(false); + } catch (e) { + do_check_true(/denied|insecure/.test(e)); + } + + // Callers can opt-out of the above. + subsb.xoNative = Cu.evalInSandbox('new XMLHttpRequest()', xorigsb); + try { + do_check_eq(Cu.evalInSandbox('echoAllowXO(xoNative)', subsb), subsb.xoNative); + do_check_true(true); + } catch (e) { + do_check_true(false); + } + + // Apply should work and |this| should carry over appropriately. + Cu.evalInSandbox("(" + function() { + var someThis = {}; + imported.apply(someThis, [someThis, 42, tobecloned, native, mixed]); + }.toSource() + ")()", subsb); + + Cu.evalInSandbox("(" + function() { + checkIfCalled(); + }.toSource() + ")()", epsb); + + // Exporting should throw if principal of the source sandbox does + // not subsume the principal of the target. + Cu.evalInSandbox("(" + function() { + try{ + exportFunction(function() {}, this.xorigsb, { defineAs: "denied" }); + do_check_true(false); + } catch (e) { + do_check_true(e.toString().indexOf('Permission denied') > -1); + } + }.toSource() + ")()", epsb); + + // Exporting should throw if the principal of the source sandbox does + // not subsume the principal of the function. + epsb.xo_function = new xorigsb.Function(); + Cu.evalInSandbox("(" + function() { + try{ + exportFunction(xo_function, this.subsb, { defineAs: "denied" }); + do_check_true(false); + } catch (e) { + dump('Exception: ' + e); + do_check_true(e.toString().indexOf('Permission denied') > -1); + } + }.toSource() + ")()", epsb); + + // Let's create an object in the target scope and add privileged + // function to it as a property. + Cu.evalInSandbox("(" + function() { + var newContentObject = createObjectIn(subsb, { defineAs: "importedObject" }); + exportFunction(funToExport, newContentObject, { defineAs: "privMethod" }); + }.toSource() + ")()", epsb); + + Cu.evalInSandbox("(" + function () { + importedObject.privMethod(importedObject, 42, tobecloned, native, mixed); + }.toSource() + ")()", subsb); + + Cu.evalInSandbox("(" + function() { + checkIfCalled(); + }.toSource() + ")()", epsb); + + // exportFunction and createObjectIn should be available from Cu too. + var newContentObject = Cu.createObjectIn(subsb, { defineAs: "importedObject2" }); + var wasCalled = false; + Cu.exportFunction(function(arg) { wasCalled = arg.wasCalled; }, + newContentObject, { defineAs: "privMethod" }); + + Cu.evalInSandbox("(" + function () { + importedObject2.privMethod({wasCalled: true}); + }.toSource() + ")()", subsb); + + // 3rd argument of exportFunction should be optional. + Cu.evalInSandbox("(" + function() { + subsb.imported2 = exportFunction(funToExport, subsb); + }.toSource() + ")()", epsb); + + Cu.evalInSandbox("(" + function () { + imported2(this, 42, tobecloned, native, mixed); + }.toSource() + ")()", subsb); + + Cu.evalInSandbox("(" + function() { + checkIfCalled(); + }.toSource() + ")()", epsb); + + do_check_true(wasCalled, true); +} diff --git a/js/xpconnect/tests/unit/test_file.js b/js/xpconnect/tests/unit/test_file.js new file mode 100644 index 000000000..a06a4208d --- /dev/null +++ b/js/xpconnect/tests/unit/test_file.js @@ -0,0 +1,15 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function run_test() { + do_load_manifest("component-file.manifest"); + const contractID = "@mozilla.org/tests/component-file;1"; + do_check_true(contractID in Components.classes); + var foo = Components.classes[contractID] + .createInstance(Components.interfaces.nsIClassInfo); + do_check_true(Boolean(foo)); + do_check_true(foo.contractID == contractID); + do_check_true(!!foo.wrappedJSObject); + do_check_true(foo.wrappedJSObject.doTest()); +} diff --git a/js/xpconnect/tests/unit/test_file2.js b/js/xpconnect/tests/unit/test_file2.js new file mode 100644 index 000000000..6ebe7b822 --- /dev/null +++ b/js/xpconnect/tests/unit/test_file2.js @@ -0,0 +1,62 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +Components.utils.importGlobalProperties(['File']); + +const Ci = Components.interfaces; + +function run_test() { + // throw if anything goes wrong + + // find the current directory path + var file = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIProperties) + .get("CurWorkD", Ci.nsIFile); + file.append("xpcshell.ini"); + + // should be able to construct a file + var f1 = File.createFromFileName(file.path); + // and with nsIFiles + var f2 = File.createFromNsIFile(file); + + // do some tests + do_check_true(f1 instanceof File, "Should be a DOM File"); + do_check_true(f2 instanceof File, "Should be a DOM File"); + + do_check_true(f1.name == "xpcshell.ini", "Should be the right file"); + do_check_true(f2.name == "xpcshell.ini", "Should be the right file"); + + do_check_true(f1.type == "", "Should be the right type"); + do_check_true(f2.type == "", "Should be the right type"); + + var threw = false; + try { + // Needs a ctor argument + var f7 = File(); + } catch (e) { + threw = true; + } + do_check_true(threw, "No ctor arguments should throw"); + + var threw = false; + try { + // Needs a valid ctor argument + var f7 = File(Date(132131532)); + } catch (e) { + threw = true; + } + do_check_true(threw, "Passing a random object should fail"); + + var threw = false + try { + // Directories fail + var dir = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIProperties) + .get("CurWorkD", Ci.nsIFile); + var f7 = File.createFromNsIFile(dir) + } catch (e) { + threw = true; + } + do_check_true(threw, "Can't create a File object for a directory"); +} diff --git a/js/xpconnect/tests/unit/test_fileReader.js b/js/xpconnect/tests/unit/test_fileReader.js new file mode 100644 index 000000000..cd4859301 --- /dev/null +++ b/js/xpconnect/tests/unit/test_fileReader.js @@ -0,0 +1,13 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +function run_test() { + var Cu = Components.utils; + var sb = new Cu.Sandbox('http://www.example.com', + { wantGlobalProperties: ["FileReader"] }); + sb.do_check_true = do_check_true; + Cu.evalInSandbox('do_check_true((new FileReader()) instanceof FileReader);', + sb); + Cu.importGlobalProperties(["FileReader"]); + do_check_true((new FileReader()) instanceof FileReader); +} diff --git a/js/xpconnect/tests/unit/test_getObjectPrincipal.js b/js/xpconnect/tests/unit/test_getObjectPrincipal.js new file mode 100644 index 000000000..509c9a5d4 --- /dev/null +++ b/js/xpconnect/tests/unit/test_getObjectPrincipal.js @@ -0,0 +1,11 @@ +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +function run_test() { + var secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager); + do_check_true(secMan.isSystemPrincipal(Cu.getObjectPrincipal({}))); + var sb = new Cu.Sandbox('http://www.example.com'); + Cu.evalInSandbox('var obj = { foo: 42 };', sb); + do_check_eq(Cu.getObjectPrincipal(sb.obj).origin, 'http://www.example.com'); +} diff --git a/js/xpconnect/tests/unit/test_import.js b/js/xpconnect/tests/unit/test_import.js new file mode 100644 index 000000000..5475c9011 --- /dev/null +++ b/js/xpconnect/tests/unit/test_import.js @@ -0,0 +1,91 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function run_test() { + var scope = {}; + Components.utils.import("resource://gre/modules/XPCOMUtils.jsm", scope); + do_check_eq(typeof(scope.XPCOMUtils), "object"); + do_check_eq(typeof(scope.XPCOMUtils.generateNSGetFactory), "function"); + + // access module's global object directly without importing any + // symbols + var module = Components.utils.import("resource://gre/modules/XPCOMUtils.jsm", + null); + do_check_eq(typeof(XPCOMUtils), "undefined"); + do_check_eq(typeof(module), "object"); + do_check_eq(typeof(module.XPCOMUtils), "object"); + do_check_eq(typeof(module.XPCOMUtils.generateNSGetFactory), "function"); + do_check_true(scope.XPCOMUtils == module.XPCOMUtils); + + // import symbols to our global object + do_check_eq(typeof(Components.utils.import), "function"); + Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + do_check_eq(typeof(XPCOMUtils), "object"); + do_check_eq(typeof(XPCOMUtils.generateNSGetFactory), "function"); + + // try on a new object + var scope2 = {}; + Components.utils.import("resource://gre/modules/XPCOMUtils.jsm", scope2); + do_check_eq(typeof(scope2.XPCOMUtils), "object"); + do_check_eq(typeof(scope2.XPCOMUtils.generateNSGetFactory), "function"); + + do_check_true(scope2.XPCOMUtils == scope.XPCOMUtils); + + // try on a new object using the resolved URL + var res = Components.classes["@mozilla.org/network/protocol;1?name=resource"] + .getService(Components.interfaces.nsIResProtocolHandler); + var resURI = res.newURI("resource://gre/modules/XPCOMUtils.jsm", null, null); + dump("resURI: " + resURI + "\n"); + var filePath = res.resolveURI(resURI); + var scope3 = {}; + Components.utils.import(filePath, scope3); + do_check_eq(typeof(scope3.XPCOMUtils), "object"); + do_check_eq(typeof(scope3.XPCOMUtils.generateNSGetFactory), "function"); + + do_check_true(scope3.XPCOMUtils == scope.XPCOMUtils); + + // make sure we throw when the second arg is bogus + var didThrow = false; + try { + Components.utils.import("resource://gre/modules/XPCOMUtils.jsm", "wrong"); + } catch (ex) { + print("exception (expected): " + ex); + didThrow = true; + } + do_check_true(didThrow); + + // try to create a component + do_load_manifest("component_import.manifest"); + const contractID = "@mozilla.org/tests/module-importer;"; + do_check_true((contractID + "1") in Components.classes); + var foo = Components.classes[contractID + "1"] + .createInstance(Components.interfaces.nsIClassInfo); + do_check_true(Boolean(foo)); + do_check_true(foo.contractID == contractID + "1"); + // XXX the following check succeeds only if the test component wasn't + // already registered. Need to figure out a way to force registration + // (to manually force it, delete compreg.dat before running the test) + // do_check_true(foo.wrappedJSObject.postRegisterCalled); + + // Call getInterfaces to test line numbers in JS components. But as long as + // we're doing that, why not test what it returns too? + // Kind of odd that this is not returning an array containing the + // number... Or for that matter not returning an array containing an object? + var interfaces = foo.getInterfaces({}); + do_check_eq(interfaces, Components.interfaces.nsIClassInfo.number); + + // try to create a component by CID + const cid = "{6b933fe6-6eba-4622-ac86-e4f654f1b474}"; + do_check_true(cid in Components.classesByID); + foo = Components.classesByID[cid] + .createInstance(Components.interfaces.nsIClassInfo); + do_check_true(foo.contractID == contractID + "1"); + + // try to create another component which doesn't directly implement QI + do_check_true((contractID + "2") in Components.classes); + var bar = Components.classes[contractID + "2"] + .createInstance(Components.interfaces.nsIClassInfo); + do_check_true(Boolean(bar)); + do_check_true(bar.contractID == contractID + "2"); +} diff --git a/js/xpconnect/tests/unit/test_import_fail.js b/js/xpconnect/tests/unit/test_import_fail.js new file mode 100644 index 000000000..d297fa01d --- /dev/null +++ b/js/xpconnect/tests/unit/test_import_fail.js @@ -0,0 +1,10 @@ +function run_test() +{ + try { + Components.utils.import("resource://test/importer.jsm"); + do_check_true(false, "import should not succeed."); + } catch (x) { + do_check_neq(x.fileName.indexOf("syntax_error.jsm"), -1); + do_check_eq(x.lineNumber, 1); + } +}
\ No newline at end of file diff --git a/js/xpconnect/tests/unit/test_interposition.js b/js/xpconnect/tests/unit/test_interposition.js new file mode 100644 index 000000000..f755bbd7d --- /dev/null +++ b/js/xpconnect/tests/unit/test_interposition.js @@ -0,0 +1,165 @@ +const Cu = Components.utils; +const Ci = Components.interfaces; +const Cc = Components.classes; + +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +const ADDONID = "bogus-addon@mozilla.org"; + +let gExpectedProp; +function expectAccess(prop, f) +{ + gExpectedProp = prop; + f(); + do_check_eq(gExpectedProp, undefined); +} + +let getter_run = false; +function test_getter() +{ + do_check_eq(getter_run, false); + getter_run = true; + return 200; +} + +let setter_run = false; +function test_setter(v) +{ + do_check_eq(setter_run, false); + do_check_eq(v, 300); + setter_run = true; +} + +let TestInterposition = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonInterposition, + Ci.nsISupportsWeakReference]), + + getWhitelist: function() { + return ["abcxyz", "utils", "dataprop", "getterprop", "setterprop", + "objprop", "defineprop", "configurableprop"]; + }, + + interposeProperty: function(addonId, target, iid, prop) { + do_check_eq(addonId, ADDONID); + do_check_eq(gExpectedProp, prop); + gExpectedProp = undefined; + + if (prop == "dataprop") { + return { configurable: false, enumerable: true, writable: false, value: 100 }; + } else if (prop == "getterprop") { + return { configurable: false, enumerable: true, get: test_getter }; + } else if (prop == "setterprop") { + return { configurable: false, enumerable: true, get: test_getter, set: test_setter }; + } else if (prop == "utils") { + do_check_eq(iid, Ci.nsIXPCComponents.number); + return null; + } else if (prop == "objprop") { + gExpectedProp = "objprop"; // allow recursive access here + return { configurable: false, enumerable: true, writable: false, value: { objprop: 1 } }; + } else if (prop == "configurableprop") { + return { configurable: true, enumerable: true, writable: false, value: 10 }; + } + + return null; + }, + + interposeCall: function(addonId, originalFunc, originalThis, args) { + do_check_eq(addonId, ADDONID); + args.splice(0, 0, addonId); + return originalFunc.apply(originalThis, args); + } +}; + +function run_test() +{ + Cu.setAddonInterposition(ADDONID, TestInterposition); + + Cu.importGlobalProperties(["XMLHttpRequest"]); + + let sandbox = Cu.Sandbox(this, {addonId: ADDONID}); + sandbox.outerObj = new XMLHttpRequest(); + + expectAccess("abcxyz", () => { + Cu.evalInSandbox("outerObj.abcxyz = 12;", sandbox); + }); + + expectAccess("utils", () => { + Cu.evalInSandbox("Components.utils;", sandbox); + }); + + expectAccess("dataprop", () => { + do_check_eq(Cu.evalInSandbox("outerObj.dataprop;", sandbox), 100); + }); + + expectAccess("dataprop", () => { + try { + Cu.evalInSandbox("'use strict'; outerObj.dataprop = 400;", sandbox); + do_check_true(false); // it should throw + } catch (e) {} + }); + + expectAccess("objprop", () => { + Cu.evalInSandbox("outerObj.objprop.objprop;", sandbox); + gExpectedProp = undefined; + }); + + expectAccess("getterprop", () => { + do_check_eq(Cu.evalInSandbox("outerObj.getterprop;", sandbox), 200); + do_check_eq(getter_run, true); + getter_run = false; + }); + + expectAccess("getterprop", () => { + try { + Cu.evalInSandbox("'use strict'; outerObj.getterprop = 400;", sandbox); + do_check_true(false); // it should throw + } catch (e) {} + do_check_eq(getter_run, false); + }); + + expectAccess("setterprop", () => { + do_check_eq(Cu.evalInSandbox("outerObj.setterprop;", sandbox), 200); + do_check_eq(getter_run, true); + getter_run = false; + do_check_eq(setter_run, false); + }); + + expectAccess("setterprop", () => { + Cu.evalInSandbox("'use strict'; outerObj.setterprop = 300;", sandbox); + do_check_eq(getter_run, false); + do_check_eq(setter_run, true); + setter_run = false; + }); + + // Make sure that calling Object.defineProperty succeeds as long as + // we're not interposing on that property. + expectAccess("defineprop", () => { + Cu.evalInSandbox("'use strict'; Object.defineProperty(outerObj, 'defineprop', {configurable:true, enumerable:true, writable:true, value:10});", sandbox); + }); + + // Related to above: make sure we can delete those properties too. + expectAccess("defineprop", () => { + Cu.evalInSandbox("'use strict'; delete outerObj.defineprop;", sandbox); + }); + + // Make sure we get configurable=false even if the interposition + // sets it to true. + expectAccess("configurableprop", () => { + let desc = Cu.evalInSandbox("Object.getOwnPropertyDescriptor(outerObj, 'configurableprop')", sandbox); + do_check_eq(desc.configurable, false); + }); + + let moduleScope = Cu.Sandbox(this); + moduleScope.ADDONID = ADDONID; + moduleScope.do_check_eq = do_check_eq; + function funToIntercept(addonId) { + do_check_eq(addonId, ADDONID); + counter++; + } + sandbox.moduleFunction = Cu.evalInSandbox(funToIntercept.toSource() + "; funToIntercept", moduleScope); + Cu.evalInSandbox("var counter = 0;", moduleScope); + Cu.evalInSandbox("Components.utils.setAddonCallInterposition(this);", moduleScope); + Cu.evalInSandbox("moduleFunction()", sandbox); + do_check_eq(Cu.evalInSandbox("counter", moduleScope), 1); + Cu.setAddonInterposition(ADDONID, null); +} diff --git a/js/xpconnect/tests/unit/test_isModuleLoaded.js b/js/xpconnect/tests/unit/test_isModuleLoaded.js new file mode 100644 index 000000000..8b1f9eb3d --- /dev/null +++ b/js/xpconnect/tests/unit/test_isModuleLoaded.js @@ -0,0 +1,33 @@ +const Cu = Components.utils; + +function run_test() { + // Existing module. + do_check_true(!Cu.isModuleLoaded("resource://gre/modules/ISO8601DateUtils.jsm"), + "isModuleLoaded returned correct value for non-loaded module"); + Cu.import("resource://gre/modules/ISO8601DateUtils.jsm"); + do_check_true(Cu.isModuleLoaded("resource://gre/modules/ISO8601DateUtils.jsm"), + "isModuleLoaded returned true after loading that module"); + Cu.unload("resource://gre/modules/ISO8601DateUtils.jsm"); + do_check_true(!Cu.isModuleLoaded("resource://gre/modules/ISO8601DateUtils.jsm"), + "isModuleLoaded returned false after unloading that module"); + + // Non-existing module + do_check_true(!Cu.isModuleLoaded("resource://gre/modules/ISO8601DateUtils1.jsm"), + "isModuleLoaded returned correct value for non-loaded module"); + try { + Cu.import("resource://gre/modules/ISO8601DateUtils1.jsm"); + do_check_true(false, + "Should have thrown while trying to load a non existing file"); + } catch (ex) {} + do_check_true(!Cu.isModuleLoaded("resource://gre/modules/ISO8601DateUtils1.jsm"), + "isModuleLoaded returned correct value for non-loaded module"); + + // incorrect url + try { + Cu.isModuleLoaded("resource://modules/ISO8601DateUtils1.jsm"); + do_check_true(false, + "Should have thrown while trying to load a non existing file"); + } catch (ex) { + do_check_true(true, "isModuleLoaded threw an exception while loading incorrect uri"); + } +} diff --git a/js/xpconnect/tests/unit/test_isProxy.js b/js/xpconnect/tests/unit/test_isProxy.js new file mode 100644 index 000000000..7cc526226 --- /dev/null +++ b/js/xpconnect/tests/unit/test_isProxy.js @@ -0,0 +1,29 @@ +function run_test() { + var Cu = Components.utils; + + var handler = { + get: function(target, name){ + return name in target? + target[name] : + 37; + } + }; + + var p = new Proxy({}, handler); + do_check_true(Cu.isProxy(p)); + do_check_false(Cu.isProxy({})); + do_check_false(Cu.isProxy(42)); + + sb = new Cu.Sandbox(this, + { wantExportHelpers: true }); + + do_check_false(Cu.isProxy(sb)); + + sb.do_check_true = do_check_true; + sb.do_check_false = do_check_false; + sb.p = p; + Cu.evalInSandbox('do_check_true(isProxy(p));' + + 'do_check_false(isProxy({}));' + + 'do_check_false(isProxy(42));', + sb); +} diff --git a/js/xpconnect/tests/unit/test_js_weak_references.js b/js/xpconnect/tests/unit/test_js_weak_references.js new file mode 100644 index 000000000..837fdb38b --- /dev/null +++ b/js/xpconnect/tests/unit/test_js_weak_references.js @@ -0,0 +1,45 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* See https://bugzilla.mozilla.org/show_bug.cgi?id=317304 */ + +function run_test() +{ + // Bug 712649: Calling getWeakReference(null) should work. + try { + var nullWeak = Components.utils.getWeakReference(null); + do_check_true(nullWeak.get() === null); + } catch (e) { + do_check_true(false); + } + + var obj = { num: 5, str: 'foo' }; + var weak = Components.utils.getWeakReference(obj); + + do_check_true(weak.get() === obj); + do_check_true(weak.get().num == 5); + do_check_true(weak.get().str == 'foo'); + + // Force garbage collection + Components.utils.forceGC(); + + // obj still references the object, so it should still be accessible via weak + do_check_true(weak.get() === obj); + do_check_true(weak.get().num == 5); + do_check_true(weak.get().str == 'foo'); + + // Clear obj's reference to the object and force garbage collection. To make + // sure that there are no instances of obj stored in the registers or on the + // native stack and the conservative GC would not find it we force the same + // code paths that we used for the initial allocation. + obj = { num: 6, str: 'foo2' }; + var weak2 = Components.utils.getWeakReference(obj); + do_check_true(weak2.get() === obj); + + Components.utils.forceGC(); + + // The object should have been garbage collected and so should no longer be + // accessible via weak + do_check_true(weak.get() === null); +} diff --git a/js/xpconnect/tests/unit/test_localeCompare.js b/js/xpconnect/tests/unit/test_localeCompare.js new file mode 100644 index 000000000..08560d1ae --- /dev/null +++ b/js/xpconnect/tests/unit/test_localeCompare.js @@ -0,0 +1,6 @@ +function run_test() { + do_check_true("C".localeCompare("D") < 0); + do_check_true("D".localeCompare("C") > 0); + do_check_true("\u010C".localeCompare("D") < 0); + do_check_true("D".localeCompare("\u010C") > 0); +} diff --git a/js/xpconnect/tests/unit/test_nuke_sandbox.js b/js/xpconnect/tests/unit/test_nuke_sandbox.js new file mode 100644 index 000000000..a4dd25498 --- /dev/null +++ b/js/xpconnect/tests/unit/test_nuke_sandbox.js @@ -0,0 +1,29 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* See https://bugzilla.mozilla.org/show_bug.cgi?id=769273 */ + +function run_test() +{ + var sb = Components.utils.Sandbox("http://www.blah.com"); + sb.prop = "prop" + var refToObjFromSb = Components.utils.evalInSandbox("var a = {prop2:'prop2'}; a", sb); + Components.utils.nukeSandbox(sb); + do_check_true(Components.utils.isDeadWrapper(sb), "sb should be dead"); + try{ + sb.prop; + do_check_true(false); + } catch (e) { + do_check_true(e.toString().indexOf("can't access dead object") > -1); + } + + Components.utils.isDeadWrapper(refToObjFromSb, "ref to object from sb should be dead"); + try{ + refToObjFromSb.prop2; + do_check_true(false); + } catch (e) { + do_check_true(e.toString().indexOf("can't access dead object") > -1); + } + +} diff --git a/js/xpconnect/tests/unit/test_onGarbageCollection-01.js b/js/xpconnect/tests/unit/test_onGarbageCollection-01.js new file mode 100644 index 000000000..138f699f9 --- /dev/null +++ b/js/xpconnect/tests/unit/test_onGarbageCollection-01.js @@ -0,0 +1,64 @@ +// Test basic usage of onGarbageCollection + +const root = newGlobal(); +const dbg = new Debugger(); +const wrappedRoot = dbg.addDebuggee(root) + +const NUM_SLICES = root.NUM_SLICES = 10; + +let fired = false; +let slicesFound = 0; + +dbg.memory.onGarbageCollection = data => { + fired = true; + + print("Got onGarbageCollection: " + JSON.stringify(data, null, 2)); + + equal(typeof data.reason, "string"); + equal(typeof data.nonincrementalReason == "string" || data.nonincrementalReason === null, + true); + + let lastStartTimestamp = 0; + for (let i = 0; i < data.collections.length; i++) { + let slice = data.collections[i]; + + equal(slice.startTimestamp >= lastStartTimestamp, true); + equal(slice.startTimestamp <= slice.endTimestamp, true); + + lastStartTimestamp = slice.startTimestamp; + } + + equal(data.collections.length >= 1, true); + slicesFound += data.collections.length; +} + +function run_test() { + do_test_pending(); + + root.eval( + ` + this.allocs = []; + + // GC slices + for (var i = 0; i < NUM_SLICES; i++) { + this.allocs.push({}); + gcslice(); + } + + // Full GC + this.allocs.push({}); + gc(); + ` + ); + + executeSoon(() => { + equal(fired, true, "The GC hook should have fired at least once"); + + // NUM_SLICES + 1 full gc + however many were triggered naturally (due to + // whatever zealousness setting). + print("Found " + slicesFound + " slices"); + equal(slicesFound >= NUM_SLICES + 1, true); + + do_test_finished(); + }); +} diff --git a/js/xpconnect/tests/unit/test_onGarbageCollection-02.js b/js/xpconnect/tests/unit/test_onGarbageCollection-02.js new file mode 100644 index 000000000..a8bf22c67 --- /dev/null +++ b/js/xpconnect/tests/unit/test_onGarbageCollection-02.js @@ -0,0 +1,94 @@ +// Test multiple debuggers, GCs, and zones interacting with each other. +// +// Note: when observing both globals, but GC'ing in only one, we don't test that +// we *didn't* GC in the other zone because GCs are finicky and unreliable. That +// used to work when this was a jit-test, but in the process of migrating to +// xpcshell, we lost some amount of reliability and determinism. + +const root1 = newGlobal(); +const dbg1 = new Debugger(); +dbg1.addDebuggee(root1) + +const root2 = newGlobal(); +const dbg2 = new Debugger(); +dbg2.addDebuggee(root2) + +let fired1 = false; +let fired2 = false; +dbg1.memory.onGarbageCollection = _ => fired1 = true; +dbg2.memory.onGarbageCollection = _ => fired2 = true; + +function reset() { + fired1 = false; + fired2 = false; +} + +function run_test() { + do_test_pending(); + + gc(); + executeSoon(() => { + reset(); + + // GC 1 only + root1.eval(`gc(this)`); + executeSoon(() => { + equal(fired1, true); + + // GC 2 only + reset(); + root2.eval(`gc(this)`); + executeSoon(() => { + equal(fired2, true); + + // Full GC + reset(); + gc(); + executeSoon(() => { + equal(fired1, true); + equal(fired2, true); + + // Full GC with no debuggees + reset(); + dbg1.removeAllDebuggees(); + dbg2.removeAllDebuggees(); + gc(); + executeSoon(() => { + equal(fired1, false); + equal(fired2, false); + + // One debugger with multiple debuggees in different zones. + + dbg1.addDebuggee(root1); + dbg1.addDebuggee(root2); + + // Just debuggee 1 + reset(); + root1.eval(`gc(this)`); + executeSoon(() => { + equal(fired1, true); + equal(fired2, false); + + // Just debuggee 2 + reset(); + root2.eval(`gc(this)`); + executeSoon(() => { + equal(fired1, true); + equal(fired2, false); + + // All debuggees + reset(); + gc(); + executeSoon(() => { + equal(fired1, true); + equal(fired2, false); + do_test_finished(); + }); + }); + }); + }); + }); + }); + }); + }); +} diff --git a/js/xpconnect/tests/unit/test_onGarbageCollection-03.js b/js/xpconnect/tests/unit/test_onGarbageCollection-03.js new file mode 100644 index 000000000..1c7f158a7 --- /dev/null +++ b/js/xpconnect/tests/unit/test_onGarbageCollection-03.js @@ -0,0 +1,35 @@ +// Test that the onGarbageCollection hook is not reentrant. + + +function run_test() { + do_test_pending(); + + const root = newGlobal(); + const dbg = new Debugger(); + const wrappedRoot = dbg.addDebuggee(root) + + let fired = true; + let depth = 0; + + dbg.memory.onGarbageCollection = _ => { + fired = true; + + equal(depth, 0); + depth++; + try { + root.eval(`gc()`); + } finally { + equal(depth, 1); + depth--; + } + } + + root.eval(`gc()`); + + executeSoon(() => { + ok(fired); + equal(depth, 0); + dbg.memory.onGarbageCollection = undefined; + do_test_finished(); + }); +} diff --git a/js/xpconnect/tests/unit/test_onGarbageCollection-04.js b/js/xpconnect/tests/unit/test_onGarbageCollection-04.js new file mode 100644 index 000000000..a7c71b82a --- /dev/null +++ b/js/xpconnect/tests/unit/test_onGarbageCollection-04.js @@ -0,0 +1,67 @@ +// Test that the onGarbageCollection reentrancy guard is on a per Debugger +// basis. That is if our first Debugger is observing our second Debugger's +// compartment, and this second Debugger triggers a GC inside its +// onGarbageCollection hook, the first Debugger's onGarbageCollection hook is +// still called. +// +// This is the scenario we are setting up: top level debugging the `debuggeree` +// global, which is debugging the `debuggee` global. Then, we trigger the +// following events: +// +// debuggee gc +// | +// V +// debuggeree's onGarbageCollection +// | +// V +// debuggeree gc +// | +// V +// top level onGarbageCollection +// +// Note that the top level's onGarbageCollection hook should be fired, at the +// same time that we are preventing reentrancy into debuggeree's +// onGarbageCollection hook. + +function run_test() { + do_test_pending(); + + const debuggeree = newGlobal(); + const debuggee = debuggeree.debuggee = newGlobal(); + + debuggeree.eval( + ` + var dbg = new Debugger(this.debuggee); + var fired = 0; + dbg.memory.onGarbageCollection = _ => { + fired++; + gc(this); + }; + ` + ); + + const dbg = new Debugger(debuggeree); + let fired = 0; + dbg.memory.onGarbageCollection = _ => { + fired++; + }; + + debuggee.eval(`gc(this)`); + + // Let first onGarbageCollection runnable get run. + executeSoon(() => { + + // Let second onGarbageCollection runnable get run. + executeSoon(() => { + + // Even though we request GC'ing a single zone, we can't rely on that + // behavior and both zones could have been scheduled for gc for both + // gc(this) calls. + ok(debuggeree.fired >= 1); + ok(fired >= 1); + + debuggeree.dbg.enabled = dbg.enabled = false; + do_test_finished(); + }); + }); +} diff --git a/js/xpconnect/tests/unit/test_onGarbageCollection-05.js b/js/xpconnect/tests/unit/test_onGarbageCollection-05.js new file mode 100644 index 000000000..a34859ceb --- /dev/null +++ b/js/xpconnect/tests/unit/test_onGarbageCollection-05.js @@ -0,0 +1,37 @@ +// Test that the onGarbageCollection hook reports its gc cycle's number (aka the +// major GC number) and that it is monotonically increasing. + +const root = newGlobal(); +const dbg = new Debugger(); +const wrappedRoot = dbg.addDebuggee(root) + +function run_test() { + do_test_pending(); + + let numFired = 0; + let lastGCCycleNumber = undefined; + + (function loop() { + if (numFired == 10) { + dbg.memory.onGarbageCollection = undefined; + dbg.enabled = false; + return void do_test_finished(); + } + + dbg.memory.onGarbageCollection = data => { + print("onGarbageCollection: " + uneval(data)); + + if (numFired != 0) { + equal(typeof lastGCCycleNumber, "number"); + equal(data.gcCycleNumber - lastGCCycleNumber, 1); + } + + numFired++; + lastGCCycleNumber = data.gcCycleNumber; + + executeSoon(loop); + }; + + root.eval("gc(this)"); + }()); +} diff --git a/js/xpconnect/tests/unit/test_params.js b/js/xpconnect/tests/unit/test_params.js new file mode 100644 index 000000000..dfe4b822a --- /dev/null +++ b/js/xpconnect/tests/unit/test_params.js @@ -0,0 +1,201 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const Cc = Components.classes; +const Ci = Components.interfaces; + +function run_test() { + + // Load the component manifests. + registerAppManifest(do_get_file('../components/native/chrome.manifest')); + registerAppManifest(do_get_file('../components/js/xpctest.manifest')); + + // Test for each component. + test_component("@mozilla.org/js/xpc/test/native/Params;1"); + test_component("@mozilla.org/js/xpc/test/js/Params;1"); +} + +function test_component(contractid) { + + // Instantiate the object. + var o = Cc[contractid].createInstance(Ci["nsIXPCTestParams"]); + + // Possible comparator functions. + var standardComparator = function(a,b) {return a == b;}; + var dotEqualsComparator = function(a,b) {return a.equals(b); } + var fuzzComparator = function(a,b) {return Math.abs(a - b) < 0.1;}; + var interfaceComparator = function(a,b) {return a.name == b.name; } + var arrayComparator = function(innerComparator) { + return function(a,b) { + if (a.length != b.length) + return false; + for (var i = 0; i < a.length; ++i) + if (!innerComparator(a[i], b[i])) + return false; + return true; + }; + }; + + // Helper test function - takes the name of test method and two values of + // the given type. + // + // The optional comparator argument can be used for alternative notions of + // equality. The comparator should return true on equality. + function doTest(name, val1, val2, comparator) { + if (!comparator) + comparator = standardComparator; + var a = val1; + var b = {value: val2}; + var rv = o[name].call(o, a, b); + do_check_true(comparator(rv, val2)); + do_check_true(comparator(val1, b.value)); + }; + + function doIsTest(name, val1, val1Is, val2, val2Is, valComparator, isComparator) { + if (!isComparator) + isComparator = standardComparator; + var a = val1; + var aIs = val1Is; + var b = {value: val2}; + var bIs = {value: val2Is}; + var rvIs = {}; + var rv = o[name].call(o, aIs, a, bIs, b, rvIs); + do_check_true(valComparator(rv, val2)); + do_check_true(isComparator(rvIs.value, val2Is)); + do_check_true(valComparator(val1, b.value)); + do_check_true(isComparator(val1Is, bIs.value)); + } + + // Special-purpose function for testing arrays of iid_is interfaces, where we + // have 2 distinct sets of dependent parameters. + function doIs2Test(name, val1, val1Size, val1IID, val2, val2Size, val2IID) { + var a = val1; + var aSize = val1Size; + var aIID = val1IID; + var b = {value: val2}; + var bSize = {value: val2Size}; + var bIID = {value: val2IID}; + var rvSize = {}; + var rvIID = {}; + var rv = o[name].call(o, aSize, aIID, a, bSize, bIID, b, rvSize, rvIID); + do_check_true(arrayComparator(interfaceComparator)(rv, val2)); + do_check_true(standardComparator(rvSize.value, val2Size)); + do_check_true(dotEqualsComparator(rvIID.value, val2IID)); + do_check_true(arrayComparator(interfaceComparator)(val1, b.value)); + do_check_true(standardComparator(val1Size, bSize.value)); + do_check_true(dotEqualsComparator(val1IID, bIID.value)); + } + + // Check that the given call (type mismatch) results in an exception being thrown. + function doTypedArrayMismatchTest(name, val1, val1Size, val2, val2Size) { + var comparator = arrayComparator(standardComparator); + var error = false; + try { + doIsTest(name, val1, val1Size, val2, val2Size, comparator); + + // An exception was not thrown as would have been expected. + do_check_true(false); + } + catch (e) { + // An exception was thrown as expected. + do_check_true(true); + } + } + + // Workaround for bug 687612 (inout parameters broken for dipper types). + // We do a simple test of copying a into b, and ignore the rv. + function doTestWorkaround(name, val1) { + var a = val1; + var b = {value: ""}; + o[name].call(o, a, b); + do_check_eq(val1, b.value); + } + + // Test all the different types + doTest("testBoolean", true, false); + doTest("testOctet", 4, 156); + doTest("testShort", -456, 1299); + doTest("testLong", 50060, -12121212); + doTest("testLongLong", 12345, -10000000000); + doTest("testUnsignedShort", 1532, 65000); + doTest("testUnsignedLong", 0, 4000000000); + doTest("testUnsignedLongLong", 215435, 3453492580348535809); + doTest("testFloat", 4.9, -11.2, fuzzComparator); + doTest("testDouble", -80.5, 15000.2, fuzzComparator); + doTest("testChar", "a", "2"); + doTest("testString", "someString", "another string"); + doTest("testWstring", "Why wasnt this", "turned on before? ಠ_ಠ"); + doTest("testWchar", "z", "ア"); + doTestWorkaround("testDOMString", "Beware: ☠ s"); + doTestWorkaround("testAString", "Frosty the ☃ ;-)"); + doTestWorkaround("testAUTF8String", "We deliver 〠!"); + doTestWorkaround("testACString", "Just a regular C string."); + doTest("testJsval", {aprop: 12, bprop: "str"}, 4.22); + + // Test out dipper parameters, since they're special and we can't really test + // inouts. + let outAString = {}; + o.testOutAString(outAString); + do_check_eq(outAString.value, "out"); + try { o.testOutAString(undefined); } catch (e) {} // Don't crash + try { o.testOutAString(null); } catch (e) {} // Don't crash + try { o.testOutAString("string"); } catch (e) {} // Don't crash + + // Helpers to instantiate various test XPCOM objects. + var numAsMade = 0; + function makeA() { + var a = Cc["@mozilla.org/js/xpc/test/js/InterfaceA;1"].createInstance(Ci['nsIXPCTestInterfaceA']); + a.name = 'testA' + numAsMade++; + return a; + }; + var numBsMade = 0; + function makeB() { + var b = Cc["@mozilla.org/js/xpc/test/js/InterfaceB;1"].createInstance(Ci['nsIXPCTestInterfaceB']); + b.name = 'testB' + numBsMade++; + return b; + }; + + // Test arrays. + doIsTest("testShortArray", [2, 4, 6], 3, [1, 3, 5, 7], 4, arrayComparator(standardComparator)); + doIsTest("testDoubleArray", [-10, -0.5], 2, [1, 3, 1e11, -8e-5 ], 4, arrayComparator(fuzzComparator)); + + doIsTest("testStringArray", ["mary", "hat", "hey", "lid", "tell", "lam"], 6, + ["ids", "fleas", "woes", "wide", "has", "know", "!"], 7, arrayComparator(standardComparator)); + doIsTest("testWstringArray", ["沒有語言", "的偉大嗎?]"], 2, + ["we", "are", "being", "sooo", "international", "right", "now"], 7, arrayComparator(standardComparator)); + doIsTest("testInterfaceArray", [makeA(), makeA()], 2, + [makeA(), makeA(), makeA(), makeA(), makeA(), makeA()], 6, arrayComparator(interfaceComparator)); + + // Test typed arrays and ArrayBuffer aliasing. + var arrayBuffer = new ArrayBuffer(16); + var int16Array = new Int16Array(arrayBuffer, 2, 3); + int16Array.set([-32768, 0, 32767]); + doIsTest("testShortArray", int16Array, 3, new Int16Array([1773, -32768, 32767, 7]), 4, arrayComparator(standardComparator)); + doIsTest("testDoubleArray", new Float64Array([-10, -0.5]), 2, new Float64Array([0, 3.2, 1.0e10, -8.33 ]), 4, arrayComparator(fuzzComparator)); + + // Test sized strings. + var ssTests = ["Tis not possible, I muttered", "give me back my free hardcore!", "quoth the server:", "4〠4"]; + doIsTest("testSizedString", ssTests[0], ssTests[0].length, ssTests[1], ssTests[1].length, standardComparator); + doIsTest("testSizedWstring", ssTests[2], ssTests[2].length, ssTests[3], ssTests[3].length, standardComparator); + + // Test iid_is. + doIsTest("testInterfaceIs", makeA(), Ci['nsIXPCTestInterfaceA'], + makeB(), Ci['nsIXPCTestInterfaceB'], + interfaceComparator, dotEqualsComparator); + + // Test arrays of iids. + doIs2Test("testInterfaceIsArray", [makeA(), makeA(), makeA(), makeA(), makeA()], 5, Ci['nsIXPCTestInterfaceA'], + [makeB(), makeB(), makeB()], 3, Ci['nsIXPCTestInterfaceB']); + + // Test optional array size. + do_check_eq(o.testStringArrayOptionalSize(["some", "string", "array"]), "somestringarray"); + + // Test incorrect (too big) array size parameter; this should throw NOT_ENOUGH_ELEMENTS. + doTypedArrayMismatchTest("testShortArray", new Int16Array([-3, 7, 4]), 4, + new Int16Array([1, -32, 6]), 3); + + // Test type mismatch (int16 <-> uint16); this should throw BAD_CONVERT_JS. + doTypedArrayMismatchTest("testShortArray", new Uint16Array([0, 7, 4, 3]), 4, + new Uint16Array([1, 5, 6]), 3); +} diff --git a/js/xpconnect/tests/unit/test_promise.js b/js/xpconnect/tests/unit/test_promise.js new file mode 100644 index 000000000..46c34a9bc --- /dev/null +++ b/js/xpconnect/tests/unit/test_promise.js @@ -0,0 +1,8 @@ +function run_test() { + var Cu = Components.utils; + sb = new Cu.Sandbox('http://www.example.com'); + sb.do_check_eq = do_check_eq; + Cu.evalInSandbox('do_check_eq(typeof new Promise(function(resolve){resolve();}), "object");', + sb); + do_check_eq(typeof new Promise(function(resolve){resolve();}), "object"); +} diff --git a/js/xpconnect/tests/unit/test_recursive_import.js b/js/xpconnect/tests/unit/test_recursive_import.js new file mode 100644 index 000000000..4145ca49f --- /dev/null +++ b/js/xpconnect/tests/unit/test_recursive_import.js @@ -0,0 +1,17 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function run_test() { + var scope = {}; + Components.utils.import("resource://test/recursive_importA.jsm", scope); + + // A imported correctly + do_check_true(scope.foo() == "foo"); + + // Symbols from B are visible through A + do_check_true(scope.bar.baz() == "baz"); + + // Symbols from A are visible through A, B, A. + do_check_true(scope.bar.qux.foo() == "foo"); +} diff --git a/js/xpconnect/tests/unit/test_reflect_parse.js b/js/xpconnect/tests/unit/test_reflect_parse.js new file mode 100644 index 000000000..48d84d91c --- /dev/null +++ b/js/xpconnect/tests/unit/test_reflect_parse.js @@ -0,0 +1,27 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +({ + loc:{start:{line:1, column:0}, end:{line:1, column:12}, source:null}, + type:"Program", + body:[ + { + loc:{start:{line:1, column:0}, end:{line:1, column:12}, source:null}, + type:"ExpressionStatement", + expression:{ + loc:{start:{line:1, column:0}, end:{line:1, column:12}, source:null}, + type:"Literal", + value:"use strict" + } + } + ] +}) +*/ + +function run_test() { + // Reflect.parse is better tested in js shell; this basically tests its presence. + var parseData = Reflect.parse('"use strict"'); + do_check_eq(parseData.body[0].expression.value, "use strict"); +} diff --git a/js/xpconnect/tests/unit/test_resolve_dead_promise.js b/js/xpconnect/tests/unit/test_resolve_dead_promise.js new file mode 100644 index 000000000..818ab5b58 --- /dev/null +++ b/js/xpconnect/tests/unit/test_resolve_dead_promise.js @@ -0,0 +1,39 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* See https://bugzilla.mozilla.org/show_bug.cgi?id=1298597 */ + +function run_test() +{ + var sb = Components.utils.Sandbox("http://www.blah.com"); + var resolveFun; + var p1 = new sb.Promise((res, rej) => {resolveFun = res}); + var rejectFun; + var p2 = new sb.Promise((res, rej) => {rejectFun = rej}); + Components.utils.nukeSandbox(sb); + do_check_true(Components.utils.isDeadWrapper(sb), "sb should be dead"); + do_check_true(Components.utils.isDeadWrapper(p1), "p1 should be dead"); + do_check_true(Components.utils.isDeadWrapper(p2), "p2 should be dead"); + + var exception; + + try{ + resolveFun(1); + do_check_true(false); + } catch (e) { + exception = e; + } + do_check_true(exception.toString().includes("can't access dead object"), + "Resolving dead wrapped promise should throw"); + + exception = undefined; + try{ + rejectFun(1); + do_check_true(false); + } catch (e) { + exception = e; + } + do_check_true(exception.toString().includes("can't access dead object"), + "Rejecting dead wrapped promise should throw"); +} diff --git a/js/xpconnect/tests/unit/test_returncode.js b/js/xpconnect/tests/unit/test_returncode.js new file mode 100644 index 000000000..8ba76c976 --- /dev/null +++ b/js/xpconnect/tests/unit/test_returncode.js @@ -0,0 +1,78 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const {interfaces: Ci, classes: Cc, utils: Cu, manager: Cm, results: Cr} = Components; + +Cu.import("resource:///modules/XPCOMUtils.jsm"); + +function getConsoleMessages() { + let consoleService = Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService); + let messages = consoleService.getMessageArray().map((m) => m.toString()); + // reset ready for the next call. + consoleService.reset(); + return messages; +} + +function run_test() { + // Load the component manifests. + registerAppManifest(do_get_file('../components/native/chrome.manifest')); + registerAppManifest(do_get_file('../components/js/xpctest.manifest')); + + // and the tests. + test_simple(); + test_nested(); +} + +function test_simple() { + let parent = Cc["@mozilla.org/js/xpc/test/native/ReturnCodeParent;1"] + .createInstance(Ci.nsIXPCTestReturnCodeParent); + let result; + + // flush existing messages before we start testing. + getConsoleMessages(); + + // Ask the C++ to call the JS object which will throw. + result = parent.callChild(Ci.nsIXPCTestReturnCodeChild.CHILD_SHOULD_THROW); + Assert.equal(result, Cr.NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS, + "exception caused NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS"); + + let messages = getConsoleMessages(); + Assert.equal(messages.length, 1, "got a console message from the exception"); + Assert.ok(messages[0].indexOf("a requested error") != -1, "got the message text"); + + // Ask the C++ to call the JS object which will return success. + result = parent.callChild(Ci.nsIXPCTestReturnCodeChild.CHILD_SHOULD_RETURN_SUCCESS); + Assert.equal(result, Cr.NS_OK, "success is success"); + + Assert.deepEqual(getConsoleMessages(), [], "no messages reported on success."); + + // And finally the point of this test! + // Ask the C++ to call the JS object which will use .returnCode + result = parent.callChild(Ci.nsIXPCTestReturnCodeChild.CHILD_SHOULD_RETURN_RESULTCODE); + Assert.equal(result, Cr.NS_ERROR_FAILURE, + "NS_ERROR_FAILURE was seen as the error code."); + + Assert.deepEqual(getConsoleMessages(), [], "no messages reported with .returnCode"); +} + +function test_nested() { + let parent = Cc["@mozilla.org/js/xpc/test/native/ReturnCodeParent;1"] + .createInstance(Ci.nsIXPCTestReturnCodeParent); + let result; + + // flush existing messages before we start testing. + getConsoleMessages(); + + // Ask the C++ to call the "outer" JS object, which will set .returnCode, but + // then create and call *another* component which itself sets the .returnCode + // to a different value. This checks the returnCode is correctly saved + // across call contexts. + result = parent.callChild(Ci.nsIXPCTestReturnCodeChild.CHILD_SHOULD_NEST_RESULTCODES); + Assert.equal(result, Cr.NS_ERROR_UNEXPECTED, + "NS_ERROR_UNEXPECTED was seen as the error code."); + // We expect one message, which is the child reporting what it got as the + // return code - which should be NS_ERROR_FAILURE + let expected = ["nested child returned " + Cr.NS_ERROR_FAILURE]; + Assert.deepEqual(getConsoleMessages(), expected, "got the correct sub-error"); +} diff --git a/js/xpconnect/tests/unit/test_rtcIdentityProvider.js b/js/xpconnect/tests/unit/test_rtcIdentityProvider.js new file mode 100644 index 000000000..d6918d3b6 --- /dev/null +++ b/js/xpconnect/tests/unit/test_rtcIdentityProvider.js @@ -0,0 +1,35 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +function run_test() { + let Cu = Components.utils; + let sb = new Cu.Sandbox('https://www.example.com', + { wantGlobalProperties: ['rtcIdentityProvider'] }); + + function exerciseInterface() { + equal(typeof rtcIdentityProvider, 'object'); + equal(typeof rtcIdentityProvider.register, 'function'); + rtcIdentityProvider.register({ + generateAssertion: function(a, b, c) { + return Promise.resolve({ + idp: { domain: 'example.com' }, + assertion: JSON.stringify([a, b, c]) + }); + }, + validateAssertion: function(d, e) { + return Promise.resolve({ + identity: 'user@example.com', + contents: JSON.stringify([d, e]) + }); + } + }); + } + + sb.equal = equal; + Cu.evalInSandbox('(' + exerciseInterface.toSource() + ')();', sb); + ok(sb.rtcIdentityProvider.hasIdp); + + Cu.importGlobalProperties(['rtcIdentityProvider']); + exerciseInterface(); + ok(rtcIdentityProvider.hasIdp); +} diff --git a/js/xpconnect/tests/unit/test_sandbox_atob.js b/js/xpconnect/tests/unit/test_sandbox_atob.js new file mode 100644 index 000000000..0c7586fe2 --- /dev/null +++ b/js/xpconnect/tests/unit/test_sandbox_atob.js @@ -0,0 +1,10 @@ +function run_test() { + var Cu = Components.utils; + sb = new Cu.Sandbox('http://www.example.com', + { wantGlobalProperties: ["atob", "btoa"] }); + sb.do_check_eq = do_check_eq; + Cu.evalInSandbox('var dummy = "Dummy test.";' + + 'do_check_eq(dummy, atob(btoa(dummy)));' + + 'do_check_eq(btoa("budapest"), "YnVkYXBlc3Q=");', + sb); +} diff --git a/js/xpconnect/tests/unit/test_sandbox_metadata.js b/js/xpconnect/tests/unit/test_sandbox_metadata.js new file mode 100644 index 000000000..c3db2c1b2 --- /dev/null +++ b/js/xpconnect/tests/unit/test_sandbox_metadata.js @@ -0,0 +1,58 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* See https://bugzilla.mozilla.org/show_bug.cgi?id=898559 */ + +function run_test() +{ + let sandbox = Components.utils.Sandbox("http://www.blah.com", { + metadata: "test metadata", + addonId: "12345" + }); + + do_check_eq(Components.utils.getSandboxMetadata(sandbox), "test metadata"); + do_check_eq(Components.utils.getSandboxAddonId(sandbox), "12345"); + + sandbox = Components.utils.Sandbox("http://www.blah.com", { + metadata: { foopy: { bar: 2 }, baz: "hi" } + }); + + let metadata = Components.utils.getSandboxMetadata(sandbox); + do_check_eq(metadata.baz, "hi"); + do_check_eq(metadata.foopy.bar, 2); + metadata.baz = "foo"; + + metadata = Components.utils.getSandboxMetadata(sandbox); + do_check_eq(metadata.baz, "foo"); + + metadata = { foo: "bar" }; + Components.utils.setSandboxMetadata(sandbox, metadata); + metadata.foo = "baz"; + metadata = Components.utils.getSandboxMetadata(sandbox); + do_check_eq(metadata.foo, "bar"); + + let thrown = false; + let reflector = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] + .createInstance(Components.interfaces.nsIXMLHttpRequest); + + try { + Components.utils.setSandboxMetadata(sandbox, { foo: reflector }); + } catch(e) { + thrown = true; + } + + do_check_eq(thrown, true); + + sandbox = Components.utils.Sandbox(this, { + metadata: { foopy: { bar: 2 }, baz: "hi" } + }); + + let inner = Components.utils.evalInSandbox("Components.utils.Sandbox('http://www.blah.com')", sandbox); + + metadata = Components.utils.getSandboxMetadata(inner); + do_check_eq(metadata.baz, "hi"); + do_check_eq(metadata.foopy.bar, 2); + metadata.baz = "foo"; +} + diff --git a/js/xpconnect/tests/unit/test_sandbox_name.js b/js/xpconnect/tests/unit/test_sandbox_name.js new file mode 100644 index 000000000..44e6bbcd4 --- /dev/null +++ b/js/xpconnect/tests/unit/test_sandbox_name.js @@ -0,0 +1,28 @@ +"use strict"; + +const { utils: Cu, interfaces: Ci, classes: Cc } = Components; + +/** + * Test that the name of a sandbox contains the name of all principals. + */ +function test_sandbox_name() { + let names = [ + "http://example.com/?" + Math.random(), + "http://example.org/?" + Math.random() + ]; + let sandbox = Cu.Sandbox(names); + let fileName = Cu.evalInSandbox( + "(new Error()).fileName", + sandbox, + "latest" /*js version*/, + ""/*file name*/ + ); + + for (let name of names) { + Assert.ok(fileName.indexOf(name) != -1, `Name ${name} appears in ${fileName}`); + } +}; + +function run_test() { + test_sandbox_name(); +} diff --git a/js/xpconnect/tests/unit/test_tearoffs.js b/js/xpconnect/tests/unit/test_tearoffs.js new file mode 100644 index 000000000..02f0a6329 --- /dev/null +++ b/js/xpconnect/tests/unit/test_tearoffs.js @@ -0,0 +1,108 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const Cc = Components.classes; +const Ci = Components.interfaces; + +function run_test() { + + // Load the component manifest containing our test interface implementations. + Components.manager.autoRegister(do_get_file('../components/js/xpctest.manifest')); + + // Shortcut the interfaces we're using. + var ifs = { + a: Ci['nsIXPCTestInterfaceA'], + b: Ci['nsIXPCTestInterfaceB'], + c: Ci['nsIXPCTestInterfaceC'] + }; + + // Shortcut the class we're instantiating. This implements all three interfaces. + var cls = Cc["@mozilla.org/js/xpc/test/js/TestInterfaceAll;1"]; + + // Run through the logic a few times. + for (i = 0; i < 2; ++i) + play_with_tearoffs(ifs, cls); +} + +function play_with_tearoffs(ifs, cls) { + + // Allocate a bunch of objects, QI-ed to B. + var instances = []; + for (var i = 0; i < 300; ++i) + instances.push(cls.createInstance(ifs.b)); + + // Nothing to collect. + gc(); + + // QI them to A. + instances.forEach(function(v, i, a) { v.QueryInterface(ifs.a); }); + + // QI them to C. + instances.forEach(function(v, i, a) { v.QueryInterface(ifs.c); }); + + // Check + do_check_true('name' in instances[10], 'Have the prop from A/B'); + do_check_true('someInteger' in instances[10], 'Have the prop from C'); + + // Grab tearoff reflections for a and b. + var aTearOffs = instances.map(function(v, i, a) { return v.nsIXPCTestInterfaceA; } ); + var bTearOffs = instances.map(function(v, i, a) { return v.nsIXPCTestInterfaceB; } ); + + // Check + do_check_true('name' in aTearOffs[1], 'Have the prop from A'); + do_check_true(!('someInteger' in aTearOffs[1]), 'Dont have the prop from C'); + + // Nothing to collect. + gc(); + + // Null out every even instance pointer. + for (var i = 0; i < instances.length; ++i) + if (i % 2 == 0) + instances[i] = null; + + // Nothing to collect, since we still have the A and B tearoff reflections. + gc(); + + // Null out A tearoff reflections that are a multiple of 3. + for (var i = 0; i < aTearOffs.length; ++i) + if (i % 3 == 0) + aTearOffs[i] = null; + + // Nothing to collect, since we still have the B tearoff reflections. + gc(); + + // Null out B tearoff reflections that are a multiple of 5. + for (var i = 0; i < bTearOffs.length; ++i) + if (i % 5 == 0) + bTearOffs[i] = null; + + // This should collect every 30th object (indices that are multiples of 2, 3, and 5). + gc(); + + // Kill the b tearoffs entirely. + bTearOffs = 0; + + // Collect more. + gc(); + + // Get C tearoffs. + var cTearOffs = instances.map(function(v, i, a) { return v ? v.nsIXPCTestInterfaceC : null; } ); + + // Check. + do_check_true(!('name' in cTearOffs[1]), 'Dont have the prop from A'); + do_check_true('someInteger' in cTearOffs[1], 'have the prop from C'); + + // Null out the a tearoffs. + aTearOffs = null; + + // Collect all even indices. + gc(); + + // Collect all indices. + instances = null; + gc(); + + // Give ourselves a pat on the back. :-) + do_check_true(true, "Got all the way through without crashing!"); +} diff --git a/js/xpconnect/tests/unit/test_textDecoder.js b/js/xpconnect/tests/unit/test_textDecoder.js new file mode 100644 index 000000000..0b31750a4 --- /dev/null +++ b/js/xpconnect/tests/unit/test_textDecoder.js @@ -0,0 +1,12 @@ +function run_test() { + var Cu = Components.utils; + sb = new Cu.Sandbox('http://www.example.com', + { wantGlobalProperties: ["TextDecoder", "TextEncoder"] }); + sb.do_check_eq = do_check_eq; + Cu.evalInSandbox('do_check_eq(new TextDecoder().encoding, "utf-8");' + + 'do_check_eq(new TextEncoder().encoding, "utf-8");', + sb); + Cu.importGlobalProperties(["TextDecoder", "TextEncoder"]); + do_check_eq(new TextDecoder().encoding, "utf-8"); + do_check_eq(new TextEncoder().encoding, "utf-8"); +} diff --git a/js/xpconnect/tests/unit/test_unload.js b/js/xpconnect/tests/unit/test_unload.js new file mode 100644 index 000000000..e3ca8ce88 --- /dev/null +++ b/js/xpconnect/tests/unit/test_unload.js @@ -0,0 +1,28 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function run_test() { + var scope1 = {}; + var global1 = Components.utils.import("resource://gre/modules/NetUtil.jsm", scope1); + + var scope2 = {}; + var global2 = Components.utils.import("resource://gre/modules/NetUtil.jsm", scope2); + + do_check_true(global1 === global2); + do_check_true(scope1.NetUtil === scope2.NetUtil); + + Components.utils.unload("resource://gre/modules/NetUtil.jsm"); + + var scope3 = {}; + var global3 = Components.utils.import("resource://gre/modules/NetUtil.jsm", scope3); + + do_check_false(global1 === global3); + do_check_false(scope1.NetUtil === scope3.NetUtil); + + // When the jsm was unloaded, the value of all its global's properties were + // set to undefined. While it must be safe (not crash) to call into the + // module, we expect it to throw an error (e.g., when trying to use Ci). + try { scope1.NetUtil.newURI("http://www.example.com"); } catch (e) {} + try { scope3.NetUtil.newURI("http://www.example.com"); } catch (e) {} +} diff --git a/js/xpconnect/tests/unit/test_url.js b/js/xpconnect/tests/unit/test_url.js new file mode 100644 index 000000000..da2502800 --- /dev/null +++ b/js/xpconnect/tests/unit/test_url.js @@ -0,0 +1,10 @@ +function run_test() { + var Cu = Components.utils; + var sb = new Cu.Sandbox('http://www.example.com', + { wantGlobalProperties: ["URL"] }); + sb.do_check_eq = do_check_eq; + Cu.evalInSandbox('do_check_eq(new URL("http://www.example.com").host, "www.example.com");', + sb); + Cu.importGlobalProperties(["URL"]); + do_check_eq(new URL("http://www.example.com").host, "www.example.com"); +} diff --git a/js/xpconnect/tests/unit/test_want_components.js b/js/xpconnect/tests/unit/test_want_components.js new file mode 100644 index 000000000..5ccebcf9c --- /dev/null +++ b/js/xpconnect/tests/unit/test_want_components.js @@ -0,0 +1,8 @@ +function run_test() { + var cu = Components.utils; + var sb = cu.Sandbox(this, + {wantComponents: false}); + + var rv = cu.evalInSandbox("this.Components", sb); + do_check_eq(rv, undefined); +} diff --git a/js/xpconnect/tests/unit/test_watchdog_default.js b/js/xpconnect/tests/unit/test_watchdog_default.js new file mode 100644 index 000000000..d5dac7b6a --- /dev/null +++ b/js/xpconnect/tests/unit/test_watchdog_default.js @@ -0,0 +1,12 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function testBody() { + // Check that we properly implement whatever behavior is specified by the + // default profile for this configuration. + checkWatchdog(isWatchdogEnabled(), continueTest); + yield; + do_test_finished(); + yield; +} diff --git a/js/xpconnect/tests/unit/test_watchdog_disable.js b/js/xpconnect/tests/unit/test_watchdog_disable.js new file mode 100644 index 000000000..dcdf21ce4 --- /dev/null +++ b/js/xpconnect/tests/unit/test_watchdog_disable.js @@ -0,0 +1,11 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function testBody() { + setWatchdogEnabled(false); + checkWatchdog(false, continueTest); + yield; + do_test_finished(); + yield; +} diff --git a/js/xpconnect/tests/unit/test_watchdog_enable.js b/js/xpconnect/tests/unit/test_watchdog_enable.js new file mode 100644 index 000000000..8e31e1316 --- /dev/null +++ b/js/xpconnect/tests/unit/test_watchdog_enable.js @@ -0,0 +1,11 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function testBody() { + setWatchdogEnabled(true); + checkWatchdog(true, continueTest); + yield; + do_test_finished(); + yield; +} diff --git a/js/xpconnect/tests/unit/test_watchdog_hibernate.js b/js/xpconnect/tests/unit/test_watchdog_hibernate.js new file mode 100644 index 000000000..ce3642963 --- /dev/null +++ b/js/xpconnect/tests/unit/test_watchdog_hibernate.js @@ -0,0 +1,53 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function testBody() { + + setWatchdogEnabled(true); + + // It's unlikely that we've ever hibernated at this point, but the timestamps + // default to 0, so this should always be true. + var now = Date.now() * 1000; + var startHibernation = Cu.getWatchdogTimestamp("WatchdogHibernateStart"); + var stopHibernation = Cu.getWatchdogTimestamp("WatchdogHibernateStop"); + do_log_info("Pre-hibernation statistics:"); + do_log_info("now: " + now / 1000000); + do_log_info("startHibernation: " + startHibernation / 1000000); + do_log_info("stopHibernation: " + stopHibernation / 1000000); + do_check_true(startHibernation < now); + do_check_true(stopHibernation < now); + + // When the watchdog runs, it hibernates if there's been no activity for the + // last 2 seconds, otherwise it sleeps for 1 second. As such, given perfect + // scheduling, we should never have more than 3 seconds of inactivity without + // hibernating. To add some padding for automation, we mandate that hibernation + // must begin between 2 and 5 seconds from now. + var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + timer.initWithCallback(continueTest, 10000, Ci.nsITimer.TYPE_ONE_SHOT); + simulateActivityCallback(false); + yield; + + simulateActivityCallback(true); + busyWait(1000); // Give the watchdog time to wake up on the condvar. + var stateChange = Cu.getWatchdogTimestamp("ContextStateChange"); + startHibernation = Cu.getWatchdogTimestamp("WatchdogHibernateStart"); + stopHibernation = Cu.getWatchdogTimestamp("WatchdogHibernateStop"); + do_log_info("Post-hibernation statistics:"); + do_log_info("stateChange: " + stateChange / 1000000); + do_log_info("startHibernation: " + startHibernation / 1000000); + do_log_info("stopHibernation: " + stopHibernation / 1000000); + // XPCOM timers, JS times, and PR_Now() are apparently not directly + // comparable, as evidenced by certain seemingly-impossible timing values + // that occasionally get logged in windows automation. We're really just + // making sure this behavior is roughly as expected on the macro scale, + // so we add a 1 second fuzz factor here. + const FUZZ_FACTOR = 1 * 1000 * 1000; + do_check_true(stateChange > now + 10*1000*1000 - FUZZ_FACTOR); + do_check_true(startHibernation > now + 2*1000*1000 - FUZZ_FACTOR); + do_check_true(startHibernation < now + 5*1000*1000 + FUZZ_FACTOR); + do_check_true(stopHibernation > now + 10*1000*1000 - FUZZ_FACTOR); + + do_test_finished(); + yield; +} diff --git a/js/xpconnect/tests/unit/test_watchdog_toggle.js b/js/xpconnect/tests/unit/test_watchdog_toggle.js new file mode 100644 index 000000000..f0d14c2df --- /dev/null +++ b/js/xpconnect/tests/unit/test_watchdog_toggle.js @@ -0,0 +1,13 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +function testBody() { + var defaultBehavior = isWatchdogEnabled(); + setWatchdogEnabled(!defaultBehavior); + setWatchdogEnabled(defaultBehavior); + checkWatchdog(defaultBehavior, continueTest); + yield; + do_test_finished(); + yield; +} diff --git a/js/xpconnect/tests/unit/test_weak_keys.js b/js/xpconnect/tests/unit/test_weak_keys.js new file mode 100644 index 000000000..e70983e9c --- /dev/null +++ b/js/xpconnect/tests/unit/test_weak_keys.js @@ -0,0 +1,45 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* See https://bugzilla.mozilla.org/show_bug.cgi?id=1165807 */ + +function run_test() +{ + var bunnies = new String("bunnies"); + var lizards = new String("lizards"); + + var weakset = new WeakSet([bunnies, lizards]); + var weakmap = new WeakMap(); + weakmap.set(bunnies, 23); + weakmap.set(lizards, "oh no"); + + var keys = ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(bunnies); + equal(keys, undefined, "test nondeterministicGetWeakMapKeys on non-WeakMap"); + + keys = ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(weakmap); + equal(keys.length, 2, "length of nondeterministicGetWeakMapKeys"); + equal(weakmap.get(bunnies), 23, "check bunnies in weakmap"); + equal(weakmap.get(lizards), "oh no", "check lizards in weakmap"); + + keys = ThreadSafeChromeUtils.nondeterministicGetWeakSetKeys(bunnies); + equal(keys, undefined, "test nondeterministicGetWeakSetKeys on non-WeakMap"); + + keys = ThreadSafeChromeUtils.nondeterministicGetWeakSetKeys(weakset); + equal(keys.length, 2, "length of nondeterministicGetWeakSetKeys"); + ok(weakset.has(bunnies), "check bunnies in weakset"); + ok(weakset.has(lizards), "check lizards in weakset"); + + bunnies = null; + keys = null; + + Components.utils.forceGC(); + + keys = ThreadSafeChromeUtils.nondeterministicGetWeakMapKeys(weakmap); + equal(keys.length, 1, "length of nondeterministicGetWeakMapKeys after GC"); + equal(weakmap.get(lizards), "oh no", "check lizards still in weakmap"); + + keys = ThreadSafeChromeUtils.nondeterministicGetWeakSetKeys(weakset); + equal(keys.length, 1, "length of nondeterministicGetWeakSetKeys after GC"); + ok(weakset.has(lizards), "check lizards still in weakset"); +} diff --git a/js/xpconnect/tests/unit/test_writeToGlobalPrototype.js b/js/xpconnect/tests/unit/test_writeToGlobalPrototype.js new file mode 100644 index 000000000..0062861eb --- /dev/null +++ b/js/xpconnect/tests/unit/test_writeToGlobalPrototype.js @@ -0,0 +1,76 @@ +var G = 3; + +const Cu = Components.utils; + +function run_test() +{ + let s = Cu.Sandbox(this, {sandboxPrototype: this, writeToGlobalPrototype: true}); + + Cu.evalInSandbox("a = 3", s); + Cu.evalInSandbox("var b = 3", s); + Cu.evalInSandbox("const c = 3", s); + Cu.evalInSandbox("this.d = 3", s); + Cu.evalInSandbox("function e() { return 3; }", s); + + do_check_eq(Cu.evalInSandbox("a", s), 3); + do_check_eq(Cu.evalInSandbox("b", s), 3); + do_check_eq(Cu.evalInSandbox("c", s), 3); + do_check_eq(Cu.evalInSandbox("d", s), 3); + do_check_eq(Cu.evalInSandbox("e()", s), 3); + + do_check_eq(a, 3); + do_check_eq(b, 3); + // c is a lexical binding and does not write to the global prototype + do_check_eq(d, 3); + do_check_eq(e(), 3); + + a = 12; + do_check_eq(Cu.evalInSandbox("a", s), 12); + b = 12; + do_check_eq(Cu.evalInSandbox("b", s), 12); + d = 12; + do_check_eq(Cu.evalInSandbox("d", s), 12); + + this.q = 3; + do_check_eq(Cu.evalInSandbox("q", s), 3); + Cu.evalInSandbox("q = 12", s); + do_check_eq(q, 12); + + do_check_eq(Cu.evalInSandbox("G", s), 3); + Cu.evalInSandbox("G = 12", s); + do_check_eq(G, 12); + + Cu.evalInSandbox("Object.defineProperty(this, 'x', {enumerable: false, value: 3})", s); + do_check_eq(Cu.evalInSandbox("x", s), 3); + do_check_eq(x, 3); + for (var p in this) { + do_check_neq(p, "x"); + } + + Cu.evalInSandbox("Object.defineProperty(this, 'y', {get: function() { this.gotten = true; return 3; }})", s); + do_check_eq(y, 3); + do_check_eq(Cu.evalInSandbox("gotten", s), true); + do_check_eq(gotten, true); + + Cu.evalInSandbox("this.gotten = false", s); + do_check_eq(Cu.evalInSandbox("y", s), 3); + do_check_eq(Cu.evalInSandbox("gotten", s), true); + do_check_eq(gotten, true); + + Cu.evalInSandbox("Object.defineProperty(this, 'z', {get: function() { this.gotten = true; return 3; }, set: function(v) { this.setTo = v; }})", s); + z = 12; + do_check_eq(setTo, 12); + do_check_eq(z, 3); + do_check_eq(gotten, true); + do_check_eq(Cu.evalInSandbox("gotten", s), true); + gotten = false; + do_check_eq(Cu.evalInSandbox("gotten", s), false); + + Cu.evalInSandbox("z = 20", s); + do_check_eq(setTo, 20); + do_check_eq(Cu.evalInSandbox("z", s), 3); + do_check_eq(gotten, true); + do_check_eq(Cu.evalInSandbox("gotten", s), true); + gotten = false; + do_check_eq(Cu.evalInSandbox("gotten", s), false); +} diff --git a/js/xpconnect/tests/unit/test_xpcomutils.js b/js/xpconnect/tests/unit/test_xpcomutils.js new file mode 100644 index 000000000..f43454253 --- /dev/null +++ b/js/xpconnect/tests/unit/test_xpcomutils.js @@ -0,0 +1,246 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- + * vim: sw=4 ts=4 sts=4 et + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * This file tests the methods on XPCOMUtils.jsm. + */ + +Components.utils.import("resource://gre/modules/Preferences.jsm"); +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +const Cc = Components.classes; +const Ci = Components.interfaces; + + +//////////////////////////////////////////////////////////////////////////////// +//// Tests + +add_test(function test_generateQI_string_names() +{ + var x = { + QueryInterface: XPCOMUtils.generateQI([ + Components.interfaces.nsIClassInfo, + "nsIDOMNode" + ]) + }; + + try { + x.QueryInterface(Components.interfaces.nsIClassInfo); + } catch(e) { + do_throw("Should QI to nsIClassInfo"); + } + try { + x.QueryInterface(Components.interfaces.nsIDOMNode); + } catch(e) { + do_throw("Should QI to nsIDOMNode"); + } + try { + x.QueryInterface(Components.interfaces.nsIDOMDocument); + do_throw("QI should not have succeeded!"); + } catch(e) {} + run_next_test(); +}); + + +add_test(function test_generateCI() +{ + const classID = Components.ID("562dae2e-7cff-432b-995b-3d4c03fa2b89"); + const classDescription = "generateCI test component"; + const flags = Components.interfaces.nsIClassInfo.DOM_OBJECT; + var x = { + QueryInterface: XPCOMUtils.generateQI([]), + classInfo: XPCOMUtils.generateCI({classID: classID, + interfaces: [], + flags: flags, + classDescription: classDescription}) + }; + + try { + var ci = x.QueryInterface(Components.interfaces.nsIClassInfo); + ci = ci.QueryInterface(Components.interfaces.nsISupports); + ci = ci.QueryInterface(Components.interfaces.nsIClassInfo); + do_check_eq(ci.classID, classID); + do_check_eq(ci.flags, flags); + do_check_eq(ci.classDescription, classDescription); + } catch(e) { + do_throw("Classinfo for x should not be missing or broken"); + } + run_next_test(); +}); + +add_test(function test_defineLazyGetter() +{ + let accessCount = 0; + let obj = { + inScope: false + }; + const TEST_VALUE = "test value"; + XPCOMUtils.defineLazyGetter(obj, "foo", function() { + accessCount++; + this.inScope = true; + return TEST_VALUE; + }); + do_check_eq(accessCount, 0); + + // Get the property, making sure the access count has increased. + do_check_eq(obj.foo, TEST_VALUE); + do_check_eq(accessCount, 1); + do_check_true(obj.inScope); + + // Get the property once more, making sure the access count has not + // increased. + do_check_eq(obj.foo, TEST_VALUE); + do_check_eq(accessCount, 1); + run_next_test(); +}); + + +add_test(function test_defineLazyServiceGetter() +{ + let obj = { }; + XPCOMUtils.defineLazyServiceGetter(obj, "service", + "@mozilla.org/consoleservice;1", + "nsIConsoleService"); + let service = Cc["@mozilla.org/consoleservice;1"]. + getService(Ci.nsIConsoleService); + + // Check that the lazy service getter and the actual service have the same + // properties. + for (let prop in obj.service) + do_check_true(prop in service); + for (let prop in service) + do_check_true(prop in obj.service); + run_next_test(); +}); + + +add_test(function test_defineLazyPreferenceGetter() +{ + const PREF = "xpcomutils.test.pref"; + + let obj = {}; + XPCOMUtils.defineLazyPreferenceGetter(obj, "pref", PREF, "defaultValue"); + + + equal(obj.pref, "defaultValue", "Should return the default value before pref is set"); + + Preferences.set(PREF, "currentValue"); + + + do_print("Create second getter on new object"); + + obj = {}; + XPCOMUtils.defineLazyPreferenceGetter(obj, "pref", PREF, "defaultValue"); + + + equal(obj.pref, "currentValue", "Should return the current value on initial read when pref is already set"); + + Preferences.set(PREF, "newValue"); + + equal(obj.pref, "newValue", "Should return new value after preference change"); + + Preferences.set(PREF, "currentValue"); + + equal(obj.pref, "currentValue", "Should return new value after second preference change"); + + + Preferences.reset(PREF); + + equal(obj.pref, "defaultValue", "Should return default value after pref is reset"); + + run_next_test(); +}); + + +add_test(function test_categoryRegistration() +{ + const CATEGORY_NAME = "test-cat"; + const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1"; + const XULAPPINFO_CID = Components.ID("{fc937916-656b-4fb3-a395-8c63569e27a8}"); + + // Create a fake app entry for our category registration apps filter. + let tmp = {}; + Components.utils.import("resource://testing-common/AppInfo.jsm", tmp); + let XULAppInfo = tmp.newAppInfo({ + name: "catRegTest", + ID: "{adb42a9a-0d19-4849-bf4d-627614ca19be}", + version: "1", + platformVersion: "", + }); + let XULAppInfoFactory = { + createInstance: function (outer, iid) { + if (outer != null) + throw Cr.NS_ERROR_NO_AGGREGATION; + return XULAppInfo.QueryInterface(iid); + } + }; + let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); + registrar.registerFactory( + XULAPPINFO_CID, + "XULAppInfo", + XULAPPINFO_CONTRACTID, + XULAppInfoFactory + ); + + // Load test components. + do_load_manifest("CatRegistrationComponents.manifest"); + + const EXPECTED_ENTRIES = new Map([ + ["CatRegisteredComponent", "@unit.test.com/cat-registered-component;1"], + ["CatAppRegisteredComponent", "@unit.test.com/cat-app-registered-component;1"], + ]); + + // Verify the correct entries are registered in the "test-cat" category. + for (let [name, value] of XPCOMUtils.enumerateCategoryEntries(CATEGORY_NAME)) { + print("Verify that the name/value pair exists in the expected entries."); + ok(EXPECTED_ENTRIES.has(name)); + do_check_eq(EXPECTED_ENTRIES.get(name), value); + EXPECTED_ENTRIES.delete(name); + } + print("Check that all of the expected entries have been deleted."); + do_check_eq(EXPECTED_ENTRIES.size, 0); + run_next_test(); +}); + +add_test(function test_generateSingletonFactory() +{ + const XPCCOMPONENT_CONTRACTID = "@mozilla.org/singletonComponentTest;1"; + const XPCCOMPONENT_CID = Components.ID("{31031c36-5e29-4dd9-9045-333a5d719a3e}"); + + function XPCComponent() {} + XPCComponent.prototype = { + classID: XPCCOMPONENT_CID, + _xpcom_factory: XPCOMUtils.generateSingletonFactory(XPCComponent), + QueryInterface: XPCOMUtils.generateQI([]) + }; + let NSGetFactory = XPCOMUtils.generateNSGetFactory([XPCComponent]); + let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); + registrar.registerFactory( + XPCCOMPONENT_CID, + "XPCComponent", + XPCCOMPONENT_CONTRACTID, + NSGetFactory(XPCCOMPONENT_CID) + ); + + // First, try to instance the component. + let instance = Cc[XPCCOMPONENT_CONTRACTID].createInstance(Ci.nsISupports); + // Try again, check that it returns the same instance as before. + do_check_eq(instance, + Cc[XPCCOMPONENT_CONTRACTID].createInstance(Ci.nsISupports)); + // Now, for sanity, check that getService is also returning the same instance. + do_check_eq(instance, + Cc[XPCCOMPONENT_CONTRACTID].getService(Ci.nsISupports)); + + run_next_test(); +}); + +//////////////////////////////////////////////////////////////////////////////// +//// Test Runner + +function run_test() +{ + run_next_test(); +} diff --git a/js/xpconnect/tests/unit/test_xpcwn_tamperproof.js b/js/xpconnect/tests/unit/test_xpcwn_tamperproof.js new file mode 100644 index 000000000..bf7b65927 --- /dev/null +++ b/js/xpconnect/tests/unit/test_xpcwn_tamperproof.js @@ -0,0 +1,171 @@ +// Test that it's not possible to create expando properties on XPCWNs. +// See <https://bugzilla.mozilla.org/show_bug.cgi?id=1143810#c5>. + +const Cc = Components.classes; +const Ci = Components.interfaces; + +function check_throws(f) { + try { + f(); + } catch (exc) { + return; + } + throw new TypeError("Expected exception, no exception thrown"); +} + +/* + * Test that XPCWrappedNative objects do not permit expando properties to be created. + * + * This function is called twice. The first time, realObj is an nsITimer XPCWN + * and accessObj === realObj. + * + * The second time, accessObj is a scripted proxy with realObj as its target. + * So the second time we are testing that scripted proxies don't magically + * bypass whatever mechanism enforces the expando policy on XPCWNs. + */ +function test_tamperproof(realObj, accessObj, {method, constant, attribute}) { + // Assignment can't create an expando property. + check_throws(function () { accessObj.expando = 14; }); + do_check_false("expando" in realObj); + + // Strict assignment throws. + check_throws(function () { "use strict"; accessObj.expando = 14; }); + do_check_false("expando" in realObj); + + // Assignment to an inherited method name doesn't work either. + check_throws(function () { accessObj.hasOwnProperty = () => "lies"; }); + check_throws(function () { "use strict"; accessObj.hasOwnProperty = () => "lies"; }); + do_check_false(realObj.hasOwnProperty("hasOwnProperty")); + + // Assignment to a method name doesn't work either. + let originalMethod; + if (method) { + originalMethod = accessObj[method]; + accessObj[method] = "nope"; // non-writable data property, no exception in non-strict code + check_throws(function () { "use strict"; accessObj[method] = "nope"; }); + do_check_true(realObj[method] === originalMethod); + } + + // A constant is the same thing. + let originalConstantValue; + if (constant) { + originalConstantValue = accessObj[constant]; + accessObj[constant] = "nope"; + do_check_eq(realObj[constant], originalConstantValue); + check_throws(function () { "use strict"; accessObj[constant] = "nope"; }); + do_check_eq(realObj[constant], originalConstantValue); + } + + // Assignment to a readonly accessor property with no setter doesn't work either. + let originalAttributeDesc; + if (attribute) { + originalAttributeDesc = Object.getOwnPropertyDescriptor(realObj, attribute); + do_check_true("set" in originalAttributeDesc); + do_check_true(originalAttributeDesc.set === undefined); + + accessObj[attribute] = "nope"; // accessor property with no setter: no exception in non-strict code + check_throws(function () { "use strict"; accessObj[attribute] = "nope"; }); + + let desc = Object.getOwnPropertyDescriptor(realObj, attribute); + do_check_true("set" in desc); + do_check_eq(originalAttributeDesc.get, desc.get); + do_check_eq(undefined, desc.set); + } + + // Reflect.set doesn't work either. + if (method) { + do_check_false(Reflect.set({}, method, "bad", accessObj)); + do_check_eq(realObj[method], originalMethod); + } + if (attribute) { + do_check_false(Reflect.set({}, attribute, "bad", accessObj)); + do_check_eq(originalAttributeDesc.get, Object.getOwnPropertyDescriptor(realObj, attribute).get); + } + + // Object.defineProperty can't do anything either. + let names = ["expando"]; + if (method) names.push(method); + if (constant) names.push(constant); + if (attribute) names.push(attribute); + for (let name of names) { + let originalDesc = Object.getOwnPropertyDescriptor(realObj, name); + check_throws(function () { + Object.defineProperty(accessObj, name, {configurable: true}); + }); + check_throws(function () { + Object.defineProperty(accessObj, name, {writable: true}); + }); + check_throws(function () { + Object.defineProperty(accessObj, name, {get: function () { return "lies"; }}); + }); + check_throws(function () { + Object.defineProperty(accessObj, name, {value: "bad"}); + }); + let desc = Object.getOwnPropertyDescriptor(realObj, name); + if (originalDesc === undefined) { + do_check_eq(undefined, desc); + } else { + do_check_eq(originalDesc.configurable, desc.configurable); + do_check_eq(originalDesc.enumerable, desc.enumerable); + do_check_eq(originalDesc.writable, desc.writable); + do_check_eq(originalDesc.value, desc.value); + do_check_eq(originalDesc.get, desc.get); + do_check_eq(originalDesc.set, desc.set); + } + } + + // Existing properties can't be deleted. + if (method) { + do_check_false(delete accessObj[method]); + check_throws(function () { "use strict"; delete accessObj[method]; }); + do_check_eq(realObj[method], originalMethod); + } + if (constant) { + do_check_false(delete accessObj[constant]); + check_throws(function () { "use strict"; delete accessObj[constant]; }); + do_check_eq(realObj[constant], originalConstantValue); + } + if (attribute) { + do_check_false(delete accessObj[attribute]); + check_throws(function () { "use strict"; delete accessObj[attribute]; }); + desc = Object.getOwnPropertyDescriptor(realObj, attribute); + do_check_eq(originalAttributeDesc.get, desc.get); + } +} + +function test_twice(obj, options) { + test_tamperproof(obj, obj, options); + + let handler = { + getPrototypeOf(t) { + return new Proxy(Object.getPrototypeOf(t), handler); + } + }; + test_tamperproof(obj, new Proxy(obj, handler), options); +} + +function run_test() { + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + test_twice(timer, { + method: "init", + constant: "TYPE_ONE_SHOT", + attribute: "callback" + }); + + let principal = Cc["@mozilla.org/nullprincipal;1"].createInstance(Ci.nsIPrincipal); + test_twice(principal, {}); + + test_twice(Object.getPrototypeOf(principal), { + method: "subsumes", + constant: "APP_STATUS_INSTALLED", + attribute: "origin" + }); + + // Test a tearoff object. + Components.manager.autoRegister(do_get_file('../components/js/xpctest.manifest')); + let b = Cc["@mozilla.org/js/xpc/test/js/TestInterfaceAll;1"].createInstance(Ci.nsIXPCTestInterfaceB); + let tearoff = b.nsIXPCTestInterfaceA; + test_twice(tearoff, { + method: "QueryInterface" + }); +} diff --git a/js/xpconnect/tests/unit/test_xray_SavedFrame-02.js b/js/xpconnect/tests/unit/test_xray_SavedFrame-02.js new file mode 100644 index 000000000..5c3397670 --- /dev/null +++ b/js/xpconnect/tests/unit/test_xray_SavedFrame-02.js @@ -0,0 +1,75 @@ +// Test calling SavedFrame getters across wrappers from privileged and +// un-privileged globals. + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/jsdebugger.jsm"); +addDebuggerToGlobal(this); + +const lowP = Cc["@mozilla.org/nullprincipal;1"].createInstance(Ci.nsIPrincipal); +const highP = Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal); + +const low = new Cu.Sandbox(lowP); +const high = new Cu.Sandbox(highP); + +function run_test() { + // Privileged compartment accessing unprivileged stack. + high.stack = getSavedFrameInstanceFromSandbox(low); + Cu.evalInSandbox("this.parent = stack.parent", high); + Cu.evalInSandbox("this.asyncParent = stack.asyncParent", high); + Cu.evalInSandbox("this.source = stack.source", high); + Cu.evalInSandbox("this.functionDisplayName = stack.functionDisplayName", high); + + // Un-privileged compartment accessing privileged stack. + low.stack = getSavedFrameInstanceFromSandbox(high); + try { + Cu.evalInSandbox("this.parent = stack.parent", low); + } catch (e) { } + try { + Cu.evalInSandbox("this.asyncParent = stack.asyncParent", low); + } catch (e) { } + try { + Cu.evalInSandbox("this.source = stack.source", low); + } catch (e) { } + try { + Cu.evalInSandbox("this.functionDisplayName = stack.functionDisplayName", low); + } catch (e) { } + + // Privileged compartment accessing privileged stack. + let stack = getSavedFrameInstanceFromSandbox(high); + let parent = stack.parent; + let asyncParent = stack.asyncParent; + let source = stack.source; + let functionDisplayName = stack.functionDisplayName; + + ok(true, "Didn't crash"); +} + +// Get a SavedFrame instance from inside the given sandbox. +// +// We can't use Cu.getJSTestingFunctions().saveStack() because Cu isn't +// available to sandboxes that don't have the system principal. The easiest way +// to get the SavedFrame is to use the Debugger API to track allocation sites +// and then do an allocation. +function getSavedFrameInstanceFromSandbox(sandbox) { + const dbg = new Debugger(sandbox); + + dbg.memory.trackingAllocationSites = true; + Cu.evalInSandbox("(function iife() { return new RegExp }())", sandbox); + const allocs = dbg.memory.drainAllocationsLog().filter(e => e.class === "RegExp"); + dbg.memory.trackingAllocationSites = false; + + ok(allocs[0], "We should observe the allocation"); + const { frame } = allocs[0]; + + if (sandbox !== high) { + ok(Cu.isXrayWrapper(frame), "`frame` should be an xray..."); + equal(Object.prototype.toString.call(Cu.waiveXrays(frame)), + "[object SavedFrame]", + "...and that xray should wrap a SavedFrame"); + } + + return frame; +} diff --git a/js/xpconnect/tests/unit/test_xray_SavedFrame.js b/js/xpconnect/tests/unit/test_xray_SavedFrame.js new file mode 100644 index 000000000..6a931cff0 --- /dev/null +++ b/js/xpconnect/tests/unit/test_xray_SavedFrame.js @@ -0,0 +1,108 @@ +// Bug 1117242: Test calling SavedFrame getters from globals that don't subsume +// that frame's principals. + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/jsdebugger.jsm"); +addDebuggerToGlobal(this); + +const lowP = Cc["@mozilla.org/nullprincipal;1"].createInstance(Ci.nsIPrincipal); +const midP = [lowP, "http://other.com"]; +const highP = Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal); + +const low = new Cu.Sandbox(lowP); +const mid = new Cu.Sandbox(midP); +const high = new Cu.Sandbox(highP); + +function run_test() { + // Test that the priveleged view of a SavedFrame from a subsumed compartment + // is the same view that the subsumed compartment gets. Create the following + // chain of function calls (with some intermediate system-principaled frames + // due to implementation): + // + // low.lowF -> mid.midF -> high.highF -> high.saveStack + // + // Where high.saveStack gets monkey patched to create stacks in each of our + // sandboxes. + + Cu.evalInSandbox("function highF() { return saveStack(); }", high); + + mid.highF = () => high.highF(); + Cu.evalInSandbox("function midF() { return highF(); }", mid); + + low.midF = () => mid.midF(); + Cu.evalInSandbox("function lowF() { return midF(); }", low); + + const expected = [ + { + sandbox: low, + frames: ["lowF"], + }, + { + sandbox: mid, + frames: ["midF", "lowF"], + }, + { + sandbox: high, + frames: ["getSavedFrameInstanceFromSandbox", + "saveStack", + "highF", + "run_test/mid.highF", + "midF", + "run_test/low.midF", + "lowF", + "run_test", + "_execute_test", + null], + } + ]; + + for (let { sandbox, frames } of expected) { + high.saveStack = function saveStack() { + return getSavedFrameInstanceFromSandbox(sandbox); + }; + + const xrayStack = low.lowF(); + equal(xrayStack.functionDisplayName, "getSavedFrameInstanceFromSandbox", + "Xrays should always be able to see everything."); + + let waived = Cu.waiveXrays(xrayStack); + do { + ok(frames.length, + "There should still be more expected frames while we have actual frames."); + equal(waived.functionDisplayName, frames.shift(), + "The waived wrapper should give us the stack's compartment's view."); + waived = waived.parent; + } while (waived); + } +} + +// Get a SavedFrame instance from inside the given sandbox. +// +// We can't use Cu.getJSTestingFunctions().saveStack() because Cu isn't +// available to sandboxes that don't have the system principal. The easiest way +// to get the SavedFrame is to use the Debugger API to track allocation sites +// and then do an allocation. +function getSavedFrameInstanceFromSandbox(sandbox) { + const dbg = new Debugger(sandbox); + + dbg.memory.trackingAllocationSites = true; + Cu.evalInSandbox("new Object", sandbox); + const allocs = dbg.memory.drainAllocationsLog(); + dbg.memory.trackingAllocationSites = false; + + ok(allocs[0], "We should observe the allocation"); + const { frame } = allocs[0]; + + if (sandbox !== high) { + ok(Cu.isXrayWrapper(frame), "`frame` should be an xray..."); + equal(Object.prototype.toString.call(Cu.waiveXrays(frame)), + "[object SavedFrame]", + "...and that xray should wrap a SavedFrame"); + } + + return frame; +} + diff --git a/js/xpconnect/tests/unit/test_xrayed_iterator.js b/js/xpconnect/tests/unit/test_xrayed_iterator.js new file mode 100644 index 000000000..1f82fd890 --- /dev/null +++ b/js/xpconnect/tests/unit/test_xrayed_iterator.js @@ -0,0 +1,36 @@ +const Cu = Components.utils; +function run_test() { + + var toEval = [ + "var customIterator = {", + " _array: [6, 7, 8, 9],", + " __iterator__: function() {", + " for (var i = 0; i < this._array.length; ++i)", + " yield this._array[i];", + " }", + "}" + ].join('\n'); + + function checkIterator(iterator) { + var control = [6, 7, 8, 9]; + var i = 0; + for (var item in iterator) { + do_check_eq(item, control[i]); + ++i; + } + } + + // First, try in our own scope. + eval(toEval); + checkIterator(customIterator); + + // Next, try a vanilla CCW. + var sbChrome = Cu.Sandbox(this); + Cu.evalInSandbox(toEval, sbChrome, '1.7'); + checkIterator(sbChrome.customIterator); + + // Finally, try an Xray waiver. + var sbContent = Cu.Sandbox('http://www.example.com'); + Cu.evalInSandbox(toEval, sbContent, '1.7'); + checkIterator(Cu.waiveXrays(sbContent.customIterator)); +} diff --git a/js/xpconnect/tests/unit/xpcshell.ini b/js/xpconnect/tests/unit/xpcshell.ini new file mode 100644 index 000000000..99d44b975 --- /dev/null +++ b/js/xpconnect/tests/unit/xpcshell.ini @@ -0,0 +1,137 @@ +[DEFAULT] +head = +tail = +support-files = + CatRegistrationComponents.manifest + bogus_element_type.jsm + bogus_exports_type.jsm + bug451678_subscript.js + component-blob.js + component-blob.manifest + component-file.js + component-file.manifest + component_import.js + component_import.manifest + importer.jsm + recursive_importA.jsm + recursive_importB.jsm + subScriptWithEarlyError.js + syntax_error.jsm + +[test_allowWaivers.js] +[test_bogus_files.js] +[test_bug408412.js] +[test_bug451678.js] +[test_bug604362.js] +[test_bug677864.js] +[test_bug711404.js] +[test_bug742444.js] +[test_bug778409.js] +[test_bug780370.js] +[test_bug809652.js] +[test_bug809674.js] +[test_bug813901.js] +[test_bug845201.js] +[test_bug845862.js] +[test_bug849730.js] +[test_bug851895.js] +[test_bug853709.js] +[test_bug854558.js] +[test_bug856067.js] +[test_bug868675.js] +[test_bug867486.js] +[test_bug872772.js] +[test_bug885800.js] +[test_bug930091.js] +[test_bug976151.js] +[test_bug1001094.js] +[test_bug1021312.js] +[test_bug1033253.js] +[test_bug1033920.js] +[test_bug1033927.js] +[test_bug1034262.js] +[test_bug1082450.js] +[test_bug1081990.js] +[test_bug1110546.js] +[test_bug1131707.js] +[test_bug1150106.js] +[test_bug1150771.js] +[test_bug1151385.js] +[test_bug1170311.js] +[test_bug1244222.js] +[test_bug_442086.js] +[test_callFunctionWithAsyncStack.js] +[test_classesByID_instanceof.js] +[test_deepFreezeClone.js] +[test_file.js] +[test_blob.js] +[test_blob2.js] +[test_file2.js] +[test_import.js] +[test_import_fail.js] +[test_interposition.js] +[test_isModuleLoaded.js] +[test_js_weak_references.js] +[test_onGarbageCollection-01.js] +head = head_ongc.js +[test_onGarbageCollection-02.js] +head = head_ongc.js +[test_onGarbageCollection-03.js] +head = head_ongc.js +[test_onGarbageCollection-04.js] +head = head_ongc.js +[test_onGarbageCollection-05.js] +head = head_ongc.js +[test_reflect_parse.js] +[test_localeCompare.js] +# Bug 676965: test fails consistently on Android +fail-if = os == "android" +[test_recursive_import.js] +[test_xpcomutils.js] +[test_unload.js] +[test_attributes.js] +# Bug 676965: test fails consistently on Android +fail-if = os == "android" +[test_params.js] +# Bug 676965: test fails consistently on Android +fail-if = os == "android" +[test_tearoffs.js] +[test_want_components.js] +[test_components.js] +[test_allowedDomains.js] +[test_allowedDomainsXHR.js] +[test_nuke_sandbox.js] +[test_sandbox_metadata.js] +[test_exportFunction.js] +[test_promise.js] +[test_returncode.js] +skip-if = os == "android" # native test components aren't available on Android +[test_textDecoder.js] +[test_url.js] +[test_URLSearchParams.js] +[test_fileReader.js] +[test_crypto.js] +[test_css.js] +[test_rtcIdentityProvider.js] +[test_sandbox_atob.js] +[test_isProxy.js] +[test_getObjectPrincipal.js] +[test_sandbox_name.js] +[test_watchdog_enable.js] +head = head_watchdog.js +[test_watchdog_disable.js] +head = head_watchdog.js +[test_watchdog_toggle.js] +head = head_watchdog.js +[test_watchdog_default.js] +head = head_watchdog.js +[test_watchdog_hibernate.js] +head = head_watchdog.js +[test_weak_keys.js] +[test_writeToGlobalPrototype.js] +[test_xpcwn_tamperproof.js] +[test_xrayed_iterator.js] +[test_xray_SavedFrame.js] +[test_xray_SavedFrame-02.js] +[test_resolve_dead_promise.js] +[test_asyncLoadSubScriptError.js] |