summaryrefslogtreecommitdiffstats
path: root/testing/mochitest/tests/Harness_sanity
diff options
context:
space:
mode:
Diffstat (limited to 'testing/mochitest/tests/Harness_sanity')
-rw-r--r--testing/mochitest/tests/Harness_sanity/.eslintrc.js12
-rw-r--r--testing/mochitest/tests/Harness_sanity/SpecialPowersLoadChromeScript.js17
-rw-r--r--testing/mochitest/tests/Harness_sanity/empty.js0
-rw-r--r--testing/mochitest/tests/Harness_sanity/file_SpecialPowersFrame1.html14
-rw-r--r--testing/mochitest/tests/Harness_sanity/file_spawn.html10
-rw-r--r--testing/mochitest/tests/Harness_sanity/mochitest.toml75
-rw-r--r--testing/mochitest/tests/Harness_sanity/specialPowers_framescript.js13
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_SimpletestGetTestFileURL.html20
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_SpecialPowersExtension.html198
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_SpecialPowersExtension2.html21
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_SpecialPowersLoadChromeScript.html65
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_SpecialPowersLoadChromeScript_function.html62
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_SpecialPowersLoadPrivilegedScript.html36
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_SpecialPowersPushPermissions.html232
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_SpecialPowersPushPrefEnv.html232
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_SpecialPowersSandbox.html153
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_SpecialPowersSandbox.js165
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_SpecialPowersSpawn.html69
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_SpecialPowersSpawn.js68
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_SpecialPowersSpawnChrome.html32
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_TestsRunningAfterSimpleTestFinish.html23
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_bug649012.html37
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_createFiles.html91
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_getweakmapkeys.html26
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_sanity.html62
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_sanityEventUtils.html694
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_sanityException.html16
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_sanityException2.html22
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_sanityParams.html13
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_sanityRegisteredServiceWorker.html24
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_sanityRegisteredServiceWorker2.html25
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_sanitySimpletest.html103
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_sanityWindowSnapshot.html35
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_sanity_cleanup.html30
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_sanity_cleanup2.html24
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_sanity_manifest.html16
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_sanity_manifest_pf.html17
-rw-r--r--testing/mochitest/tests/Harness_sanity/test_sanity_waitForCondition.html51
-rw-r--r--testing/mochitest/tests/Harness_sanity/xpcshell.toml5
39 files changed, 2808 insertions, 0 deletions
diff --git a/testing/mochitest/tests/Harness_sanity/.eslintrc.js b/testing/mochitest/tests/Harness_sanity/.eslintrc.js
new file mode 100644
index 0000000000..a858d9de4c
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/.eslintrc.js
@@ -0,0 +1,12 @@
+"use strict";
+
+module.exports = {
+ extends: ["plugin:mozilla/xpcshell-test"],
+
+ overrides: [
+ {
+ files: "*.html",
+ env: { browser: true },
+ },
+ ],
+};
diff --git a/testing/mochitest/tests/Harness_sanity/SpecialPowersLoadChromeScript.js b/testing/mochitest/tests/Harness_sanity/SpecialPowersLoadChromeScript.js
new file mode 100644
index 0000000000..9591f3bca4
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/SpecialPowersLoadChromeScript.js
@@ -0,0 +1,17 @@
+/* eslint-env mozilla/chrome-script */
+
+// Just receive 'foo' message and forward it back
+// as 'bar' message
+addMessageListener("foo", function (message) {
+ sendAsyncMessage("bar", message);
+});
+
+addMessageListener("valid-assert", function (message) {
+ assert.ok(true, "valid assertion");
+ assert.equal(1, 1, "another valid assertion");
+ sendAsyncMessage("valid-assert-done");
+});
+
+addMessageListener("sync-message", () => {
+ return "Received a synchronous message.";
+});
diff --git a/testing/mochitest/tests/Harness_sanity/empty.js b/testing/mochitest/tests/Harness_sanity/empty.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/empty.js
diff --git a/testing/mochitest/tests/Harness_sanity/file_SpecialPowersFrame1.html b/testing/mochitest/tests/Harness_sanity/file_SpecialPowersFrame1.html
new file mode 100644
index 0000000000..f6d7046e9a
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/file_SpecialPowersFrame1.html
@@ -0,0 +1,14 @@
+<html>
+ <head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <div id="content" style="display: none">
+ <script type="text/javascript">
+ is(SpecialPowers.sanityCheck(), "foo", "Check Special Powers in iframe");
+ </script>
+ </div>
+ </body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/file_spawn.html b/testing/mochitest/tests/Harness_sanity/file_spawn.html
new file mode 100644
index 0000000000..b34317b024
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/file_spawn.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title></title>
+</head>
+<body>
+ <span id="span">Hello there.</span>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/mochitest.toml b/testing/mochitest/tests/Harness_sanity/mochitest.toml
new file mode 100644
index 0000000000..a910407dc8
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/mochitest.toml
@@ -0,0 +1,75 @@
+[DEFAULT]
+
+["test_SimpletestGetTestFileURL.html"]
+
+["test_SpecialPowersExtension.html"]
+
+["test_SpecialPowersExtension2.html"]
+support-files = "file_SpecialPowersFrame1.html"
+
+["test_SpecialPowersLoadChromeScript.html"]
+support-files = "SpecialPowersLoadChromeScript.js"
+
+["test_SpecialPowersLoadChromeScript_function.html"]
+
+["test_SpecialPowersLoadPrivilegedScript.html"]
+
+["test_SpecialPowersPushPermissions.html"]
+support-files = "specialPowers_framescript.js"
+
+["test_SpecialPowersPushPrefEnv.html"]
+
+["test_SpecialPowersSandbox.html"]
+
+["test_SpecialPowersSpawn.html"]
+support-files = "file_spawn.html"
+
+["test_SpecialPowersSpawnChrome.html"]
+
+["test_TestsRunningAfterSimpleTestFinish.html"]
+skip-if = ["true"] #depends on fix for bug 1048446
+
+["test_bug649012.html"]
+
+["test_createFiles.html"]
+
+["test_getweakmapkeys.html"]
+
+["test_sanity.html"]
+
+["test_sanityEventUtils.html"]
+skip-if = [
+ "verify && (os == 'win')", # bug 688052
+ "fission && xorigin", # Bug 1716411 - New fission platform triage
+]
+
+["test_sanityException.html"]
+
+["test_sanityException2.html"]
+
+["test_sanityParams.html"]
+
+["test_sanityRegisteredServiceWorker.html"]
+support-files = "empty.js"
+
+["test_sanityRegisteredServiceWorker2.html"]
+skip-if = [
+ "verify",
+]
+support-files = "empty.js"
+
+["test_sanitySimpletest.html"]
+
+["test_sanityWindowSnapshot.html"]
+
+["test_sanity_cleanup.html"]
+
+["test_sanity_cleanup2.html"]
+
+["test_sanity_manifest.html"]
+fail-if = ["true"]
+
+["test_sanity_manifest_pf.html"]
+fail-if = ["true"]
+
+["test_sanity_waitForCondition.html"]
diff --git a/testing/mochitest/tests/Harness_sanity/specialPowers_framescript.js b/testing/mochitest/tests/Harness_sanity/specialPowers_framescript.js
new file mode 100644
index 0000000000..efc017099b
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/specialPowers_framescript.js
@@ -0,0 +1,13 @@
+/* eslint-env mozilla/chrome-script */
+
+var permChangedObs = {
+ observe(subject, topic, data) {
+ if (topic == "perm-changed") {
+ var permission = subject.QueryInterface(Ci.nsIPermission);
+ var msg = { op: data, type: permission.type };
+ sendAsyncMessage("perm-changed", msg);
+ }
+ },
+};
+
+Services.obs.addObserver(permChangedObs, "perm-changed");
diff --git a/testing/mochitest/tests/Harness_sanity/test_SimpletestGetTestFileURL.html b/testing/mochitest/tests/Harness_sanity/test_SimpletestGetTestFileURL.html
new file mode 100644
index 0000000000..ef368c0ab5
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_SimpletestGetTestFileURL.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for SpecialPowers extension</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+var filename = "MyTestDataFile.txt";
+var url = SimpleTest.getTestFileURL(filename);
+is(url, document.location.href.replace(/test_SimpletestGetTestFileURL\.html.*/, filename));
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_SpecialPowersExtension.html b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersExtension.html
new file mode 100644
index 0000000000..34b75933a0
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersExtension.html
@@ -0,0 +1,198 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for SpecialPowers extension</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="starttest();">
+
+<div id="content" style="display: none">
+ <canvas id="testcanvas" width="200" height="200">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+var eventCount = 0;
+function testEventListener(e) {
+ ++eventCount;
+}
+
+function testEventListener2(e) {
+ ++eventCount;
+}
+
+function dispatchTestEvent() {
+ var e = document.createEvent("Event");
+ e.initEvent("TestEvent", true, true);
+ window.dispatchEvent(e);
+}
+
+dump("\nSPECIALPTEST:::Test script loaded " + (new Date).getTime() + "\n");
+SimpleTest.waitForExplicitFinish();
+var startTime = new Date();
+async function starttest(){
+ dump("\nSPECIALPTEST:::Test script running after load " + (new Date).getTime() + "\n");
+
+ /** Test for SpecialPowers extension **/
+ is(SpecialPowers.sanityCheck(), "foo", "check to see whether the Special Powers extension is installed.");
+
+ // Test a sync call into chrome
+ await SpecialPowers.setBoolPref('extensions.checkCompatibility', true);
+ is(SpecialPowers.getBoolPref('extensions.checkCompatibility'), true, "Check to see if we can set a preference properly");
+ await SpecialPowers.clearUserPref('extensions.checkCompatibility');
+
+ // Test a int pref
+ await SpecialPowers.setIntPref('extensions.foobar', 42);
+ is(SpecialPowers.getIntPref('extensions.foobar'), 42, "Check int pref");
+ await SpecialPowers.clearUserPref('extensions.foobar');
+
+ // Test a string pref
+ await SpecialPowers.setCharPref("extensions.foobaz", "hi there");
+ is(SpecialPowers.getCharPref("extensions.foobaz"), "hi there", "Check string pref");
+ await SpecialPowers.clearUserPref("extensions.foobaz");
+
+ // Test an invalid pref
+ var retVal = null;
+ // eslint-disable-next-line mozilla/use-default-preference-values
+ try {
+ retVal = SpecialPowers.getBoolPref('extensions.checkCompat0123456789');
+ } catch (ex) {
+ retVal = ex;
+ }
+ is(retVal.result, SpecialPowers.Cr.NS_ERROR_UNEXPECTED,
+ "received an exception trying to get an unset preference value");
+
+ SpecialPowers.addChromeEventListener("TestEvent", testEventListener, true, true);
+ SpecialPowers.addChromeEventListener("TestEvent", testEventListener2, true, false);
+ dispatchTestEvent();
+ is(eventCount, 1, "Should have got an event!");
+
+ SpecialPowers.removeChromeEventListener("TestEvent", testEventListener, true);
+ SpecialPowers.removeChromeEventListener("TestEvent", testEventListener2, true);
+ dispatchTestEvent();
+ is(eventCount, 1, "Shouldn't have got an event!");
+
+ // Test Complex Pref - TODO: Without chrome access, I don't know how you'd actually
+ // set this preference since you have to create an XPCOM object.
+ // Leaving untested for now.
+
+ // Test a DOMWindowUtils method and property
+ is(SpecialPowers.DOMWindowUtils.getClassName(window), "Proxy");
+ is(SpecialPowers.DOMWindowUtils.docCharsetIsForced, false);
+
+ // QueryInterface and getPrivilegedProps tests
+ is(SpecialPowers.can_QI(SpecialPowers), false);
+ let doc = SpecialPowers.wrap(document);
+ is(SpecialPowers.getPrivilegedProps(doc, "baseURIObject.fileName"), null,
+ "Should not have a fileName property yet");
+ let uri = SpecialPowers.getPrivilegedProps(doc, "baseURIObject");
+ ok(SpecialPowers.can_QI(uri));
+ ok(SpecialPowers.do_QueryInterface(uri, "nsIURL"));
+ is(SpecialPowers.getPrivilegedProps(doc, "baseURIObject.fileName"),
+ "test_SpecialPowersExtension.html",
+ "Should have a fileName property now");
+
+ //try to run garbage collection
+ SpecialPowers.gc();
+
+ //
+ // Test the SpecialPowers wrapper.
+ //
+
+ let fp = SpecialPowers.Cc["@mozilla.org/filepicker;1"].createInstance(SpecialPowers.Ci.nsIFilePicker);
+ is(fp.mode, SpecialPowers.Ci.nsIFilePicker.modeOpen, "Should be able to get props off privileged objects");
+ var testURI = SpecialPowers.Cc['@mozilla.org/network/standard-url-mutator;1']
+ .createInstance(SpecialPowers.Ci.nsIURIMutator)
+ .setSpec("http://www.foobar.org/")
+ .finalize();
+ is(testURI.spec, "http://www.foobar.org/", "Getters/Setters should work correctly");
+ is(SpecialPowers.wrap(document).getElementsByTagName('details').length, 0, "Should work with proxy-based DOM bindings.");
+
+ // Play with the window object.
+ var docShell = SpecialPowers.wrap(window).docShell;
+ ok(docShell.browsingContext, "Able to pull properties off of docshell!");
+
+ // Make sure Xray-wrapped functions work.
+ try {
+ SpecialPowers.wrap(SpecialPowers.Components).ID('{00000000-0000-0000-0000-000000000000}');
+ ok(true, "Didn't throw");
+ }
+ catch (e) {
+ ok(false, "Threw while trying to call Xray-wrapped function.");
+ }
+
+ // Check constructors.
+ var BinaryInputStream = SpecialPowers.wrap(SpecialPowers.Components).Constructor("@mozilla.org/binaryinputstream;1");
+ var bis = new BinaryInputStream();
+ ok(/nsISupports/.exec(bis.toString()), "Should get the proper object out of the constructor");
+ function TestConstructor() {
+ SpecialPowers.wrap(this).foo = 2;
+ }
+ var WrappedConstructor = SpecialPowers.wrap(TestConstructor);
+ is((new WrappedConstructor()).foo, 2, "JS constructors work properly when wrapped");
+
+ // Try messing around with QuickStubbed getters/setters and make sure the wrapper deals.
+ var ctx = SpecialPowers.wrap(document).getElementById('testcanvas').getContext('2d');
+ var pixels = ctx.getImageData(0,0,1,1);
+ try {
+ pixels.data;
+ ok(true, "Didn't throw getting quickstubbed accessor prop from proto");
+ }
+ catch (e) {
+ ok(false, "Threw while getting quickstubbed accessor prop from proto");
+ }
+
+ // Check functions that return null.
+ var returnsNull = function() { return null; }
+ is(SpecialPowers.wrap(returnsNull)(), null, "Should be able to handle functions that return null.");
+
+ // Check a function that throws.
+ var thrower = function() { throw new Error('hah'); }
+ try {
+ SpecialPowers.wrap(thrower)();
+ ok(false, "Should have thrown");
+ } catch (e) {
+ ok(SpecialPowers.isWrapper(e), "Exceptions should be wrapped for call");
+ is(e.message, 'hah', "Correct message");
+ }
+ try {
+ var ctor = SpecialPowers.wrap(thrower);
+ new ctor();
+ ok(false, "Should have thrown");
+ } catch (e) {
+ ok(SpecialPowers.isWrapper(e), "Exceptions should be wrapped for construct");
+ is(e.message, 'hah', "Correct message");
+ }
+
+ // Play around with a JS object to check the non-xray path.
+ var noxray_proto = {a: 3, b: 12};
+ var noxray = {a: 5, c: 32};
+ noxray.__proto__ = noxray_proto;
+ var noxray_wrapper = SpecialPowers.wrap(noxray);
+ is(noxray_wrapper.c, 32, "Regular properties should work.");
+ is(noxray_wrapper.a, 5, "Shadow properties should work.");
+ is(noxray_wrapper.b, 12, "Proto properties should work.");
+ noxray.b = 122;
+ is(noxray_wrapper.b, 122, "Should be able to shadow.");
+
+ // Try setting file input values via an Xray wrapper.
+ SpecialPowers.wrap(document).title = "foo";
+ is(document.title, "foo", "Set property correctly on Xray-wrapped DOM object");
+ is(SpecialPowers.wrap(document).URI, document.URI, "Got property correctly on Xray-wrapped DOM object");
+
+ info("\nProfile::SpecialPowersRunTime: " + (new Date() - startTime) + "\n");
+
+ // bug 855192
+ ok(SpecialPowers.MockPermissionPrompt, "check mock permission prompt");
+
+ // Set a pref using pushPrefEnv to make sure that flushPrefEnv is
+ // automatically called before we invoke
+ // test_SpecialPowersExtension2.html.
+ SpecialPowers.pushPrefEnv({set: [['testing.some_arbitrary_pref', true]]},
+ function() { SimpleTest.finish(); });
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_SpecialPowersExtension2.html b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersExtension2.html
new file mode 100644
index 0000000000..1adee718f1
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersExtension2.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for SpecialPowers extension</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<div id="content" class="testbody">
+ <script type="text/javascript">
+ dump("\nSPECIALPTEST2:::Loading test2 file now " + (new Date).getTime() + "\n");
+ is(SpecialPowers.sanityCheck(), "foo", "Special Powers top level");
+ ok(!SpecialPowers.Services.prefs.prefHasUserValue('testing.some_arbitrary_pref'),
+ "should not retain pref from previous test");
+ </script>
+ <iframe id="frame1" src="file_SpecialPowersFrame1.html">
+ </iframe>
+</div>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_SpecialPowersLoadChromeScript.html b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersLoadChromeScript.html
new file mode 100644
index 0000000000..7242bd19f5
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersLoadChromeScript.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for SpecialPowers.loadChromeScript</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+var url = SimpleTest.getTestFileURL("SpecialPowersLoadChromeScript.js");
+var script = SpecialPowers.loadChromeScript(url);
+
+var MESSAGE = { bar: true };
+script.addMessageListener("bar", function (message) {
+ is(JSON.stringify(message), JSON.stringify(MESSAGE),
+ "received back message from the chrome script");
+
+ checkAssert();
+});
+
+function checkAssert() {
+ script.sendAsyncMessage("valid-assert");
+ script.addMessageListener("valid-assert-done", endOfFirstTest);
+}
+
+var script2;
+
+function endOfFirstTest() {
+ script.destroy();
+
+ // wantGlobalProperties should add the specified properties to the sandbox
+ // that is used to run the chrome script.
+ script2 = SpecialPowers.loadChromeScript(_ => {
+ /* eslint-env mozilla/chrome-script */
+ addMessageListener("valid-assert", function (message) {
+ assert.equal(typeof XMLHttpRequest, "function", "XMLHttpRequest is defined");
+ assert.equal(typeof CSS, "undefined", "CSS is not defined");
+ sendAsyncMessage("valid-assert-done");
+ });
+ }, { wantGlobalProperties: ["ChromeUtils", "XMLHttpRequest"] });
+
+ script2.sendAsyncMessage("valid-assert");
+ script2.addMessageListener("valid-assert-done", endOfTest);
+
+}
+
+async function endOfTest() {
+ is(await script.sendQuery("sync-message"), "Received a synchronous message.",
+ "Check sync return value");
+
+ script2.destroy();
+ SimpleTest.finish();
+}
+
+script.sendAsyncMessage("foo", MESSAGE);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_SpecialPowersLoadChromeScript_function.html b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersLoadChromeScript_function.html
new file mode 100644
index 0000000000..af31d7b25a
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersLoadChromeScript_function.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for SpecialPowers.loadChromeScript</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+
+
+var script = SpecialPowers.loadChromeScript(function loadChromeScriptTest() {
+ /* eslint-env mozilla/chrome-script */
+ // Copied from SpecialPowersLoadChromeScript.js
+
+ // Just receive 'foo' message and forward it back
+ // as 'bar' message
+ addMessageListener("foo", function (message) {
+ sendAsyncMessage("bar", message);
+ });
+
+ addMessageListener("valid-assert", function (message) {
+ assert.ok(true, "valid assertion");
+ assert.equal(1, 1, "another valid assertion");
+ sendAsyncMessage("valid-assert-done");
+ });
+
+ addMessageListener("sync-message", () => {
+ return "Received a synchronous message.";
+ });
+});
+
+var MESSAGE = { bar: true };
+script.addMessageListener("bar", function (message) {
+ is(JSON.stringify(message), JSON.stringify(MESSAGE),
+ "received back message from the chrome script");
+
+ checkAssert();
+});
+
+function checkAssert() {
+ script.sendAsyncMessage("valid-assert");
+ script.addMessageListener("valid-assert-done", endOfTest);
+}
+
+async function endOfTest() {
+ is(await script.sendQuery("sync-message"), "Received a synchronous message.",
+ "Check sync return value");
+
+ script.destroy();
+ SimpleTest.finish();
+}
+
+script.sendAsyncMessage("foo", MESSAGE);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_SpecialPowersLoadPrivilegedScript.html b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersLoadPrivilegedScript.html
new file mode 100644
index 0000000000..6294c44dfe
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersLoadPrivilegedScript.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for SpecialPowers.loadChromeScript</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+/* eslint-disable mozilla/use-services */
+function loadPrivilegedScriptTest() {
+ function isMainProcess() {
+ return Cc["@mozilla.org/xre/app-info;1"].
+ getService(Ci.nsIXULRuntime).
+ processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+ }
+ port.postMessage({'isMainProcess': isMainProcess()});
+}
+
+var contentProcessType = SpecialPowers.isMainProcess();
+var port;
+try {
+ port = SpecialPowers.loadPrivilegedScript(loadPrivilegedScriptTest.toString());
+} catch (e) {
+ ok(false, "loadPrivilegedScript shoulde not throw");
+}
+port.onmessage = (e) => {
+ is(contentProcessType, e.data.isMainProcess, "content and the script should be in the same process");
+ SimpleTest.finish();
+};
+</script>
diff --git a/testing/mochitest/tests/Harness_sanity/test_SpecialPowersPushPermissions.html b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersPushPermissions.html
new file mode 100644
index 0000000000..3aeebb22ff
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersPushPermissions.html
@@ -0,0 +1,232 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for SpecialPowers extension</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="starttest();">
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+const ALLOW_ACTION = SpecialPowers.Ci.nsIPermissionManager.ALLOW_ACTION;
+const DENY_ACTION = SpecialPowers.Ci.nsIPermissionManager.DENY_ACTION;
+const UNKNOWN_ACTION = SpecialPowers.Ci.nsIPermissionManager.UNKNOWN_ACTION;
+const PROMPT_ACTION = SpecialPowers.Ci.nsIPermissionManager.PROMPT_ACTION;
+const ACCESS_SESSION = SpecialPowers.Ci.nsICookiePermission.ACCESS_SESSION;
+
+const EXPIRE_TIME = SpecialPowers.Ci.nsIPermissionManager.EXPIRE_TIME;
+// expire Setting:
+// start expire time point
+// ----|------------------------|-----------
+// <------------------------>
+// PERIOD
+var start;
+// PR_Now() that called in PermissionManager to get the system time
+// is sometimes 100ms~600s more than Date.now() on Android 4.3 API11.
+// Thus, the PERIOD should be larger than 600ms in this test.
+const PERIOD = 900;
+var gScript = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('specialPowers_framescript.js'));
+SimpleTest.requestFlakyTimeout("untriaged");
+
+function starttest(){
+ SpecialPowers.addPermission("pPROMPT", PROMPT_ACTION, document);
+ SpecialPowers.addPermission("pALLOW", ALLOW_ACTION, document);
+ SpecialPowers.addPermission("pDENY", DENY_ACTION, document);
+ SpecialPowers.addPermission("pREMOVE", ALLOW_ACTION, document);
+ SpecialPowers.addPermission("pSESSION", ACCESS_SESSION, document);
+
+ setTimeout(test1, 0);
+}
+
+SimpleTest.waitForExplicitFinish();
+
+async function test1() {
+ if (!await SpecialPowers.testPermission('pALLOW', ALLOW_ACTION, document)) {
+ dump('/**** allow not set ****/\n');
+ setTimeout(test1, 0);
+ } else if (!await SpecialPowers.testPermission('pDENY', DENY_ACTION, document)) {
+ dump('/**** deny not set ****/\n');
+ setTimeout(test1, 0);
+ } else if (!await SpecialPowers.testPermission('pPROMPT', PROMPT_ACTION, document)) {
+ dump('/**** prompt not set ****/\n');
+ setTimeout(test1, 0);
+ } else if (!await SpecialPowers.testPermission('pREMOVE', ALLOW_ACTION, document)) {
+ dump('/**** remove not set ****/\n');
+ setTimeout(test1, 0);
+ } else if (!await SpecialPowers.testPermission('pSESSION', ACCESS_SESSION, document)) {
+ dump('/**** ACCESS_SESSION not set ****/\n');
+ setTimeout(test1, 0);
+ } else {
+ test2();
+ }
+}
+
+async function test2() {
+ ok(await SpecialPowers.testPermission('pUNKNOWN', UNKNOWN_ACTION, document), 'pUNKNOWN value should have UNKOWN permission');
+ SpecialPowers.pushPermissions([
+ {'type': 'pUNKNOWN', 'allow': true, 'context': document},
+ {'type': 'pALLOW', 'allow': false, 'context': document},
+ {'type': 'pDENY', 'allow': true, 'context': document},
+ {'type': 'pPROMPT', 'allow': true, 'context': document},
+ {'type': 'pSESSION', 'allow': true, 'context': document},
+ {'type': 'pREMOVE', 'remove': true, 'context': document},
+ ], test3);
+}
+
+async function test3() {
+ ok(await SpecialPowers.testPermission('pUNKNOWN', ALLOW_ACTION, document), 'pUNKNOWN value should have ALLOW permission');
+ ok(await SpecialPowers.testPermission('pPROMPT', ALLOW_ACTION, document), 'pPROMPT value should have ALLOW permission');
+ ok(await SpecialPowers.testPermission('pALLOW', DENY_ACTION, document), 'pALLOW should have DENY permission');
+ ok(await SpecialPowers.testPermission('pDENY', ALLOW_ACTION, document), 'pDENY should have ALLOW permission');
+ ok(await SpecialPowers.testPermission('pREMOVE', UNKNOWN_ACTION, document), 'pREMOVE should have REMOVE permission');
+ ok(await SpecialPowers.testPermission('pSESSION', ALLOW_ACTION, document), 'pSESSION should have ALLOW permission');
+
+ // only pPROMPT (last one) is different, the other stuff is just to see if it doesn't cause test failures
+ SpecialPowers.pushPermissions([
+ {'type': 'pUNKNOWN', 'allow': true, 'context': document},
+ {'type': 'pALLOW', 'allow': false, 'context': document},
+ {'type': 'pDENY', 'allow': true, 'context': document},
+ {'type': 'pPROMPT', 'allow': false, 'context': document},
+ {'type': 'pREMOVE', 'remove': true, 'context': document},
+ ], test3b);
+}
+
+async function test3b() {
+ ok(await SpecialPowers.testPermission('pPROMPT', DENY_ACTION, document), 'pPROMPT value should have DENY permission');
+ SpecialPowers.pushPermissions([
+ {'type': 'pUNKNOWN', 'allow': DENY_ACTION, 'context': document},
+ {'type': 'pALLOW', 'allow': PROMPT_ACTION, 'context': document},
+ {'type': 'pDENY', 'allow': PROMPT_ACTION, 'context': document},
+ {'type': 'pPROMPT', 'allow': ALLOW_ACTION, 'context': document},
+ ], test4);
+}
+
+async function test4() {
+ ok(await SpecialPowers.testPermission('pUNKNOWN', DENY_ACTION, document), 'pUNKNOWN value should have DENY permission');
+ ok(await SpecialPowers.testPermission('pPROMPT', ALLOW_ACTION, document), 'pPROMPT value should have ALLOW permission');
+ ok(await SpecialPowers.testPermission('pALLOW', PROMPT_ACTION, document), 'pALLOW should have PROMPT permission');
+ ok(await SpecialPowers.testPermission('pDENY', PROMPT_ACTION, document), 'pDENY should have PROMPT permission');
+ //this should reset all the permissions to before all the pushPermissions calls
+ SpecialPowers.flushPermissions(test5);
+}
+
+async function test5() {
+ ok(await SpecialPowers.testPermission('pUNKNOWN', UNKNOWN_ACTION, document), 'pUNKNOWN should have UNKNOWN permission');
+ ok(await SpecialPowers.testPermission('pALLOW', ALLOW_ACTION, document), 'pALLOW should have ALLOW permission');
+ ok(await SpecialPowers.testPermission('pDENY', DENY_ACTION, document), 'pDENY should have DENY permission');
+ ok(await SpecialPowers.testPermission('pPROMPT', PROMPT_ACTION, document), 'pPROMPT should have PROMPT permission');
+ ok(await SpecialPowers.testPermission('pREMOVE', ALLOW_ACTION, document), 'pREMOVE should have ALLOW permission');
+ ok(await SpecialPowers.testPermission('pSESSION', ACCESS_SESSION, document), 'pSESSION should have ACCESS_SESSION permission');
+
+ SpecialPowers.removePermission("pPROMPT", document);
+ SpecialPowers.removePermission("pALLOW", document);
+ SpecialPowers.removePermission("pDENY", document);
+ SpecialPowers.removePermission("pREMOVE", document);
+ SpecialPowers.removePermission("pSESSION", document);
+
+ setTimeout(test6, 0);
+}
+
+async function test6() {
+ if (!await SpecialPowers.testPermission('pALLOW', UNKNOWN_ACTION, document)) {
+ dump('/**** allow still set ****/\n');
+ setTimeout(test6, 0);
+ } else if (!await SpecialPowers.testPermission('pDENY', UNKNOWN_ACTION, document)) {
+ dump('/**** deny still set ****/\n');
+ setTimeout(test6, 0);
+ } else if (!await SpecialPowers.testPermission('pPROMPT', UNKNOWN_ACTION, document)) {
+ dump('/**** prompt still set ****/\n');
+ setTimeout(test6, 0);
+ } else if (!await SpecialPowers.testPermission('pREMOVE', UNKNOWN_ACTION, document)) {
+ dump('/**** remove still set ****/\n');
+ setTimeout(test6, 0);
+ } else if (!await SpecialPowers.testPermission('pSESSION', UNKNOWN_ACTION, document)) {
+ dump('/**** pSESSION still set ****/\n');
+ setTimeout(test6, 0);
+ } else {
+ test7();
+ }
+}
+
+function test7() {
+ afterPermissionChanged('pEXPIRE', 'deleted', test8);
+ afterPermissionChanged('pEXPIRE', 'added', permissionPollingCheck);
+ start = Number(Date.now());
+ SpecialPowers.addPermission('pEXPIRE',
+ true,
+ document,
+ EXPIRE_TIME,
+ (start + PERIOD + getPlatformInfo().timeCompensation));
+}
+
+function test8() {
+ afterPermissionChanged('pEXPIRE', 'deleted', SimpleTest.finish);
+ afterPermissionChanged('pEXPIRE', 'added', permissionPollingCheck);
+ start = Number(Date.now());
+ SpecialPowers.pushPermissions([
+ { 'type': 'pEXPIRE',
+ 'allow': true,
+ 'expireType': EXPIRE_TIME,
+ 'expireTime': (start + PERIOD + getPlatformInfo().timeCompensation),
+ 'context': document
+ }], function() {
+ info("Wait for permission-changed signal!");
+ }
+ );
+}
+
+function afterPermissionChanged(type, op, callback) {
+ // handle the message from specialPowers_framescript.js
+ gScript.addMessageListener('perm-changed', function onChange(msg) {
+ if (msg.type == type && msg.op == op) {
+ gScript.removeMessageListener('perm-changed', onChange);
+ callback();
+ }
+ });
+}
+
+async function permissionPollingCheck() {
+ var now = Number(Date.now());
+ if (now < (start + PERIOD)) {
+ if (await SpecialPowers.testPermission('pEXPIRE', ALLOW_ACTION, document)) {
+ // To make sure that permission will be expired in next round,
+ // the next permissionPollingCheck calling will be fired 100ms later after
+ // permission is out-of-period.
+ setTimeout(permissionPollingCheck, PERIOD + 100);
+ return;
+ }
+
+ errorHandler('unexpired permission should be allowed!');
+ }
+
+ // The permission is already expired!
+ if (await SpecialPowers.testPermission('pEXPIRE', ALLOW_ACTION, document)) {
+ errorHandler('expired permission should be removed!');
+ }
+}
+
+function getPlatformInfo() {
+ var version = SpecialPowers.Services.sysinfo.getProperty('version');
+ version = parseFloat(version);
+
+ // PR_Now() that called in PermissionManager to get the system time and
+ // Date.now() are out of sync on win32 platform(XP/win7). The PR_Now() is
+ // 15~20ms less than Date.now(). Unfortunately, this time skew can't be
+ // avoided, so it needs to add a time buffer to compensate.
+ // Version 5.1 is win XP, 6.1 is win7
+ if (navigator.platform.startsWith('Win32') && (version <= 6.1)) {
+ return { platform: "Win32", timeCompensation: -100 };
+ }
+
+ return { platform: "NoMatter", timeCompensation: 0 };
+}
+
+function errorHandler(msg) {
+ ok(false, msg);
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_SpecialPowersPushPrefEnv.html b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersPushPrefEnv.html
new file mode 100644
index 0000000000..727f17349b
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersPushPrefEnv.html
@@ -0,0 +1,232 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for SpecialPowers extension</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="starttest();">
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+async function starttest() {
+ try {
+ await SpecialPowers.setBoolPref("test.bool", 1);
+ } catch(e) {
+ await SpecialPowers.setBoolPref("test.bool", true);
+ }
+ try {
+ await SpecialPowers.setIntPref("test.int", true);
+ } catch(e) {
+ await SpecialPowers.setIntPref("test.int", 1);
+ }
+ await SpecialPowers.setCharPref("test.char", 'test');
+ await SpecialPowers.setBoolPref("test.cleanup", false);
+
+ setTimeout(test1, 0, 0);
+}
+
+SimpleTest.waitForExplicitFinish();
+
+function test1(aCount) {
+ if (aCount >= 20) {
+ ok(false, "Too many times attempting to set pref, aborting");
+ SimpleTest.finish();
+ return;
+ }
+
+ try {
+ is(SpecialPowers.getBoolPref('test.bool'), true, 'test.bool should be true');
+ } catch(e) {
+ setTimeout(test1, 0, ++aCount);
+ return;
+ }
+
+ try {
+ is(SpecialPowers.getIntPref('test.int'), 1, 'test.int should be 1');
+ } catch(e) {
+ setTimeout(test1, 0, ++aCount);
+ return;
+ }
+
+ try {
+ is(SpecialPowers.getCharPref('test.char'), 'test', 'test.char should be test');
+ } catch(e) {
+ setTimeout(test1, 0, ++aCount);
+ return;
+ }
+
+ test2();
+}
+
+function test2() {
+ // test non-changing values
+ SpecialPowers.pushPrefEnv({"set": [["test.bool", true], ["test.int", 1], ["test.char", "test"]]}, test3);
+}
+
+function test3() {
+ // test changing char pref using the Promise
+ is(SpecialPowers.getBoolPref('test.bool'), true, 'test.bool should be true');
+ is(SpecialPowers.getIntPref('test.int'), 1, 'test.int should be 1');
+ is(SpecialPowers.getCharPref('test.char'), 'test', 'test.char should be test');
+ SpecialPowers.pushPrefEnv({"set": [["test.bool", true], ["test.int", 1], ["test.char", "test2"]]}).then(test4);
+}
+
+function test4() {
+ // test changing all values and adding test.char2 pref
+ is(SpecialPowers.getCharPref('test.char'), 'test2', 'test.char should be test2');
+ SpecialPowers.pushPrefEnv({"set": [["test.bool", false], ["test.int", 10], ["test.char", "test2"], ["test.char2", "test"]]}, test5);
+}
+
+function test5() {
+ // test flushPrefEnv
+ is(SpecialPowers.getBoolPref('test.bool'), false, 'test.bool should be false');
+ is(SpecialPowers.getIntPref('test.int'), 10, 'test.int should be 10');
+ is(SpecialPowers.getCharPref('test.char'), 'test2', 'test.char should be test2');
+ is(SpecialPowers.getCharPref('test.char2'), 'test', 'test.char2 should be test');
+ SpecialPowers.flushPrefEnv(test6);
+}
+
+function test6() {
+ // test clearing prefs
+ is(SpecialPowers.getBoolPref('test.bool'), true, 'test.bool should be true');
+ is(typeof SpecialPowers.getBoolPref('test.bool'), typeof true, 'test.bool should be boolean');
+ is(SpecialPowers.getIntPref('test.int'), 1, 'test.int should be 1');
+ is(typeof SpecialPowers.getIntPref('test.int'), typeof 1, 'test.int should be integer');
+ is(SpecialPowers.getCharPref('test.char'), 'test', 'test.char should be test');
+ is(typeof SpecialPowers.getCharPref('test.char'), typeof 'test', 'test.char should be String');
+ try {
+ SpecialPowers.getCharPref('test.char2');
+ ok(false, 'This ok should not be reached!');
+ } catch(e) {
+ ok(true, 'getCharPref("test.char2") should throw');
+ }
+ SpecialPowers.pushPrefEnv({"clear": [["test.bool"], ["test.int"], ["test.char"], ["test.char2"]]}, test6b);
+}
+
+function test6b() {
+ // test if clearing another time doesn't cause issues
+ SpecialPowers.pushPrefEnv({"clear": [["test.bool"], ["test.int"], ["test.char"], ["test.char2"]]}, test7);
+}
+
+function test7() {
+ try {
+ SpecialPowers.getBoolPref('test.bool');
+ ok(false, 'This ok should not be reached!');
+ } catch(e) {
+ ok(true, 'getBoolPref("test.bool") should throw');
+ }
+
+ try {
+ SpecialPowers.getIntPref('test.int');
+ ok(false, 'This ok should not be reached!');
+ } catch(e) {
+ ok(true, 'getIntPref("test.int") should throw');
+ }
+
+ try {
+ SpecialPowers.getCharPref('test.char');
+ ok(false, 'This ok should not be reached!');
+ } catch(e) {
+ ok(true, 'getCharPref("test.char") should throw');
+ }
+
+ try {
+ SpecialPowers.getCharPref('test.char2');
+ ok(false, 'This ok should not be reached!');
+ } catch(e) {
+ ok(true, 'getCharPref("test.char2") should throw');
+ }
+
+ SpecialPowers.flushPrefEnv().then(test8);
+}
+
+function test8() {
+ is(SpecialPowers.getBoolPref('test.bool'), true, 'test.bool should be true');
+ is(typeof SpecialPowers.getBoolPref('test.bool'), typeof true, 'test.bool should be boolean');
+ is(SpecialPowers.getIntPref('test.int'), 1, 'test.int should be 1');
+ is(typeof SpecialPowers.getIntPref('test.int'), typeof 1, 'test.int should be integer');
+ is(SpecialPowers.getCharPref('test.char'), 'test', 'test.char should be test');
+ is(typeof SpecialPowers.getCharPref('test.char'), typeof 'test', 'test.char should be String');
+ try {
+ SpecialPowers.getCharPref('test.char2');
+ ok(false, 'This ok should not be reached!');
+ } catch(e) {
+ ok(true, 'getCharPref("test.char2") should throw');
+ }
+ SpecialPowers.clearUserPref("test.bool");
+ SpecialPowers.clearUserPref("test.int");
+ SpecialPowers.clearUserPref("test.char");
+ setTimeout(test9, 0, 0);
+}
+
+function test9(aCount) {
+ if (aCount >= 20) {
+ ok(false, "Too many times attempting to set pref, aborting");
+ SimpleTest.finish();
+ return;
+ }
+
+ try {
+ SpecialPowers.getBoolPref('test.bool');
+ setTimeout(test9, 0, ++aCount);
+ } catch(e) {
+ test10(0);
+ }
+}
+
+function test10(aCount) {
+ if (aCount >= 20) {
+ ok(false, "Too many times attempting to set pref, aborting");
+ SimpleTest.finish();
+ return;
+ }
+
+ try {
+ SpecialPowers.getIntPref('test.int');
+ setTimeout(test10, 0, ++aCount);
+ } catch(e) {
+ test11(0);
+ }
+}
+
+function test11(aCount) {
+ if (aCount >= 20) {
+ ok(false, "Too many times attempting to set pref, aborting");
+ SimpleTest.finish();
+ return;
+ }
+
+ try {
+ SpecialPowers.getCharPref('test.char');
+ setTimeout(test11, 0, ++aCount);
+ } catch(e) {
+ test12();
+ }
+}
+
+function test12() {
+ // Set test.cleanup to true via pushPrefEnv, while its default value is false.
+ SpecialPowers.pushPrefEnv({"set": [["test.cleanup", true]]}, () => {
+ // Update the preference manually back to its original value.
+ SpecialPowers.setBoolPref("test.cleanup", false);
+ setTimeout(test13, 0);
+ });
+}
+
+function test13() {
+ // Try to flush preferences. Since test.cleanup has manually been set to false, there
+ // will not be any visible update. Check that the flush does not timeout.
+ SpecialPowers.flushPrefEnv(() => {
+ ok(true, 'flushPrefEnv did not time out');
+ is(SpecialPowers.getBoolPref('test.cleanup'), false, 'test.cleanup should be false');
+ SimpleTest.finish();
+ });
+}
+
+// todo - test non-changing values, test complex values, test mixing of pushprefEnv 'set' and 'clear'
+// When bug 776424 gets fixed, getPref doesn't throw anymore, so this test would have to be changed accordingly
+</script>
+</pre>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_SpecialPowersSandbox.html b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersSandbox.html
new file mode 100644
index 0000000000..44efb70eeb
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersSandbox.html
@@ -0,0 +1,153 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for SpecialPowers sandboxes</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<iframe id="iframe"></iframe>
+
+<script>
+/**
+ * Tests that the shared sandbox functionality for cross-process script
+ * execution works as expected. In particular, ensures that Assert methods
+ * report the correct diagnostics in the caller scope.
+ */
+
+/* globals SpecialPowers, Assert */
+
+async function interceptDiagnostics(func) {
+ let originalRecord = SimpleTest.record;
+ try {
+ let diags = [];
+
+ SimpleTest.record = (condition, name, diag, stack) => {
+ diags.push({condition, name, diag, stack});
+ };
+
+ await func();
+
+ return diags;
+ } finally {
+ SimpleTest.record = originalRecord;
+ }
+}
+
+add_task(async function() {
+ const frameSrc = "https://example.com/tests/testing/mochitest/tests/Harness_sanity/file_spawn.html";
+ const subframeSrc = "https://example.org/tests/testing/mochitest/tests/Harness_sanity/file_spawn.html";
+
+ let frame = document.getElementById("iframe");
+ frame.src = frameSrc;
+
+ await new Promise(resolve => {
+ frame.addEventListener("load", resolve, {once: true});
+ });
+
+ let expected = [
+ [false, "Thing - 1 == 2", "got 1, expected 2 (operator ==)"],
+ [true, "Hmm - 1 == 1", undefined],
+ [true, "Yay. - true == true", undefined],
+ [false, "Boo!. - false == true", "got false, expected true (operator ==)"],
+ [false, "Missing expected exception Rej_bad", "got null, expected /./ (operator undefined)"],
+ [true, "Rej_ok", undefined],
+ ];
+
+ // Test that a representative variety of assertions work as expected, and
+ // trigger the expected calls to the harness's reporting function.
+ //
+ // Note: Assert.sys.mjs has its own tests, and defers all of its reporting to a
+ // single reporting function, so we don't need to test it comprehensively. We
+ // just need to make sure that the general functionality works as expected.
+ let tests = {
+ "SpecialPowers.spawn": () => {
+ return SpecialPowers.spawn(frame, [], async () => {
+ Assert.equal(1, 2, "Thing");
+ Assert.equal(1, 1, "Hmm");
+ Assert.ok(true, "Yay.");
+ Assert.ok(false, "Boo!.");
+ await Assert.rejects(Promise.resolve(), /./, "Rej_bad");
+ await Assert.rejects(Promise.reject(new Error("k")), /k/, "Rej_ok");
+ });
+ },
+ "SpecialPowers.spawn-subframe": () => {
+ return SpecialPowers.spawn(frame, [subframeSrc], async src => {
+ let subFrame = this.content.document.createElement("iframe");
+ subFrame.src = src;
+ this.content.document.body.appendChild(subFrame);
+
+ await new Promise(resolve => {
+ subFrame.addEventListener("load", resolve, { once: true });
+ });
+
+ await SpecialPowers.spawn(subFrame, [], async () => {
+ Assert.equal(1, 2, "Thing");
+ Assert.equal(1, 1, "Hmm");
+ Assert.ok(true, "Yay.");
+ Assert.ok(false, "Boo!.");
+ await Assert.rejects(Promise.resolve(), /./, "Rej_bad");
+ await Assert.rejects(Promise.reject(new Error("k")), /k/, "Rej_ok");
+ });
+ });
+ },
+ "SpecialPowers.spawnChrome": () => {
+ return SpecialPowers.spawnChrome([], async () => {
+ Assert.equal(1, 2, "Thing");
+ Assert.equal(1, 1, "Hmm");
+ Assert.ok(true, "Yay.");
+ Assert.ok(false, "Boo!.");
+ await Assert.rejects(Promise.resolve(), /./, "Rej_bad");
+ await Assert.rejects(Promise.reject(new Error("k")), /k/, "Rej_ok");
+ });
+ },
+ "SpecialPowers.loadChromeScript": async () => {
+ let script = SpecialPowers.loadChromeScript(() => {
+ /* eslint-env mozilla/chrome-script */
+ const resultPromise = (async () => {
+ Assert.equal(1, 2, "Thing");
+ Assert.equal(1, 1, "Hmm");
+ Assert.ok(true, "Yay.");
+ Assert.ok(false, "Boo!.");
+ await Assert.rejects(Promise.resolve(), /./, "Rej_bad");
+ await Assert.rejects(Promise.reject(new Error("k")), /k/, "Rej_ok");
+ })();
+ this.addMessageListener("ping", () => resultPromise);
+ });
+
+ await script.sendQuery("ping");
+ script.destroy();
+ },
+ };
+
+ for (let [name, func] of Object.entries(tests)) {
+ info(`Starting task: ${name}`);
+
+ let diags = await interceptDiagnostics(func);
+
+ let results = diags.map(diag => [diag.condition, diag.name, diag.diag]);
+
+ isDeeply(results, expected, "Got expected assertions");
+ for (let { name: diagName, stack } of diags) {
+ ok(stack, `Got stack for: ${diagName}`);
+ // Unlike test_SpecialPowersSandbox.js, expectedFilenamePart does not end
+ // with ":" (separator between file name and line number). This is because
+ // xorigin tests run this test file with a query param ("currentTestURL"),
+ // and the name therefore looks like "test_SpecialPowersSandbox.html?...:"
+ // instead of "test_SpecialPowersSandbox.html:"
+ let expectedFilenamePart = "/test_SpecialPowersSandbox.html";
+ if (name === "SpecialPowers.loadChromeScript") {
+ // Unfortunately, the original file name is not included;
+ // the function name or a dummy value is used instead.
+ expectedFilenamePart = "loadChromeScript anonymous function>";
+ }
+ if (!stack.includes(expectedFilenamePart)) {
+ ok(false, `Stack does not contain ${expectedFilenamePart}: ${stack}`);
+ }
+ }
+ }
+});
+</script>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_SpecialPowersSandbox.js b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersSandbox.js
new file mode 100644
index 0000000000..60db9440b0
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersSandbox.js
@@ -0,0 +1,165 @@
+"use strict";
+
+/* eslint-disable @microsoft/sdl/no-insecure-url */
+
+const { XPCShellContentUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/XPCShellContentUtils.sys.mjs"
+);
+
+XPCShellContentUtils.init(this);
+
+const HTML = String.raw`<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title></title>
+</head>
+<body>
+ <span id="span">Hello there.</span>
+</body>
+</html>`;
+
+const server = XPCShellContentUtils.createHttpServer({
+ hosts: ["example.com", "example.org"],
+});
+
+server.registerPathHandler("/", (request, response) => {
+ response.setHeader("Content-Type", "text/html");
+ response.write(HTML);
+});
+/**
+ * Tests that the shared sandbox functionality for cross-process script
+ * execution works as expected. In particular, ensures that Assert methods
+ * report the correct diagnostics in the caller scope.
+ */
+
+let scope = this;
+
+async function interceptDiagnostics(func) {
+ let originalRecord = scope.do_report_result;
+ try {
+ let diags = [];
+
+ scope.do_report_result = (passed, msg, stack) => {
+ diags.push({ passed, msg, stack });
+ };
+
+ await func();
+
+ return diags;
+ } finally {
+ scope.do_report_result = originalRecord;
+ }
+}
+
+add_task(async function () {
+ const frameSrc = "http://example.com/";
+ const subframeSrc = "http://example.org/";
+
+ let page = await XPCShellContentUtils.loadContentPage(frameSrc, {
+ remote: true,
+ remoteSubframes: true,
+ });
+
+ let { SpecialPowers, browsingContext } = page;
+
+ let expected = [
+ [false, "Thing - 1 == 2"],
+ [true, "Hmm - 1 == 1"],
+ [true, "Yay. - true == true"],
+ [false, "Boo!. - false == true"],
+ [false, "Missing expected exception Rej_bad"],
+ [true, "Rej_ok"],
+ ];
+
+ // Test that a representative variety of assertions work as expected, and
+ // trigger the expected calls to the harness's reporting function.
+ //
+ // Note: Assert.sys.mjs has its own tests, and defers all of its reporting to a
+ // single reporting function, so we don't need to test it comprehensively. We
+ // just need to make sure that the general functionality works as expected.
+ let tests = {
+ "SpecialPowers.spawn": () => {
+ return SpecialPowers.spawn(browsingContext, [], async () => {
+ Assert.equal(1, 2, "Thing");
+ Assert.equal(1, 1, "Hmm");
+ Assert.ok(true, "Yay.");
+ Assert.ok(false, "Boo!.");
+ await Assert.rejects(Promise.resolve(), /./, "Rej_bad");
+ await Assert.rejects(Promise.reject(new Error("k")), /k/, "Rej_ok");
+ });
+ },
+ "SpecialPowers.spawn-subframe": () => {
+ return SpecialPowers.spawn(browsingContext, [subframeSrc], async src => {
+ let subFrame = this.content.document.createElement("iframe");
+ subFrame.src = src;
+ this.content.document.body.appendChild(subFrame);
+
+ await new Promise(resolve => {
+ subFrame.addEventListener("load", resolve, { once: true });
+ });
+
+ await SpecialPowers.spawn(subFrame, [], async () => {
+ Assert.equal(1, 2, "Thing");
+ Assert.equal(1, 1, "Hmm");
+ Assert.ok(true, "Yay.");
+ Assert.ok(false, "Boo!.");
+ await Assert.rejects(Promise.resolve(), /./, "Rej_bad");
+ await Assert.rejects(Promise.reject(new Error("k")), /k/, "Rej_ok");
+ });
+ });
+ },
+ "SpecialPowers.spawnChrome": () => {
+ return SpecialPowers.spawnChrome([], async () => {
+ Assert.equal(1, 2, "Thing");
+ Assert.equal(1, 1, "Hmm");
+ Assert.ok(true, "Yay.");
+ Assert.ok(false, "Boo!.");
+ await Assert.rejects(Promise.resolve(), /./, "Rej_bad");
+ await Assert.rejects(Promise.reject(new Error("k")), /k/, "Rej_ok");
+ });
+ },
+ "SpecialPowers.loadChromeScript": async () => {
+ let script = SpecialPowers.loadChromeScript(() => {
+ /* eslint-env mozilla/chrome-script */
+ const resultPromise = (async () => {
+ Assert.equal(1, 2, "Thing");
+ Assert.equal(1, 1, "Hmm");
+ Assert.ok(true, "Yay.");
+ Assert.ok(false, "Boo!.");
+ await Assert.rejects(Promise.resolve(), /./, "Rej_bad");
+ await Assert.rejects(Promise.reject(new Error("k")), /k/, "Rej_ok");
+ })();
+ this.addMessageListener("ping", () => resultPromise);
+ });
+
+ await script.sendQuery("ping");
+ script.destroy();
+ },
+ };
+
+ for (let [name, func] of Object.entries(tests)) {
+ info(`Starting task: ${name}`);
+
+ let diags = await interceptDiagnostics(func);
+
+ let results = diags.map(diag => [diag.passed, diag.msg]);
+
+ deepEqual(results, expected, "Got expected assertions");
+ for (let { msg, stack } of diags) {
+ ok(stack, `Got stack for: ${msg}`);
+ // Unlike the html version of this test, this one does not include a "/"
+ // in front of the file name, because somehow Android only includes the
+ // file name, and not the fuller path.
+ let expectedFilenamePart = "test_SpecialPowersSandbox.js:";
+ if (name === "SpecialPowers.loadChromeScript") {
+ // Unfortunately, the original file name is not included;
+ // the function name or a dummy value is used instead.
+ expectedFilenamePart = "loadChromeScript anonymous function>:";
+ }
+ if (!stack.includes(expectedFilenamePart)) {
+ ok(false, `Stack does not contain ${expectedFilenamePart}: ${stack}`);
+ }
+ }
+ }
+});
diff --git a/testing/mochitest/tests/Harness_sanity/test_SpecialPowersSpawn.html b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersSpawn.html
new file mode 100644
index 0000000000..fe93c1fe83
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersSpawn.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for SpecialPowers.spawn</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<iframe id="iframe"></iframe>
+
+<span id="hello">World.</span>
+
+<script>
+/* globals SpecialPowers, content */
+
+ add_task(async function() {
+ let frame = document.getElementById("iframe");
+ frame.src = "https://example.com/tests/testing/mochitest/tests/Harness_sanity/file_spawn.html";
+
+ await new Promise(resolve => {
+ frame.addEventListener("load", resolve, {once: true});
+ });
+
+ let result = await SpecialPowers.spawn(frame, ["#span"], selector => {
+ let elem = content.document.querySelector(selector);
+ return elem.textContent;
+ });
+
+ is(result, "Hello there.", "Got correct element text from frame");
+
+ /* eslint-disable no-shadow */
+ result = await SpecialPowers.spawn(frame, ["#hello"], selector => {
+ return SpecialPowers.spawn(content.parent, [selector], selector => {
+ let elem = content.document.querySelector(selector);
+ return elem.textContent;
+ });
+ });
+
+ is(result, "World.", "Got correct element text from frame's window.parent");
+
+ result = await SpecialPowers.spawn(frame.contentWindow, ["#span"], selector => {
+ let elem = content.document.querySelector(selector);
+ return elem.textContent;
+ });
+
+ is(result, "Hello there.", "Got correct element text from window proxy");
+
+ result = await SpecialPowers.spawn(SpecialPowers.getPrivilegedProps(frame, "browsingContext"),
+ ["#span"], selector => {
+ let elem = content.document.querySelector(selector);
+ return elem.textContent;
+ });
+
+ is(result, "Hello there.", "Got correct element text from browsing context");
+
+ let line = 58; // Keep this in sync with the line number where the callback function starts.
+ let callback = () => {
+ let e = new Error("Hello.");
+ return { filename: e.fileName, lineNumber: e.lineNumber };
+ };
+
+ let loc = await SpecialPowers.spawn(frame, [], callback);
+ is(loc.filename, location.href, "Error should have correct script filename");
+ is(loc.lineNumber, line + 2, "Error should have correct script line number");
+ });
+</script>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_SpecialPowersSpawn.js b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersSpawn.js
new file mode 100644
index 0000000000..b910f94436
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersSpawn.js
@@ -0,0 +1,68 @@
+"use strict";
+
+const { XPCShellContentUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/XPCShellContentUtils.sys.mjs"
+);
+
+XPCShellContentUtils.init(this);
+
+const HTML = String.raw`<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title></title>
+</head>
+<body>
+ <span id="span">Hello there.</span>
+</body>
+</html>`;
+
+const server = XPCShellContentUtils.createHttpServer({
+ hosts: ["example.com"],
+});
+
+server.registerPathHandler("/", (request, response) => {
+ response.setHeader("Content-Type", "text/html");
+ response.write(HTML);
+});
+
+add_task(async function () {
+ // eslint-disable-next-line @microsoft/sdl/no-insecure-url
+ let page = await XPCShellContentUtils.loadContentPage("http://example.com/", {
+ remote: true,
+ remoteSubframes: true,
+ });
+
+ let { SpecialPowers, browsingContext } = page;
+
+ let result = await SpecialPowers.spawn(
+ browsingContext,
+ ["#span"],
+ selector => {
+ let elem = content.document.querySelector(selector);
+ return elem.textContent;
+ }
+ );
+
+ equal(result, "Hello there.", "Got correct element text from frame");
+
+ let line = Components.stack.lineNumber + 1;
+ let callback = () => {
+ let e = new Error("Hello.");
+ return { filename: e.fileName, lineNumber: e.lineNumber };
+ };
+
+ let loc = await SpecialPowers.spawn(browsingContext, [], callback);
+ equal(
+ loc.filename,
+ Components.stack.filename,
+ "Error should have correct script filename"
+ );
+ equal(
+ loc.lineNumber,
+ line + 2,
+ "Error should have correct script line number"
+ );
+
+ await page.close();
+});
diff --git a/testing/mochitest/tests/Harness_sanity/test_SpecialPowersSpawnChrome.html b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersSpawnChrome.html
new file mode 100644
index 0000000000..9c3f1ee658
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersSpawnChrome.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for SpecialPowers.spawnChrome</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<script>
+ add_task(async function() {
+ let { browsingContextId, innerWindowId } = await SpecialPowers.spawnChrome([12, { b: 42 }], (a, b) => {
+ Assert.equal(a, 12, "Arg 1");
+ Assert.equal(b.b, 42, "Arg 2");
+
+ Assert.equal(Services.appinfo.processType,
+ Services.appinfo.PROCESS_TYPE_DEFAULT,
+ "Task runs in correct process");
+
+ return {
+ browsingContextId: browsingContext.id,
+ innerWindowId: windowGlobalParent.innerWindowId,
+ };
+ });
+
+ let wgc = SpecialPowers.wrap(window).windowGlobalChild;
+ is(browsingContextId, wgc.browsingContext.id, "Correct browsing context id");
+ is(innerWindowId, wgc.innerWindowId, "Correct inner window id");
+ });
+</script>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_TestsRunningAfterSimpleTestFinish.html b/testing/mochitest/tests/Harness_sanity/test_TestsRunningAfterSimpleTestFinish.html
new file mode 100644
index 0000000000..aa0a7d39ef
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_TestsRunningAfterSimpleTestFinish.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for whether SimpLeTest.ok after SimpleTest.finish is causing an error to be logged</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<div id="content" class="testbody">
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(function() {
+ ok(true, "This should pass");
+ SimpleTest.finish();
+ });
+ window.onbeforeunload = function() {
+ ok(true, "This should cause failures in the harness, because it's run after SimpleTest.finish()");
+ }
+ </script>
+</div>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_bug649012.html b/testing/mochitest/tests/Harness_sanity/test_bug649012.html
new file mode 100644
index 0000000000..8807aa56f3
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_bug649012.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=649012
+-->
+<head>
+ <title>Test for Bug 649012</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=649012">Mozilla Bug 649012</a>
+<p id="display"></p>
+<div id="content">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 649012 **/
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+ // Test that setTimeout(f, 0) doesn't raise an error
+ setTimeout(function() {
+ // Test that setTimeout(f, t) where t > 0 doesn't raise an error if we've used
+ // SimpleTest.requestFlakyTimeout
+ SimpleTest.requestFlakyTimeout("Just testing to make sure things work. I would never do this in real life of course!");
+ setTimeout(function() {
+ SimpleTest.finish();
+ }, 1);
+ }, 0);
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_createFiles.html b/testing/mochitest/tests/Harness_sanity/test_createFiles.html
new file mode 100644
index 0000000000..0e0637a230
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_createFiles.html
@@ -0,0 +1,91 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for SpecialPowers.createFiles</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<div id="content" class="testbody">
+ <script type="text/javascript">
+ // Creating one file, followed by failing to create a file.
+ function test1() {
+ const fileType = "some file type";
+ let fdata = "this is same data for a file";
+ SpecialPowers.createFiles([{name: "test1.txt", data:fdata, options:{type:fileType}}],
+ function (files) {
+ is(files.length, 1, "Created 1 file");
+ let f = files[0];
+ is("[object File]", f.toString(), "first thing in array is a file");
+ is(f.size, fdata.length, "test1 size of first file should be length of its data");
+ is("test1.txt", f.name, "test1 test file should have the right name");
+ is(f.type, fileType, "File should have the specified type");
+ test2();
+ },
+ function (msg) { ok(false, "Should be able to create a file without an error"); test2(); }
+ );
+ }
+
+ // Failing to create a file, followed by creating a file.
+ function test2() {
+ function test3Check(passed) {
+ ok(passed, "Should trigger the error handler for a bad file name.");
+ test3();
+ };
+
+ SpecialPowers.createFiles([{name: "/\/\/\/\/\/\/\/\/\/\/\invalidname",}],
+ function () { test3Check(false); },
+ function (msg) { test3Check(true); }
+ );
+ }
+
+ // Creating two files at the same time.
+ function test3() {
+ let f1data = "hello";
+ SpecialPowers.createFiles([{name: "test3_file.txt", data:f1data}, {name: "emptyfile.txt"}],
+ function (files) {
+ is(files.length, 2, "Expected two files to be created");
+ let f1 = files[0];
+ let f2 = files[1];
+ is("[object File]", f1.toString(), "first thing in array is a file");
+ is("[object File]", f2.toString(), "second thing in array is a file");
+ is("test3_file.txt", f1.name, "first test3 test file should have the right name");
+ is("emptyfile.txt", f2.name, "second test3 test file should have the right name");
+ is(f1.size, f1data.length, "size of first file should be length of its data");
+ is(f2.size, 0, "size of second file should be 0");
+ test4();
+ },
+ function (msg) {
+ ok(false, "Failed to create files: " + msg);
+ test4();
+ }
+ );
+ };
+
+ // Creating a file without specifying a name should work.
+ function test4() {
+ let fdata = "this is same data for a file";
+ SpecialPowers.createFiles([{data:fdata}],
+ function (files) {
+ is(files.length, 1, "Created 1 file");
+ let f = files[0];
+ is("[object File]", f.toString(), "first thing in array is a file");
+ is(f.size, fdata.length, "test4 size of first file should be length of its data");
+ ok(f.name, "test4 test file should have a name");
+ SimpleTest.finish();
+ },
+ function (msg) {
+ ok(false, "Should be able to create a file without a name without an error");
+ SimpleTest.finish();
+ }
+ );
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ test1();
+
+ </script>
+</div>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_getweakmapkeys.html b/testing/mochitest/tests/Harness_sanity/test_getweakmapkeys.html
new file mode 100644
index 0000000000..58722d977f
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_getweakmapkeys.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for SpecialPowers.nondeterministicGetWeakMapKeys</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<div id="content" class="testbody">
+ <script type="text/javascript">
+ var emptyMap = new WeakMap;
+ is(SpecialPowers.nondeterministicGetWeakMapKeys(emptyMap).length, 0, "Empty map has no keys");
+ var twoMap = new WeakMap;
+ var x = {};
+ var y = {};
+ twoMap.set(x, 1);
+ twoMap.set(y, 2);
+ var twoMapKeys = SpecialPowers.nondeterministicGetWeakMapKeys(twoMap);
+ is(twoMapKeys.length, 2, "Map with two things should have two keys");
+ ok(twoMapKeys[0] == x || twoMapKeys[1] == x, "One of the keys should be x");
+ ok(twoMapKeys[0] == y || twoMapKeys[1] == y, "One of the keys should be y");
+ </script>
+</div>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_sanity.html b/testing/mochitest/tests/Harness_sanity/test_sanity.html
new file mode 100644
index 0000000000..e40ccca35b
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_sanity.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for mochitest harness sanity</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
+<p id="display">
+ <input id="testKeyEvent1" onkeypress="press1 = true">
+ <input id="testKeyEvent2" onkeydown="return false;" onkeypress="press2 = true">
+ <input id="testKeyEvent3" onkeypress="press3 = true">
+ <input id="testKeyEvent4" onkeydown="return false;" onkeypress="press4 = true">
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for sanity **/
+ok(true, "true must be ok");
+isnot(1, true, "1 must not be true");
+isnot(1, false, "1 must not be false");
+isnot(0, false, "0 must not be false");
+isnot(0, true, "0 must not be true");
+isnot("", 0, "Empty string must not be 0");
+isnot("1", 1, "Numeric string must not equal the number");
+isnot("", null, "Empty string must not be null");
+isnot(undefined, null, "Undefined must not be null");
+
+var press1 = false;
+$("testKeyEvent1").focus();
+sendString("x");
+is($("testKeyEvent1").value, "x", "synthesizeKey should work");
+is(press1, true, "synthesizeKey should dispatch keyPress");
+
+var press2 = false;
+$("testKeyEvent2").focus();
+sendString("x");
+is($("testKeyEvent2").value, "", "synthesizeKey should respect keydown preventDefault");
+is(press2, false, "synthesizeKey should not dispatch keyPress with default prevented");
+
+var press3 = false;
+$("testKeyEvent3").focus();
+sendChar("x")
+is($("testKeyEvent3").value, "x", "sendChar should work");
+is(press3, true, "sendChar should dispatch keyPress");
+
+var press4 = false;
+$("testKeyEvent4").focus();
+sendChar("x")
+is($("testKeyEvent4").value, "", "sendChar should respect keydown preventDefault");
+is(press4, false, "sendChar should not dispatch keyPress with default prevented");
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_sanityEventUtils.html b/testing/mochitest/tests/Harness_sanity/test_sanityEventUtils.html
new file mode 100644
index 0000000000..b24251b227
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_sanityEventUtils.html
@@ -0,0 +1,694 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Profiling test suite for EventUtils</title>
+ <script type="text/javascript">
+ var start = new Date();
+ </script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript">
+ var loadTime = new Date();
+ </script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="starttest()">
+<input type="radio" id="radioTarget1" name="group">Radio Target 1</input>
+<input id="textBoxA">
+<input id="textBoxB">
+<input id="testMouseEvent" type="button" value="click">
+<input id="testKeyEvent" >
+<input id="testStrEvent" >
+<div id="scrollB" style="width: 190px;height: 250px;overflow:auto">
+<p>blah blah blah blah</p>
+<p>blah blah blah blah</p>
+<p>blah blah blah blah</p>
+<p>blah blah blah blah</p>
+<p>blah blah blah blah</p>
+<p>blah blah blah blah</p>
+<p>blah blah blah blah</p>
+<p>blah blah blah blah</p>
+</div>
+<script class="testbody" type="text/javascript">
+info("\nProfile::EventUtilsLoadTime: " + (loadTime - start) + "\n");
+function starttest() {
+ SimpleTest.waitForFocus(
+ async function () {
+ SimpleTest.waitForExplicitFinish();
+ var startTime = new Date();
+ var check = false;
+ function doCheck() {
+ check = true;
+ }
+
+ const kIsHeadless = await SpecialPowers.spawnChrome([], () => {
+ return Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo).isHeadless;
+ });
+
+ if (navigator.appVersion.includes("Android")) {
+ // This is the workaround for test failure on debug build.
+ await SpecialPowers.pushPrefEnv({set: [["formhelper.autozoom.force-disable.test-only", true]]});
+ }
+
+ /* test send* functions */
+ $("testMouseEvent").addEventListener("click", doCheck, {once: true});
+ sendMouseEvent({type:'click'}, "testMouseEvent");
+ is(check, true, 'sendMouseEvent should dispatch click event');
+
+ await (async function testSynthesizeNativeMouseEvent() {
+ let events = [];
+ let listener = event => events.push(event);
+ let preventDefault = event => event.preventDefault();
+ // promiseNativeMouseEventAndWaitForEvent uses capturing listeners
+ // internally, so use capturing listeners also here.
+ $("testMouseEvent").addEventListener("mousedown", listener, true);
+ $("testMouseEvent").addEventListener("mouseup", listener, true);
+ // Clicking with modifiers may open context menu so that we should prevent to open it.
+ window.addEventListener("contextmenu", preventDefault, { capture: true });
+ for (const test of [
+ {
+ description: "ShiftLeft",
+ modifiers: { shiftKey: true },
+ },
+ {
+ description: "ShiftRight",
+ modifiers: { shiftRightKey: true },
+ },
+ {
+ description: "CtrlLeft",
+ modifiers: { ctrlKey: true },
+ },
+ {
+ description: "CtrlRight",
+ modifiers: { ctrlRightKey: true },
+ },
+ {
+ description: "AltLeft",
+ modifiers: { altKey: true },
+ },
+ {
+ description: "AltRight",
+ modifiers: { altRightKey: true },
+ },
+ {
+ description: "MetaLeft",
+ modifiers: { metaKey: true },
+ skip: () => {
+ // We've not supported "Meta" as Windows logo key or Super/Hyper keys.
+ return navigator.platform.includes("Win") || navigator.platform.includes("Linux");
+ },
+ },
+ {
+ description: "MetaRight",
+ modifiers: { metaRightKey: true },
+ skip: () => {
+ // We've not supported "Meta" as Windows logo key or Super/Hyper keys.
+ return navigator.platform.includes("Win") || navigator.platform.includes("Linux");
+ },
+ },
+ {
+ description: "CapsLock",
+ modifiers: { capsLockKey: true },
+ },
+ {
+ description: "NumLock",
+ modifiers: { numLockKey: true },
+ skip: () => {
+ // macOS does not have `NumLock` key nor state.
+ return navigator.platform.includes("Mac");
+ },
+ },
+ {
+ description: "Ctrl+Shift",
+ modifiers: { ctrlKey: true, shiftKey: true },
+ skip: () => {
+ // We forcibly open context menu on macOS so the following test
+ // will fail to receive mouse events.
+ return navigator.platform.includes("Mac");
+ },
+ },
+ {
+ description: "Alt+Shift",
+ modifiers: { altKey: true, shiftKey: true },
+ },
+ {
+ description: "Meta+Shift",
+ modifiers: { metaKey: true, shiftKey: true },
+ skip: () => {
+ // We've not supported "Meta" as Windows logo key or Super/Hyper keys.
+ return navigator.platform.includes("Win") || navigator.platform.includes("Linux");
+ },
+ },
+ ]) {
+ if (test.skip && test.skip()) {
+ continue;
+ }
+ events = [];
+ info(`testSynthesizeNativeMouseEvent: sending native mouse click (${test.description})`);
+ await promiseNativeMouseEventAndWaitForEvent({
+ type: "click",
+ target: $("testMouseEvent"),
+ atCenter: true,
+ modifiers: test.modifiers,
+ eventTypeToWait: "mouseup",
+ });
+ is(events.length, 2,
+ `testSynthesizeNativeMouseEvent: a pair of "mousedown" and "mouseup" events should be fired (${test.description})`);
+ is(events[0]?.type, "mousedown",
+ `testSynthesizeNativeMouseEvent: "mousedown" should be fired (${test.description})`);
+ is(events[1]?.type, "mouseup",
+ `testSynthesizeNativeMouseEvent: "mouseup" should be fired (${test.description})`);
+ if (events.length !== 2) {
+ continue;
+ }
+ for (const mod of [{ keyName: "Alt", propNames: [ "altKey", "altRightKey" ]},
+ { keyName: "Control", propNames: [ "ctrlKey", "ctrlRightKey" ]},
+ { keyName: "Shift", propNames: [ "shiftKey", "shiftRightKey" ]},
+ { keyName: "Meta", propNames: [ "metaKey", "metaRightKey" ]},
+ { keyName: "CapsLock", propNames: [ "capsLockKey" ]},
+ { keyName: "NumLock", propNames: [ "numLockKey" ]},
+ ]) {
+ const activeExpected =
+ (test.modifiers.hasOwnProperty(mod.propNames[0]) &&
+ test.modifiers[mod.propNames[0]]) ||
+ (mod.propNames.length !== 1 &&
+ test.modifiers.hasOwnProperty(mod.propNames[1]) &&
+ test.modifiers[mod.propNames[1]]);
+ const checkFn = activeExpected && (
+ // Bug 1693240: We don't support setting modifiers while posting a mouse event on Windows.
+ navigator.platform.includes("Win") ||
+ // Bug 1693237: We don't support setting modifiers on Android.
+ navigator.appVersion.includes("Android") ||
+ // In Headless mode, modifiers are not supported by this kind of APIs.
+ kIsHeadless) ? todo_is : is;
+ checkFn(events[0]?.getModifierState(mod.keyName), activeExpected,
+ `testSynthesizeNativeMouseEvent: "mousedown".getModifierState("${mod.keyName}") should return ${activeExpected} (${test.description}`);
+ checkFn(events[1]?.getModifierState(mod.keyName), activeExpected,
+ `testSynthesizeNativeMouseEvent: "mouseup".getModifierState("${mod.keyName}") should return ${activeExpected} (${test.description}`);
+ }
+ }
+ const supportsX1AndX2Buttons =
+ // On Windows, it triggers APP_COMMAND. Therefore, this test is unloaded.
+ !navigator.platform.includes("Win") &&
+ // On macOS, it seems that no API to specify X1 and X2 button at creating an NSEvent.
+ !navigator.platform.includes("Mac") &&
+ // On Linux, it seems that X1 button and X2 button events are not synthesized correctly.
+ !navigator.platform.includes("Linux");
+ for (let i = 0; i < (supportsX1AndX2Buttons ? 5 : 3); i++) {
+ events = [];
+ info(`testSynthesizeNativeMouseEvent: sending native mouse click (button=${i})`);
+ await promiseNativeMouseEventAndWaitForEvent({
+ type: "click",
+ target: $("testMouseEvent"),
+ atCenter: true,
+ button: i,
+ eventTypeToWait: "mouseup",
+ });
+ is(events.length, 2,
+ `testSynthesizeNativeMouseEvent: a pair of "mousedown" and "mouseup" events should be fired (button=${i})`);
+ is(events[0]?.type, "mousedown",
+ `testSynthesizeNativeMouseEvent: "mousedown" should be fired (button=${i})`);
+ is(events[1]?.type, "mouseup",
+ `testSynthesizeNativeMouseEvent: "mouseup" should be fired (button=${i})`);
+ if (events.length !== 2) {
+ continue;
+ }
+ is(events[0].button, i,
+ `testSynthesizeNativeMouseEvent: button of "mousedown" event should be ${i}`);
+ is(events[1].button, i,
+ `testSynthesizeNativeMouseEvent: button of "mouseup" event should be ${i}`);
+ }
+ $("testMouseEvent").removeEventListener("mousedown", listener);
+ $("testMouseEvent").removeEventListener("mouseup", listener);
+ window.removeEventListener("contextmenu", preventDefault, { capture: true });
+ })();
+
+ check = false;
+ $("testKeyEvent").addEventListener("keypress", doCheck, {once: true});
+ $("testKeyEvent").focus();
+ sendChar("x");
+ is($("testKeyEvent").value, "x", "sendChar should work");
+ is(check, true, "sendChar should dispatch keyPress");
+ $("testKeyEvent").value = "";
+
+ $("testStrEvent").focus();
+ sendString("string");
+ is($("testStrEvent").value, "string", "sendString should work");
+ $("testStrEvent").value = "";
+
+ var keydown = false;
+ var keypress = false;
+ $("testKeyEvent").focus();
+ $("testKeyEvent").addEventListener("keydown", function() { keydown = true; }, {once: true});
+ $("testKeyEvent").addEventListener("keypress", function() { keypress = true; }, {once: true});
+ sendKey("DOWN");
+ ok(keydown, "sendKey should dispatch keyDown");
+ ok(!keypress, "sendKey shouldn't dispatch keyPress for non-printable key");
+
+ /* test synthesizeMouse* */
+ //focus trick enables us to run this in iframes
+ $("radioTarget1").addEventListener('focus', function (aEvent) {
+ synthesizeMouse($("radioTarget1"), 1, 1, {});
+ is($("radioTarget1").checked, true, "synthesizeMouse should work")
+ $("radioTarget1").checked = false;
+ disableNonTestMouseEvents(true);
+ synthesizeMouse($("radioTarget1"), 1, 1, {});
+ is($("radioTarget1").checked, true, "synthesizeMouse should still work with non-test mouse events disabled");
+ $("radioTarget1").checked = false;
+ disableNonTestMouseEvents(false);
+ }, {once: true});
+ $("radioTarget1").focus();
+
+ //focus trick enables us to run this in iframes
+ $("textBoxA").addEventListener("focus", function (aEvent) {
+ check = false;
+ $("textBoxA").addEventListener("click", function() { check = true; }, { once: true });
+ synthesizeMouseAtCenter($("textBoxA"), {});
+ is(check, true, 'synthesizeMouse should dispatch mouse event');
+
+ check = false;
+ $("scrollB").addEventListener("click", function() { check = true; }, { once: true });
+ synthesizeMouseExpectEvent($("scrollB"), 1, 1, {}, $("scrollB"), "click", "synthesizeMouseExpectEvent should fire click event");
+ is(check, true, 'synthesizeMouse should dispatch mouse event');
+ }, {once: true});
+ $("textBoxA").focus();
+
+ /**
+ * TODO: testing synthesizeWheel requires a setTimeout
+ * since there is delay between the scroll event and a check, so for now just test
+ * that we can successfully call it to avoid having setTimeout vary the runtime metric.
+ * Testing of this method is currently done here:
+ * toolkit/content/tests/chrome/test_mousescroll.xul
+ */
+ synthesizeWheel($("scrollB"), 5, 5, {'deltaY': 10.0, deltaMode: WheelEvent.DOM_DELTA_LINE});
+
+ /* test synthesizeKey* */
+ check = false;
+ $("testKeyEvent").addEventListener("keypress", doCheck, {once:true});
+ $("testKeyEvent").focus();
+ sendString("a");
+ is($("testKeyEvent").value, "a", "synthesizeKey should work");
+ is(check, true, "synthesizeKey should dispatch keyPress");
+ $("testKeyEvent").value = "";
+
+ // If |.code| value is not specified explicitly, it should be computed
+ // from the |.key| value or |.keyCode| value. If a printable key is
+ // specified, the |.code| value should be guessed with US-English
+ // keyboard layout.
+ for (let test of [{ arg: "KEY_Enter", code: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN },
+ { arg: "VK_RETURN", code: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN },
+ { arg: "KEY_Backspace", code: "Backspace", keyCode: KeyboardEvent.DOM_VK_BACK_SPACE },
+ { arg: "KEY_Delete", code: "Delete", keyCode: KeyboardEvent.DOM_VK_DELETE },
+ { arg: "KEY_Home", code: "Home", keyCode: KeyboardEvent.DOM_VK_HOME },
+ { arg: "KEY_End", code: "End", keyCode: KeyboardEvent.DOM_VK_END },
+ { arg: "KEY_ArrowDown", code: "ArrowDown", keyCode: KeyboardEvent.DOM_VK_DOWN },
+ { arg: "KEY_ArrowUp", code: "ArrowUp", keyCode: KeyboardEvent.DOM_VK_UP },
+ { arg: "KEY_ArrowLeft", code: "ArrowLeft", keyCode: KeyboardEvent.DOM_VK_LEFT },
+ { arg: "KEY_ArrowRight", code: "ArrowRight", keyCode: KeyboardEvent.DOM_VK_RIGHT },
+ { arg: "KEY_Shift", code: "ShiftLeft", keyCode: KeyboardEvent.DOM_VK_SHIFT },
+ { arg: "KEY_Control", code: "ControlLeft", keyCode: KeyboardEvent.DOM_VK_CONTROL },
+ { arg: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A },
+ { arg: "B", code: "KeyB", keyCode: KeyboardEvent.DOM_VK_B },
+ { arg: " ", code: "Space", keyCode: KeyboardEvent.DOM_VK_SPACE },
+ { arg: "0", code: "Digit0", keyCode: KeyboardEvent.DOM_VK_0 },
+ { arg: "(", code: "Digit9", keyCode: KeyboardEvent.DOM_VK_9 },
+ { arg: "!", code: "Digit1", keyCode: KeyboardEvent.DOM_VK_1 },
+ { arg: "[", code: "BracketLeft", keyCode: KeyboardEvent.DOM_VK_OPEN_BRACKET },
+ { arg: ";", code: "Semicolon", keyCode: KeyboardEvent.DOM_VK_SEMICOLON },
+ { arg: "\"", code: "Quote", keyCode: KeyboardEvent.DOM_VK_QUOTE },
+ { arg: "~", code: "Backquote", keyCode: KeyboardEvent.DOM_VK_BACK_QUOTE },
+ { arg: "<", code: "Comma", keyCode: KeyboardEvent.DOM_VK_COMMA },
+ { arg: ".", code: "Period", keyCode: KeyboardEvent.DOM_VK_PERIOD }]) {
+ let testKeydown, keyup;
+ $("testKeyEvent").focus();
+ $("testKeyEvent").addEventListener("keydown", (e) => { testKeydown = e; }, {once: true});
+ $("testKeyEvent").addEventListener("keyup", (e) => { keyup = e; }, {once: true});
+ synthesizeKey(test.arg);
+ is(testKeydown.code, test.code, `Synthesizing "${test.arg}" should set code value of "keydown" to "${test.code}"`);
+ is(testKeydown.keyCode, test.keyCode, `Synthesizing "${test.arg}" should set keyCode value of "keydown" to "${test.keyCode}"`);
+ is(keyup.code, test.code, `Synthesizing "${test.arg}" key should set code value of "keyup" to "${test.code}"`);
+ is(keyup.keyCode, test.keyCode, `Synthesizing "${test.arg}" key should set keyCode value of "keyup" to "${test.keyCode}"`);
+ $("testKeyEvent").value = "";
+ }
+
+ /* test synthesizeComposition */
+ var description = "";
+ var keydownEvent = null;
+ var keyupEvent = null;
+ function onKeyDown(aEvent) {
+ ok(!keydownEvent, description + "keydown should be fired only once" + (keydownEvent ? keydownEvent.key : "") + ", " + (keyupEvent ? keyupEvent.key : ""));
+ keydownEvent = aEvent;
+ }
+ function onKeyUp(aEvent) {
+ ok(!keyupEvent, description + "keyup should be fired only once");
+ keyupEvent = aEvent;
+ }
+ function resetKeyDownAndKeyUp(aDescription) {
+ description = aDescription + ": ";
+ keydownEvent = null;
+ keyupEvent = null;
+ check = false;
+ }
+ function checkKeyDownAndKeyUp(aKeyDown, aKeyUp) {
+ if (aKeyDown) {
+ is(keydownEvent.keyCode, aKeyDown.keyCode,
+ description + "keydown event should be dispatched (checking keyCode)");
+ is(keydownEvent.key, aKeyDown.key,
+ description + "keydown event should be dispatched (checking key)");
+ } else {
+ is(keydownEvent, null,
+ description + "keydown event shouldn't be fired");
+ }
+ if (aKeyUp) {
+ is(keyupEvent.keyCode, aKeyUp.keyCode,
+ description + "keyup event should be dispatched (checking keyCode)");
+ is(keyupEvent.key, aKeyUp.key,
+ description + "keyup event should be dispatched (checking key)");
+ } else {
+ is(keyupEvent, null,
+ description + "keyup event shouldn't be fired");
+ }
+ }
+ $("textBoxB").addEventListener("keydown", onKeyDown);
+ $("textBoxB").addEventListener("keyup", onKeyUp);
+
+ $("textBoxB").focus();
+
+ // If key event is not specified, fake keydown and keyup events which are
+ // marked as "processed by IME" should be fired.
+ resetKeyDownAndKeyUp("synthesizing eCompositionStart without specifying keyboard event");
+ window.addEventListener("compositionstart", doCheck, {once: true});
+ synthesizeComposition({type: "compositionstart"});
+ ok(check, description + "synthesizeComposition() should dispatch compositionstart");
+ checkKeyDownAndKeyUp({inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
+ {inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
+
+ resetKeyDownAndKeyUp("trying to synthesize eCompositionUpdate directly without specifying keyboard event");
+ window.addEventListener("compositionupdate", doCheck, {once: true});
+ synthesizeComposition({type: "compositionupdate", data: "a"});
+ ok(!check, description + "synthesizeComposition() should not dispatch compositionupdate without error");
+ checkKeyDownAndKeyUp(null, null);
+
+ resetKeyDownAndKeyUp("synthesizing eCompositionChange without specifying keyboard event");
+ window.addEventListener("text", doCheck, {once: true});
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "a",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ }
+ );
+ ok(check, description + "synthesizeCompositionChange should cause dispatching a DOM text event");
+ checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
+ {inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
+
+ resetKeyDownAndKeyUp("synthesizing eCompositionChange for removing clauses without specifying keyboard event");
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "a",
+ "clauses":
+ [
+ { "length": 0, "attr": 0 }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ }
+ );
+ checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
+ {inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
+
+ resetKeyDownAndKeyUp("trying to synthesize eCompositionEnd directly without specifying keyboard event");
+ window.addEventListener("compositionend", doCheck, {once: true});
+ synthesizeComposition({type: "compositionend", data: "a"});
+ ok(!check, description + "synthesizeComposition() should not dispatch compositionend");
+ checkKeyDownAndKeyUp(null, null);
+
+ resetKeyDownAndKeyUp("synthesizing eCompositionCommit without specifying keyboard event");
+ synthesizeComposition({type: "compositioncommit", data: "a"});
+ ok(check, description + "synthesizeComposition() should dispatch compositionend");
+ checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
+ {inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
+
+ var querySelectedText = synthesizeQuerySelectedText();
+ ok(querySelectedText, "query selected text event result is null");
+ ok(querySelectedText.succeeded, "query selected text event failed");
+ is(querySelectedText.offset, 1,
+ "query selected text event returns wrong offset");
+ is(querySelectedText.text, "",
+ "query selected text event returns wrong selected text");
+ $("textBoxB").value = "";
+
+ querySelectedText = synthesizeQuerySelectedText();
+ ok(querySelectedText, "query selected text event result is null");
+ ok(querySelectedText.succeeded, "query selected text event failed");
+ is(querySelectedText.offset, 0,
+ "query selected text event returns wrong offset");
+ is(querySelectedText.text, "",
+ "query selected text event returns wrong selected text");
+ var endTime = new Date();
+ info("\nProfile::EventUtilsRunTime: " + (endTime-startTime) + "\n");
+
+ // In most cases, automated tests shouldn't try to synthesize
+ // compositionstart manually. Let's check if synthesizeCompositionChange()
+ // dispatches compositionstart automatically.
+ resetKeyDownAndKeyUp("synthesizing eCompositionChange without specifying keyboard event when there is no composition");
+ window.addEventListener("compositionstart", doCheck, {once: true});
+ synthesizeCompositionChange(
+ { "composition":
+ { "string": "a",
+ "clauses":
+ [
+ { "length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ "caret": { "start": 1, "length": 0 }
+ }
+ );
+ ok(check, description + "synthesizeCompositionChange should dispatch \"compositionstart\" automatically if there is no composition");
+ checkKeyDownAndKeyUp({inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
+ {inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
+
+ resetKeyDownAndKeyUp("synthesizing eCompositionCommitAsIs without specifying keyboard event");
+ synthesizeComposition({type: "compositioncommitasis"});
+ checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
+ {inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
+
+ // If key event is specified, keydown event which is marked as "processed
+ // by IME" should be fired and keyup event which is NOT marked as so
+ // should be fired too.
+ resetKeyDownAndKeyUp("synthesizing eCompositionStart with specifying keyboard event");
+ synthesizeComposition({type: "compositionstart", key: {key: "a"}});
+ checkKeyDownAndKeyUp({inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
+ {inComposition: true, keyCode: KeyboardEvent.DOM_VK_A, key: "a"});
+
+ resetKeyDownAndKeyUp("synthesizing eCompositionChange with specifying keyboard event");
+ synthesizeCompositionChange(
+ {"composition":
+ {"string": "b", "clauses": [
+ {"length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE}
+ ]},
+ "caret": {"start": 1, "length": 0},
+ "key": {key: "b"},
+ }
+ );
+ checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
+ {inComposition: true, keyCode: KeyboardEvent.DOM_VK_B, key: "b"});
+
+ resetKeyDownAndKeyUp("synthesizing eCompositionCommit with specifying keyboard event");
+ synthesizeComposition({type: "compositioncommit", data: "c", key: {key: "KEY_Enter"}});
+ checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
+ {inComposition: false, keyCode: KeyboardEvent.DOM_VK_RETURN, key: "Enter"});
+
+ // keyup shouldn't be dispatched automatically if type is specified as keydown
+ resetKeyDownAndKeyUp("synthesizing eCompositionStart with specifying keyboard event whose type is keydown");
+ synthesizeComposition({type: "compositionstart", key: {key: "a", type: "keydown"}});
+ checkKeyDownAndKeyUp({inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
+ null);
+
+ resetKeyDownAndKeyUp("synthesizing eCompositionChange with specifying keyboard event whose type is keydown");
+ synthesizeCompositionChange(
+ {"composition":
+ {"string": "b", "clauses": [
+ {"length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE}
+ ]},
+ "caret": {"start": 1, "length": 0},
+ "key": {key: "b", type: "keydown"},
+ }
+ );
+ checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
+ null);
+
+ resetKeyDownAndKeyUp("synthesizing eCompositionCommit with specifying keyboard event whose type is keydown");
+ synthesizeComposition({type: "compositioncommit", data: "c", key: {key: "KEY_Enter", type: "keydown"}});
+ checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
+ null);
+
+ // keydown shouldn't be dispatched automatically if type is specified as keyup
+ resetKeyDownAndKeyUp("synthesizing eCompositionStart with specifying keyboard event whose type is keyup");
+ synthesizeComposition({type: "compositionstart", key: {key: "a", type: "keyup"}});
+ checkKeyDownAndKeyUp(null,
+ {inComposition: true, keyCode: KeyboardEvent.DOM_VK_A, key: "a"});
+
+ resetKeyDownAndKeyUp("synthesizing eCompositionChange with specifying keyboard event whose type is keyup");
+ synthesizeCompositionChange(
+ {"composition":
+ {"string": "b", "clauses": [
+ {"length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE}
+ ]},
+ "caret": {"start": 1, "length": 0},
+ "key": {key: "b", type: "keyup"},
+ }
+ );
+ checkKeyDownAndKeyUp(null,
+ {inComposition: true, keyCode: KeyboardEvent.DOM_VK_B, key: "b"});
+
+ resetKeyDownAndKeyUp("synthesizing eCompositionCommit with specifying keyboard event whose type is keyup");
+ synthesizeComposition({type: "compositioncommit", data: "c", key: {key: "KEY_Enter", type: "keyup"}});
+ checkKeyDownAndKeyUp(null,
+ {inComposition: false, keyCode: KeyboardEvent.DOM_VK_RETURN, key: "Enter"});
+
+ // keydown event shouldn't be marked as "processed by IME" if doNotMarkKeydownAsProcessed is true
+ resetKeyDownAndKeyUp("synthesizing eCompositionStart with specifying keyboard event whose doNotMarkKeydownAsProcessed is true");
+ synthesizeComposition({type: "compositionstart", key: {key: "a", doNotMarkKeydownAsProcessed: true}});
+ checkKeyDownAndKeyUp({inComposition: false, keyCode: KeyboardEvent.DOM_VK_A, key: "a"},
+ {inComposition: true, keyCode: KeyboardEvent.DOM_VK_A, key: "a"});
+
+ resetKeyDownAndKeyUp("synthesizing eCompositionChange with specifying keyboard event whose doNotMarkKeydownAsProcessed is true");
+ synthesizeCompositionChange(
+ {"composition":
+ {"string": "b", "clauses": [
+ {"length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE}
+ ]},
+ "caret": {"start": 1, "length": 0},
+ "key": {key: "b", doNotMarkKeydownAsProcessed: true},
+ }
+ );
+ checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_B, key: "b"},
+ {inComposition: true, keyCode: KeyboardEvent.DOM_VK_B, key: "b"});
+
+ resetKeyDownAndKeyUp("synthesizing eCompositionCommit with specifying keyboard event whose doNotMarkKeydownAsProcessed is true");
+ synthesizeComposition({type: "compositioncommit", data: "c", key: {key: "KEY_Enter", doNotMarkKeydownAsProcessed: true}});
+ checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_RETURN, key: "Enter"},
+ {inComposition: false, keyCode: KeyboardEvent.DOM_VK_RETURN, key: "Enter"});
+
+ // keyup event should be marked as "processed by IME" if markKeyupAsProcessed is true
+ resetKeyDownAndKeyUp("synthesizing eCompositionStart with specifying keyboard event whose markKeyupAsProcessed is true");
+ synthesizeComposition({type: "compositionstart", key: {key: "a", markKeyupAsProcessed: true}});
+ checkKeyDownAndKeyUp({inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
+ {inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
+
+ resetKeyDownAndKeyUp("synthesizing eCompositionChange with specifying keyboard event whose markKeyupAsProcessed is true");
+ synthesizeCompositionChange(
+ {"composition":
+ {"string": "b", "clauses": [
+ {"length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE}
+ ]},
+ "caret": {"start": 1, "length": 0},
+ "key": {key: "b", markKeyupAsProcessed: true},
+ }
+ );
+ checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
+ {inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
+
+ resetKeyDownAndKeyUp("synthesizing eCompositionCommit with specifying keyboard event whose markKeyupAsProcessed is true");
+ synthesizeComposition({type: "compositioncommit", data: "c", key: {key: "KEY_Enter", markKeyupAsProcessed: true}});
+ checkKeyDownAndKeyUp({inComposition: true, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"},
+ {inComposition: false, keyCode: KeyboardEvent.DOM_VK_PROCESSKEY, key: "Process"});
+
+ // If key event is explicitly declared with null, keyboard events shouldn't
+ // be fired for emulating text inputs without keyboard such as voice input or something.
+ resetKeyDownAndKeyUp("synthesizing eCompositionStart with specifying keyboard event as null");
+ synthesizeComposition({type: "compositionstart", key: null});
+ checkKeyDownAndKeyUp(null, null);
+
+ resetKeyDownAndKeyUp("synthesizing eCompositionChange with specifying keyboard event as null");
+ synthesizeCompositionChange(
+ {"composition":
+ {"string": "b", "clauses": [
+ {"length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE}
+ ]},
+ "caret": {"start": 1, "length": 0},
+ "key": null,
+ }
+ );
+ checkKeyDownAndKeyUp(null, null);
+
+ resetKeyDownAndKeyUp("synthesizing eCompositionCommit with specifying keyboard event as null");
+ synthesizeComposition({type: "compositioncommit", data: "c", key: null});
+ checkKeyDownAndKeyUp(null, null);
+
+ // If key event is explicitly declared with empty object, keyboard events
+ // shouldn't be fired for emulating text inputs without keyboard such as
+ // voice input or something.
+ resetKeyDownAndKeyUp("synthesizing eCompositionStart with specifying keyboard event as empty");
+ synthesizeComposition({type: "compositionstart", key: {}});
+ checkKeyDownAndKeyUp(null, null);
+
+ resetKeyDownAndKeyUp("synthesizing eCompositionChange with specifying keyboard event as empty");
+ synthesizeCompositionChange(
+ {"composition":
+ {"string": "b", "clauses": [
+ {"length": 1, "attr": COMPOSITION_ATTR_RAW_CLAUSE}
+ ]},
+ "caret": {"start": 1, "length": 0},
+ "key": {},
+ }
+ );
+ checkKeyDownAndKeyUp(null, null);
+
+ resetKeyDownAndKeyUp("synthesizing eCompositionCommit with specifying keyboard event as empty");
+ synthesizeComposition({type: "compositioncommit", data: "c", key: {}});
+ checkKeyDownAndKeyUp(null, null);
+
+ $("textBoxB").removeEventListener("keydown", onKeyDown);
+ $("textBoxB").removeEventListener("keyup", onKeyUp);
+
+
+ // Async event synthesizing.
+ // On Android this does not work.
+ if (navigator.userAgent.includes("Android")) {
+ SimpleTest.finish();
+ return;
+ }
+
+ await (async function () {
+ await SpecialPowers.pushPrefEnv({set: [["test.events.async.enabled", true]]});
+ try {
+ disableNonTestMouseEvents(true);
+ let mouseMoveCount = 0;
+ let waitForAllSynthesizedMouseMove =
+ new Promise(resolve => {
+ window.addEventListener("mousemove", function onMouseMove(aEvent) {
+ mouseMoveCount++;
+ is(aEvent.target, $("testMouseEvent"),
+ `The mousemove event target of ${
+ mouseMoveCount
+ } should be the input#testMouseEvent, but ${
+ aEvent.target.nodeName
+ }`);
+ if (mouseMoveCount === 30) {
+ window.removeEventListener("mousemove", onMouseMove, { capture: true });
+ resolve();
+ }
+ }, { capture: true });
+ });
+ for (let i = 0; i < 30; i++) {
+ synthesizeMouse($("testMouseEvent"), 3 + i % 2, 3 + i % 2, { type: "mousemove" });
+ }
+ await waitForAllSynthesizedMouseMove;
+ } finally {
+ disableNonTestMouseEvents(false);
+ }
+ })();
+
+ SimpleTest.finish();
+ }
+ );
+};
+</script>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_sanityException.html b/testing/mochitest/tests/Harness_sanity/test_sanityException.html
new file mode 100644
index 0000000000..463f51d80e
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_sanityException.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test that uncaught exceptions in mochitests cause failures</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=670817">Mozilla Bug 670817</a>
+<script>
+ok(true, "a call to ok");
+SimpleTest.expectUncaughtException();
+// eslint-disable-next-line no-throw-literal
+throw "an uncaught exception";
+</script>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_sanityException2.html b/testing/mochitest/tests/Harness_sanity/test_sanityException2.html
new file mode 100644
index 0000000000..1136b73916
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_sanityException2.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test that uncaught exceptions in mochitests cause failures</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=670817">Mozilla Bug 670817</a>
+<script>
+SimpleTest.waitForExplicitFinish();
+ok(true, "a call to ok");
+SimpleTest.executeSoon(function() {
+ SimpleTest.expectUncaughtException();
+ // eslint-disable-next-line no-throw-literal
+ throw "an uncaught exception";
+});
+SimpleTest.executeSoon(function() {
+ SimpleTest.finish();
+});
+</script>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_sanityParams.html b/testing/mochitest/tests/Harness_sanity/test_sanityParams.html
new file mode 100644
index 0000000000..192d6ef96a
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_sanityParams.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for exposing test suite information</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script class="testbody" type="text/javascript">
+ok(SimpleTest.harnessParameters, "Should have parameters.");
+</script>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_sanityRegisteredServiceWorker.html b/testing/mochitest/tests/Harness_sanity/test_sanityRegisteredServiceWorker.html
new file mode 100644
index 0000000000..f3c6d422d9
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_sanityRegisteredServiceWorker.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test that service worker registrations not cleaned up in mochitests cause failures</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<script>
+SimpleTest.waitForExplicitFinish();
+SimpleTest.expectRegisteredServiceWorker();
+SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+]}, function() {
+ navigator.serviceWorker.register("empty.js", {scope: "scope"})
+ .then(function(registration) {
+ ok(registration, "Registration succeeded");
+ SimpleTest.finish();
+ });
+});
+</script>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_sanityRegisteredServiceWorker2.html b/testing/mochitest/tests/Harness_sanity/test_sanityRegisteredServiceWorker2.html
new file mode 100644
index 0000000000..f46944c2e5
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_sanityRegisteredServiceWorker2.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test that service worker registrations not cleaned up in mochitests cause failures</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<script>
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({"set": [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true]
+]}, function() {
+ navigator.serviceWorker.getRegistration("scope")
+ .then(function(registration) {
+ ok(registration, "Registration successfully obtained");
+ return registration.unregister();
+ }).then(function() {
+ SimpleTest.finish();
+ });
+});
+</script>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_sanitySimpletest.html b/testing/mochitest/tests/Harness_sanity/test_sanitySimpletest.html
new file mode 100644
index 0000000000..2b289f1387
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_sanitySimpletest.html
@@ -0,0 +1,103 @@
+<!--This test should be updated each time new functionality is added to SimpleTest-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Profiling test suite for SimpleTest</title>
+ <script type="text/javascript">
+ var start = new Date();
+ </script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script type="text/javascript">
+ var loadTime = new Date();
+ </script>
+</head>
+<body>
+<input id="textB"/>
+<script class="testbody" type="text/javascript">
+info("Profile::SimpleTestLoadTime: " + (loadTime - start));
+var startTime = new Date();
+SimpleTest.waitForExplicitFinish();
+function starttest() {
+ SimpleTest.waitForFocus(
+ function() {
+ //test log
+ info("Logging some info")
+
+ //basic usage
+ ok(true, "test ok");
+ SimpleTest.record(true, "test ok", "diagnostic information");
+ is(0, 0, "is() test failed");
+ isnot(0, 1, "isnot() test failed");
+
+ //todo tests
+ todo(false, "test todo", "todo() test should not pass");
+ todo_is(false, true, "test todo_is");
+ todo_isnot(true, true, "test todo_isnot");
+
+ //misc
+ SimpleTest.requestLongerTimeout(1);
+
+ //note: this test may alter runtimes as it waits
+ var check = false;
+ $('textB').focus();
+ SimpleTest.waitForClipboard("a",
+ function () {
+ SpecialPowers.clipboardCopyString("a");
+ },
+ function () {
+ check = true;
+ is(check, true, "waitForClipboard should work");
+ manipulateElements();
+ },
+ function () {
+ check = false;
+ is(check, false, "waitForClipboard should work");
+ manipulateElements();
+ }
+ );
+
+ //use helper functions
+ function manipulateElements()
+ {
+ var div1 = createEl('div', {'id': 'somediv', 'display': 'block'}, "I am a div");
+ document.body.appendChild(div1);
+ var divObj = this.getElement('somediv');
+ is(divObj, div1, 'createEl did not create element as expected');
+ is($('somediv'), divObj, '$ helper did not get element as expected');
+ is(computedStyle(divObj, 'display'), 'block', 'computedStyle did not get right display value');
+ document.body.removeChild(div1);
+
+ /* note: expectChildProcessCrash is not being tested here, as it causes wildly variable
+ * run times. It is currently being tested in:
+ * dom/plugins/test/test_hanging.html and dom/plugins/test/test_crashing.html
+ */
+
+ //note: this also adds a short wait period
+ SimpleTest.executeSoon(
+ function () {
+ //finish() calls a slew of SimpleTest functions
+ SimpleTest.finish();
+ //call this after finish so we can make sure it works and doesn't hang our process
+ var endTime = new Date();
+ info("Profile::SimpleTestRunTime: " + (endTime-startTime));
+ //expect and throw exception here. Otherwise, any code that follows the throw call will never be executed
+ SimpleTest.expectUncaughtException();
+ //make sure we catch this error
+ // eslint-disable-next-line no-throw-literal
+ throw "i am an uncaught exception"
+ }
+ );
+ }
+ }
+ );
+};
+//use addLoadEvent
+addLoadEvent(
+ function() {
+ starttest();
+ }
+);
+</script>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_sanityWindowSnapshot.html b/testing/mochitest/tests/Harness_sanity/test_sanityWindowSnapshot.html
new file mode 100644
index 0000000000..6960fc2104
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_sanityWindowSnapshot.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Profiling test suite for WindowSnapshot</title>
+ <script type="text/javascript">
+ var start = new Date();
+ </script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script type="text/javascript">
+ var loadTime = new Date();
+ </script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="starttest()">
+<script class="testbody" type="text/javascript">
+info("\nProfile::WindowSnapshotLoadTime: " + (loadTime - start) + "\n");
+function starttest() {
+ SimpleTest.waitForExplicitFinish();
+ var startTime = new Date();
+ var snap = snapshotWindow(window, false);
+ var snap2 = snapshotWindow(window, false);
+ is(compareSnapshots(snap, snap2, true)[0], true, "this should be true");
+ var div1 = createEl('div', {'id': 'somediv', 'display': 'block'}, "I am a div");
+ document.body.appendChild(div1);
+ snap2 = snapshotWindow(window, false);
+ is(compareSnapshots(snap, snap2, true)[0], false, "this should be false");
+ document.body.removeChild(div1);
+ var endTime = new Date();
+ info("\nProfile::WindowSnapshotRunTime: " + (endTime-startTime) + "\n");
+ SimpleTest.finish();
+};
+</script>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_sanity_cleanup.html b/testing/mochitest/tests/Harness_sanity/test_sanity_cleanup.html
new file mode 100644
index 0000000000..644be67674
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_sanity_cleanup.html
@@ -0,0 +1,30 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>SimpleTest.registerCleanupFunction test</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script class="testbody" type="text/javascript">
+// Not a great example, since we have the pushPrefEnv API to cover
+// this use case, but I want to be able to test that the cleanup
+// function gets run, so setting and clearing a pref seems straightforward.
+function do_cleanup1() {
+ SpecialPowers.clearUserPref("simpletest.cleanup.1");
+ info("do_cleanup1 run!");
+}
+function do_cleanup2() {
+ SpecialPowers.clearUserPref("simpletest.cleanup.2");
+ info("do_cleanup2 run!");
+}
+SpecialPowers.setBoolPref("simpletest.cleanup.1", true);
+SpecialPowers.setBoolPref("simpletest.cleanup.2", true);
+SimpleTest.registerCleanupFunction(do_cleanup1);
+SimpleTest.registerCleanupFunction(do_cleanup2);
+ok(true, "dummy check");
+</script>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_sanity_cleanup2.html b/testing/mochitest/tests/Harness_sanity/test_sanity_cleanup2.html
new file mode 100644
index 0000000000..b0b7523819
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_sanity_cleanup2.html
@@ -0,0 +1,24 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>SimpleTest.registerCleanupFunction test</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script class="testbody" type="text/javascript">
+for (const pref of [1, 2]) {
+ try {
+ SpecialPowers.getBoolPref("simpletest.cleanup." + pref);
+ ok(false, "Cleanup function should have unset pref");
+ }
+ catch(ex) {
+ ok(true, "Pref was not set");
+ }
+}
+
+</script>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_sanity_manifest.html b/testing/mochitest/tests/Harness_sanity/test_sanity_manifest.html
new file mode 100644
index 0000000000..d07df21ef5
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_sanity_manifest.html
@@ -0,0 +1,16 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>SimpleTest.expected = 'fail' test</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script class="testbody" type="text/javascript">
+ok(false, "We expect this to fail");
+
+</script>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_sanity_manifest_pf.html b/testing/mochitest/tests/Harness_sanity/test_sanity_manifest_pf.html
new file mode 100644
index 0000000000..4b83fda596
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_sanity_manifest_pf.html
@@ -0,0 +1,17 @@
+<!-- Any copyright is dedicated to the Public Domain.
+ - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>SimpleTest.expected = 'fail' test</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script class="testbody" type="text/javascript">
+ok(true, "We expect this to pass");
+ok(false, "We expect this to fail");
+
+</script>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/test_sanity_waitForCondition.html b/testing/mochitest/tests/Harness_sanity/test_sanity_waitForCondition.html
new file mode 100644
index 0000000000..f059adb88d
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/test_sanity_waitForCondition.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="UTF-8">
+ <title>SimpleTest.waitForCondition test</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<script>
+
+var captureFailure = false;
+var capturedFailures = [];
+window.ok = function (cond, name) {
+ if (!captureFailure) {
+ SimpleTest.ok(cond, name);
+ } else if (cond) {
+ SimpleTest.ok(false, `Expect a failure with "${name}"`);
+ } else {
+ capturedFailures.push(name);
+ }
+};
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("test behavior SimpleTest.waitForCondition");
+
+addLoadEvent(testNormal);
+
+function testNormal() {
+ var condition = false;
+ SimpleTest.waitForCondition(() => condition, () => {
+ ok(condition, "Should only be called when condition is true");
+ SimpleTest.executeSoon(testTimeout);
+ }, "Shouldn't timeout");
+ setTimeout(() => { condition = true; }, 1000);
+}
+
+function testTimeout() {
+ captureFailure = true;
+ SimpleTest.waitForCondition(() => false, () => {
+ captureFailure = false;
+ is(capturedFailures.length, 1, "Should captured one failure");
+ is(capturedFailures[0], "Should timeout",
+ "Should capture the failure passed in");
+ SimpleTest.executeSoon(() => SimpleTest.finish());
+ }, "Should timeout");
+}
+
+</script>
+</body>
+</html>
diff --git a/testing/mochitest/tests/Harness_sanity/xpcshell.toml b/testing/mochitest/tests/Harness_sanity/xpcshell.toml
new file mode 100644
index 0000000000..f203c1ef9f
--- /dev/null
+++ b/testing/mochitest/tests/Harness_sanity/xpcshell.toml
@@ -0,0 +1,5 @@
+[DEFAULT]
+
+["test_SpecialPowersSandbox.js"]
+
+["test_SpecialPowersSpawn.js"]