Bug 1541557: Part 4 - Stop relying on synchronous preference getters/setters. r=nika
☠☠ backed out by f9bf5e4b0b4f ☠ ☠
authorKris Maglione <maglione.k@gmail.com>
Thu, 13 Jun 2019 09:34:39 -0700
changeset 540538 189dc8a359815e059a4a217f788d183260bb2bfe
parent 540537 b4ed40bea2698802ef562a0931c0b560737fb89d
child 540539 75ebd6fce136ab3bd0e591c2b8b2d06d3b5bf923
push id11529
push userarchaeopteryx@coole-files.de
push dateThu, 04 Jul 2019 15:22:33 +0000
treeherdermozilla-beta@ebb510a784b8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnika
bugs1541557
milestone69.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1541557: Part 4 - Stop relying on synchronous preference getters/setters. r=nika The SpecialPowers set*Pref/get*Pref APIs currently use synchronous messaging to set and get preference values from the parent process. Aside from directly affecting callers of those APIs, it also affects callers of `pushPrefEnv`, which is meant to be asynchronous, but is in practice usually synchronous due to the synchronous messaging it uses. This patch updates the getPref APIs to use the in-process preference service (which most callers are expecting anyway), and also updates the callers of the setPref and pushPrefEnv APIs to await the result if they're relying on it taking effect immediately. Unfortunately, there are some corner cases in tests that appear to only work because of the quirks of the current sync messaging approach. The synchronous setPref APIs, for instance, trigger preference changes in the parent instantly, but don't update the values in the child until we've returned to the event loop and had a chance to process the notifications from the parent. The differnce in timing leads some tests to fail in strange ways, which this patch works around by just adding timeouts. There should be follow-ups for test owners to fix the flakiness. Differential Revision: https://phabricator.services.mozilla.com/D35054
dom/base/test/chrome/cpows_parent.xul
dom/base/test/chrome/test_cpows.xul
dom/base/test/chrome/window_nsITextInputProcessor.xul
dom/events/test/test_legacy_non-primary_click.html
dom/html/test/test_bug1414077.html
dom/notification/test/mochitest/NotificationTest.js
dom/notification/test/mochitest/test_notification_basics.html
dom/security/test/mixedcontentblocker/test_frameNavigation.html
dom/tests/mochitest/gamepad/test_gamepad_extensions_iframe.html
editor/libeditor/tests/test_selection_move_commands.html
gfx/tests/mochitest/test_font_whitelist.html
js/xpconnect/tests/chrome/test_bug1124898.html
js/xpconnect/tests/chrome/test_bug596580.xul
js/xpconnect/tests/chrome/test_bug732665.xul
js/xpconnect/tests/chrome/test_cows.xul
js/xpconnect/tests/chrome/test_windowProxyDeadWrapper.html
js/xpconnect/tests/chrome/test_xrayToJS.xul
testing/mochitest/tests/Harness_sanity/test_SpecialPowersExtension.html
testing/mochitest/tests/Harness_sanity/test_SpecialPowersPushPrefEnv.html
testing/specialpowers/content/SpecialPowersAPI.jsm
toolkit/components/url-classifier/tests/mochitest/classifierHelper.js
toolkit/components/url-classifier/tests/mochitest/test_cachemiss.html
toolkit/components/url-classifier/tests/mochitest/test_classify_ping.html
toolkit/content/tests/chrome/test_preferences_onsyncfrompreference.xul
widget/tests/window_composition_text_querycontent.xul
--- a/dom/base/test/chrome/cpows_parent.xul
+++ b/dom/base/test/chrome/cpows_parent.xul
@@ -5,16 +5,18 @@
 <window title="MessageManager CPOW tests"
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
   onload="start()">
 
   <!-- test results are displayed in the html:body -->
   <label value="CPOWs"/>
 
   <script type="application/javascript"><![CDATA[
+    const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
     var test_state = "remote";
     var test_node = null;
     var reentered = false;
     var savedMM = null;
 
     function info(message) {
       return opener.wrappedJSObject.info(message);
     }
@@ -395,33 +397,33 @@
       ok(failed, "CPOW should fail due to cancelation");
       msg.target.messageManager.sendAsyncMessage("cpows:cancel_test2_done");
     }
 
     function recvUnsafe(msg) {
       let failed = false;
 
       const PREF_UNSAFE_FORBIDDEN = "dom.ipc.cpows.forbid-unsafe-from-browser";
-      opener.wrappedJSObject.SpecialPowers.setBoolPref(PREF_UNSAFE_FORBIDDEN, true);
+      Services.prefs.setBoolPref(PREF_UNSAFE_FORBIDDEN, true);
       try {
         msg.objects.f();
       } catch (e) {
         if (!/unsafe CPOW usage forbidden/.test(String(e))) {
           throw e;
         }
         failed = true;
       }
       opener.wrappedJSObject.SpecialPowers.clearUserPref(PREF_UNSAFE_FORBIDDEN);
       ok(failed, "CPOW should fail when unsafe");
       msg.target.messageManager.sendAsyncMessage("cpows:unsafe_done");
     }
 
     function recvSafe(msg) {
       const PREF_UNSAFE_FORBIDDEN = "dom.ipc.cpows.forbid-unsafe-from-browser";
-      opener.wrappedJSObject.SpecialPowers.setBoolPref(PREF_UNSAFE_FORBIDDEN, true);
+      Services.prefs.setBoolPref(PREF_UNSAFE_FORBIDDEN, true);
       try {
         msg.objects.f();
       } catch (e) {
         if (!/unsafe CPOW usage forbidden/.test(String(e))) {
           throw e;
         }
         ok(false, "cpow failed");
       }
--- a/dom/base/test/chrome/test_cpows.xul
+++ b/dom/base/test/chrome/test_cpows.xul
@@ -8,22 +8,24 @@
 
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
   </body>
 
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
 
+  const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
   SimpleTest.waitForExplicitFinish();
 
   const PREF_UNSAFE_FORBIDDEN = "dom.ipc.cpows.forbid-unsafe-from-browser";
-  SpecialPowers.setBoolPref(PREF_UNSAFE_FORBIDDEN, false);
+  Services.prefs.setBoolPref(PREF_UNSAFE_FORBIDDEN, false);
   SimpleTest.registerCleanupFunction(() => {
-    SpecialPowers.clearUserPref(PREF_UNSAFE_FORBIDDEN);
+    Services.prefs.clearUserPref(PREF_UNSAFE_FORBIDDEN);
   });
 
   function done() {
     SimpleTest.finish();
   }
 
   addLoadEvent(function() {
     window.open("cpows_parent.xul", "", "chrome");
--- a/dom/base/test/chrome/window_nsITextInputProcessor.xul
+++ b/dom/base/test/chrome/window_nsITextInputProcessor.xul
@@ -18,17 +18,18 @@
 </div>
 <pre id="test">
 </pre>
 </body>
 
 <script class="testbody" type="application/javascript">
 <![CDATA[
 
-var SpecialPowers = window.opener.wrappedJSObject.SpecialPowers;
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
 var SimpleTest = window.opener.wrappedJSObject.SimpleTest;
 
 SimpleTest.waitForFocus(runTests, window);
 
 function ok(aCondition, aMessage)
 {
   SimpleTest.ok(aCondition, aMessage);
 }
@@ -1475,17 +1476,17 @@ function runReleaseTests()
   TIP.setCaretInPendingComposition(3);
   TIP.flushPendingComposition();
   is(input.value, "foo",
      description + "the input should have composition string");
 
   // Release the TIP
   TIP = null;
   // Needs to run GC forcibly for testing this.
-  SpecialPowers.gc();
+  Cu.forceGC();
 
   is(input.value, "",
      description + "the input should be empty because the composition should be canceled");
 
   TIP = createTIP();
   ok(TIP.beginInputTransactionForTests(window),
      description + "TIP.beginInputTransactionForTests() should succeed #2");
 }
@@ -1765,17 +1766,17 @@ function runCompositionWithKeyEventTests
   input.focus();
 
   var printableKeyEvent = new KeyboardEvent("", { key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A });
   var enterKeyEvent = new KeyboardEvent("", { key: "Enter", code: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN });
   var escKeyEvent = new KeyboardEvent("", { key: "Escape", code: "Escape", keyCode: KeyboardEvent.DOM_VK_ESCAPE });
   var convertKeyEvent = new KeyboardEvent("", { key: "Convert", code: "Convert", keyCode: KeyboardEvent.DOM_VK_CONVERT });
   var backspaceKeyEvent = new KeyboardEvent("", { key: "Backspace", code: "Backspace", keyCode: KeyboardEvent.DOM_VK_BACK_SPACE });
 
-  SpecialPowers.setBoolPref("dom.keyboardevent.dispatch_during_composition", false);
+  Services.prefs.setBoolPref("dom.keyboardevent.dispatch_during_composition", false);
 
   // nsITextInputProcessor.startComposition()
   reset();
   TIP.startComposition(printableKeyEvent);
   is(events.length, 2,
      description + "startComposition(printableKeyEvent) should cause keydown and compositionstart");
   is(events[0].type, "keydown",
      description + "startComposition(printableKeyEvent) should cause keydown");
@@ -1999,17 +2000,17 @@ function runCompositionWithKeyEventTests
      description + "committing text directly should cause compositionend after compositionupdate");
   is(events[3].data, "boo!",
      description + "compositionend caused by committing text directly should have the committing text in its data");
   is(events[4].type, "keyup",
      description + "committing text directly should cause keyup after compositionend");
   is(input.value, "FOobarbuzzboo!",
      description + "committing text directly should append the committing text to the focused editor");
 
-  SpecialPowers.setBoolPref("dom.keyboardevent.dispatch_during_composition", true);
+  Services.prefs.setBoolPref("dom.keyboardevent.dispatch_during_composition", true);
 
   // Even if "dom.keyboardevent.dispatch_during_composition" is true, keypress event shouldn't be fired during composition
   reset();
   TIP.startComposition(printableKeyEvent);
   is(events.length, 3,
      description + "TIP.startComposition(printableKeyEvent) should cause keydown, compositionstart and keyup (keypress event shouldn't be fired during composition)");
   is(events[0].type, "keydown",
      description + "TIP.startComposition(printableKeyEvent) should cause keydown (keypress event shouldn't be fired during composition)");
@@ -2103,17 +2104,17 @@ function runCompositionWithKeyEventTests
   TIP.cancelComposition(escKeydownEvent);
   is(events.length, 2,
      description + "TIP.cancelComposition(escKeydownEvent) should cause keydown and compositionend (keyup event shouldn't be fired)");
   is(events[0].type, "keydown",
      description + "TIP.cancelComposition(escKeydownEvent) should cause keydown (keyup event shouldn't be fired)");
   is(events[1].type, "compositionend",
      description + "TIP.cancelComposition(escKeydownEvent) should cause compositionend (keyup event shouldn't be fired)");
 
-  SpecialPowers.clearUserPref("dom.keyboardevent.dispatch_during_composition");
+  Services.prefs.clearUserPref("dom.keyboardevent.dispatch_during_composition");
 
   window.removeEventListener("compositionstart", handler, false);
   window.removeEventListener("compositionupdate", handler, false);
   window.removeEventListener("compositionend", handler, false);
   window.removeEventListener("keydown", handler, false);
   window.removeEventListener("keypress", handler, false);
   window.removeEventListener("keyup", handler, false);
 }
@@ -2150,17 +2151,17 @@ function runConsumingKeydownBeforeCompos
 
   input.value = "";
   input.focus();
 
   var printableKeyEvent = new KeyboardEvent("", { key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A });
   var enterKeyEvent = new KeyboardEvent("", { key: "Enter", code: "Enter", keyCode: KeyboardEvent.DOM_VK_RETURN });
   var escKeyEvent = new KeyboardEvent("", { key: "Escape", code: "Escape", keyCode: KeyboardEvent.DOM_VK_ESCAPE });
 
-  SpecialPowers.setBoolPref("dom.keyboardevent.dispatch_during_composition", false);
+  Services.prefs.setBoolPref("dom.keyboardevent.dispatch_during_composition", false);
 
   // If keydown before compositionstart is consumed, composition shouldn't be started.
   reset();
   ok(!TIP.startComposition(printableKeyEvent),
      description + "TIP.startComposition(printableKeyEvent) should return false because it's keydown is consumed");
   is(events.length, 2,
      description + "TIP.startComposition(printableKeyEvent) should cause only keydown and keyup events");
   is(events[0].type, "keydown",
@@ -2200,17 +2201,17 @@ function runConsumingKeydownBeforeCompos
      description + "TIP.commitCompositionWith(\"foo\", printableKeyEvent) should cause keydown event first");
   is(events[1].type, "keyup",
      description + "TIP.commitCompositionWith(\"foo\", printableKeyEvent) should cause keyup event after keydown");
   ok(!TIP.hasComposition,
      description + "TIP.commitCompositionWith(\"foo\", printableKeyEvent) shouldn't cause composition");
   is(input.value, "",
      description + "TIP.commitCompositionWith(\"foo\", printableKeyEvent) shouldn't cause inserting text");
 
-  SpecialPowers.setBoolPref("dom.keyboardevent.dispatch_during_composition", true);
+  Services.prefs.setBoolPref("dom.keyboardevent.dispatch_during_composition", true);
 
   // If composition is already started, TIP.flushPendingComposition(printableKeyEvent) shouldn't be canceled.
   TIP.startComposition();
   ok(TIP.hasComposition,
      description + "Before TIP.flushPendingComposition(printableKeyEvent), composition should've been created");
   reset();
   TIP.setPendingCompositionString("foo");
   TIP.appendClauseToPendingComposition(3, TIP.ATTR_RAW_CLAUSE);
@@ -2268,17 +2269,17 @@ function runConsumingKeydownBeforeCompos
      description + "TIP.cancelComposition(escKeyEvent) should cause compositionend event after compositionupdate");
   is(events[3].type, "keyup",
      description + "TIP.cancelComposition(escKeyEvent) should cause keyup event after compositionend");
   ok(!TIP.hasComposition,
      description + "TIP.cancelComposition(escKeyEvent) should cause canceling composition even if preceding keydown is consumed because there was a composition already");
   is(input.value, "",
      description + "TIP.cancelComposition(escKeyEvent) should cancel composition even if preceding keydown is consumed because there was a composition already");
 
-  SpecialPowers.clearUserPref("dom.keyboardevent.dispatch_during_composition");
+  Services.prefs.clearUserPref("dom.keyboardevent.dispatch_during_composition");
 
   window.removeEventListener("compositionstart", handler, false);
   window.removeEventListener("compositionupdate", handler, false);
   window.removeEventListener("compositionend", handler, false);
   window.removeEventListener("keydown", handler, false);
   window.removeEventListener("keypress", handler, false);
   window.removeEventListener("keyup", handler, false);
 }
@@ -2589,47 +2590,47 @@ function runKeyTests()
                 { type: "keypress", key: "a", code: "KeyA", keyCode: 0,                      charCode: "a".charCodeAt(0), defaultPrevented: true });
   checkKeyAttrs("TIP.keydown(keyA) and TIP.keyup(keyA) with preventing default of keyup", events[2],
                 { type: "keyup",    key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A, charCode: 0,                 defaultPrevented: true });
   is(input.value, "a",
      description + "input.value should be \"a\" by TIP.keyup(keyA) even if the keyup event is consumed");
 
   // key events during composition
   try {
-    SpecialPowers.setBoolPref("dom.keyboardevent.dispatch_during_composition", false);
+    Services.prefs.setBoolPref("dom.keyboardevent.dispatch_during_composition", false);
 
     ok(TIP.startComposition(), "TIP.startComposition() should start composition");
 
     input.value = "";
     reset();
     TIP.keydown(keyA);
     is(events.length, 0,
        description + "TIP.keydown(keyA) shouldn't cause key events during composition if it's disabled by the pref");
     reset();
     TIP.keyup(keyA);
     is(events.length, 0,
        description + "TIP.keyup(keyA) shouldn't cause key events during composition if it's disabled by the pref");
 
-    SpecialPowers.setBoolPref("dom.keyboardevent.dispatch_during_composition", true);
+    Services.prefs.setBoolPref("dom.keyboardevent.dispatch_during_composition", true);
     reset();
     TIP.keydown(keyA);
     is(events.length, 1,
        description + "TIP.keydown(keyA) should cause keydown event even composition if it's enabled by the pref");
     checkKeyAttrs("TIP.keydown(keyA) during composition", events[0],
                   { type: "keydown",  key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A, charCode: 0, isComposing: true });
     reset();
     TIP.keyup(keyA);
     is(events.length, 1,
        description + "TIP.keyup(keyA) should cause keyup event even composition if it's enabled by the pref");
     checkKeyAttrs("TIP.keyup(keyA) during composition", events[0],
                   { type: "keyup",    key: "a", code: "KeyA", keyCode: KeyboardEvent.DOM_VK_A, charCode: 0, isComposing: true });
 
   } finally {
     TIP.cancelComposition();
-    SpecialPowers.clearUserPref("dom.keyboardevent.dispatch_during_composition");
+    Services.prefs.clearUserPref("dom.keyboardevent.dispatch_during_composition");
   }
 
   // Test .location computation
   const kCodeToLocation = [
     { code: "BracketLeft",              location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD },
     { code: "BracketRight",             location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD },
     { code: "Comma",                    location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD },
     { code: "Digit0",                   location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD },
--- a/dom/events/test/test_legacy_non-primary_click.html
+++ b/dom/events/test/test_legacy_non-primary_click.html
@@ -18,35 +18,36 @@ SimpleTest.waitForExplicitFinish();
 
 const HACK_PREF = "dom.mouseevent.click.hack.use_legacy_non-primary_dispatch";
 const testEl = document.getElementById("test");
 const linkEl = document.getElementById("link-test");
 let seenClick = false;
 
 SpecialPowers.pushPrefEnv(
   { set: [[HACK_PREF, document.domain]] },
-  SimpleTest.waitForFocus(() => {
-    // Test seeing the non-primary 'click'
-    document.addEventListener("click", (e) => {
-      ok(true, "Saw 'click' event");
-      seenClick = true;
-    }, { once: true });
-    document.addEventListener("auxclick", (e) => {
-      ok(true, "Saw 'auxclick' event");
-      ok(seenClick, "Saw 'click' event before 'auxclick' event");
-    }, { once: true });
-    synthesizeMouseAtCenter(testEl, { button: 1 });
+  () => {
+    SimpleTest.waitForFocus(() => {
+      // Test seeing the non-primary 'click'
+      document.addEventListener("click", (e) => {
+        ok(true, "Saw 'click' event");
+        seenClick = true;
+      }, { once: true });
+      document.addEventListener("auxclick", (e) => {
+        ok(true, "Saw 'auxclick' event");
+        ok(seenClick, "Saw 'click' event before 'auxclick' event");
+      }, { once: true });
+      synthesizeMouseAtCenter(testEl, { button: 1 });
 
-    // Test preventDefaulting on non-primary 'click'
-    document.addEventListener("click", (e) => {
-      is(e.target, linkEl, "Saw 'click' on link");
-      e.preventDefault();
-      SimpleTest.finish();
-    }, { once: true, capture: true });
-    document.addEventListener("auxclick", (e) => {
-      ok(false, "Shouldn't have got 'auxclick' after preventDefaulting 'click'");
-    }, { once: true });
-    synthesizeMouseAtCenter(linkEl, { button: 1 });
-  })
-);
+      // Test preventDefaulting on non-primary 'click'
+      document.addEventListener("click", (e) => {
+        is(e.target, linkEl, "Saw 'click' on link");
+        e.preventDefault();
+        SimpleTest.finish();
+      }, { once: true, capture: true });
+      document.addEventListener("auxclick", (e) => {
+        ok(false, "Shouldn't have got 'auxclick' after preventDefaulting 'click'");
+      }, { once: true });
+      synthesizeMouseAtCenter(linkEl, { button: 1 });
+    });
+  });
 </script>
 </body>
 </html>
--- a/dom/html/test/test_bug1414077.html
+++ b/dom/html/test/test_bug1414077.html
@@ -9,17 +9,21 @@ https://bugzilla.mozilla.org/show_bug.cg
 <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
 <script type="application/javascript">
 
 /** Test for Bug 1414077 **/
 
 SimpleTest.waitForExplicitFinish();
 
-SpecialPowers.pushPrefEnv({"set":[["browser.enable_automatic_image_resizing", true]]}, function() {
+SimpleTest.requestFlakyTimeout("This test is flaky.");
+
+SpecialPowers.pushPrefEnv({"set":[["browser.enable_automatic_image_resizing", true]]}, async function() {
+  await new Promise(resolve => setTimeout(resolve, 1000));
+
   var testWin = document.querySelector("iframe");
   testWin.height = 0;
   testWin.width = 0;
   testWin.src = "image.png";
   testWin.onload = function() {
     var testDoc = testWin.contentDocument;
 
     // testDoc should be a image document.
--- a/dom/notification/test/mochitest/NotificationTest.js
+++ b/dom/notification/test/mochitest/NotificationTest.js
@@ -3,22 +3,22 @@ var NotificationTest = (function() {
 
   function info(msg, name) {
     SimpleTest.info("::Notification Tests::" + (name || ""), msg);
   }
 
   function setup_testing_env() {
     SimpleTest.waitForExplicitFinish();
     // turn on testing pref (used by notification.cpp, and mock the alerts
-    SpecialPowers.setBoolPref("notification.prompt.testing", true);
+    return SpecialPowers.setBoolPref("notification.prompt.testing", true);
   }
 
-  function teardown_testing_env() {
-    SpecialPowers.clearUserPref("notification.prompt.testing");
-    SpecialPowers.clearUserPref("notification.prompt.testing.allow");
+  async function teardown_testing_env() {
+    await SpecialPowers.clearUserPref("notification.prompt.testing");
+    await SpecialPowers.clearUserPref("notification.prompt.testing.allow");
 
     SimpleTest.finish();
   }
 
   function executeTests(tests, callback) {
     // context is `this` object in test functions
     // it can be used to track data between tests
     var context = {};
@@ -73,32 +73,33 @@ var NotificationTest = (function() {
       map,
       set,
     };
   })();
 
   // NotificationTest API
   return {
     run(tests, callback) {
-      setup_testing_env();
+      let ready = setup_testing_env();
 
-      addLoadEvent(function() {
+      addLoadEvent(async function() {
+        await ready;
         executeTests(tests, function() {
           teardown_testing_env();
           callback && callback();
         });
       });
     },
 
     allowNotifications() {
-      SpecialPowers.setBoolPref("notification.prompt.testing.allow", true);
+      return SpecialPowers.setBoolPref("notification.prompt.testing.allow", true);
     },
 
     denyNotifications() {
-      SpecialPowers.setBoolPref("notification.prompt.testing.allow", false);
+      return SpecialPowers.setBoolPref("notification.prompt.testing.allow", false);
     },
 
     clickNotification(notification) {
       // TODO: how??
     },
 
     fireCloseEvent(title) {
       window.dispatchEvent(new CustomEvent("mock-notification-close-event", {
--- a/dom/notification/test/mochitest/test_notification_basics.html
+++ b/dom/notification/test/mochitest/test_notification_basics.html
@@ -27,39 +27,39 @@
       ok(Notification.get, "Notification.get exists");
     },
 
     function() {
       info("Test requestPermission without callback");
       Notification.requestPermission();
     },
 
-    function(done) {
+    async function(done) {
       info("Test requestPermission deny");
       function assertPermissionDenied(perm) {
         is(perm, "denied", "Permission should be denied.");
         is(Notification.permission, "denied", "Permission should be denied.");
       }
-      NotificationTest.denyNotifications();
+      await NotificationTest.denyNotifications();
       Notification.requestPermission()
         .then(assertPermissionDenied)
         .then(_ => Notification.requestPermission(assertPermissionDenied))
         .catch(err => {
           ok(!err, "requestPermission should not reject promise");
         })
         .then(done);
     },
 
-    function(done) {
+    async function(done) {
       info("Test requestPermission grant");
       function assertPermissionGranted(perm) {
         is(perm, "granted", "Permission should be granted.");
         is(Notification.permission, "granted", "Permission should be granted");
       }
-      NotificationTest.allowNotifications();
+      await NotificationTest.allowNotifications();
       Notification.requestPermission()
         .then(assertPermissionGranted)
         .then(_ => Notification.requestPermission(assertPermissionGranted))
         .catch(err => {
           ok(!err, "requestPermission should not reject promise");
         })
         .then(done);
     },
--- a/dom/security/test/mixedcontentblocker/test_frameNavigation.html
+++ b/dom/security/test/mixedcontentblocker/test_frameNavigation.html
@@ -28,17 +28,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     blankTarget: false,
   };
 
   function log(msg) {
     document.getElementById("log").textContent += "\n" + msg;
   }
 
   var secureTestsStarted = false;
-  function checkTestsCompleted() {
+  async function checkTestsCompleted() {
     for (var prop in testsToRunInsecure) {
       // some test hasn't run yet so we're not done
       if (!testsToRunInsecure[prop])
         return;
     }
     // If we are here, all the insecure tests have run.
     // If we haven't changed the iframe to run the secure tests, change it now.
     if (!secureTestsStarted) {
@@ -55,17 +55,17 @@ https://bugzilla.mozilla.org/show_bug.cg
        for (var prop in testsToRunSecure) {
          testsToRunSecure[prop] = false;
        }
        for (var prop in testsToRunInsecure) {
          testsToRunInsecure[prop] = false;
        }
       //call to change the preferences
       counter++;
-      SpecialPowers.setBoolPref("security.mixed_content.block_active_content", false);
+      await SpecialPowers.setBoolPref("security.mixed_content.block_active_content", false);
       blockActive = SpecialPowers.getBoolPref("security.mixed_content.block_active_content");
       log("blockActive set to "+blockActive+".");
       secureTestsStarted = false;
       document.getElementById('framediv').innerHTML = '<iframe src="http://example.com/tests/dom/security/test/mixedcontentblocker/file_frameNavigation.html" id="testing_frame"></iframe>';
     }
     else {
       //set the prefs back to what they were set to originally
       SpecialPowers.setBoolPref("security.mixed_content.block_active_content", origBlockActive);
--- a/dom/tests/mochitest/gamepad/test_gamepad_extensions_iframe.html
+++ b/dom/tests/mochitest/gamepad/test_gamepad_extensions_iframe.html
@@ -38,21 +38,21 @@ window.addEventListener("gamepadbuttondo
   SpecialPowers.executeSoon(tests[testNum++]);
 });
 
 function pressButton() {
   GamepadService.newButtonEvent(gamepad_index, 0, true, true);
   GamepadService.newButtonEvent(gamepad_index, 0, false, false);
 }
 
-function startTest() {
-  SpecialPowers.pushPrefEnv({ "set": [
-                              ["dom.gamepad.extensions.enabled", true],
-                              ["dom.gamepad.extensions.lightindicator", true],
-                              ["dom.gamepad.extensions.multitouch", true]] });
+async function startTest() {
+  await SpecialPowers.pushPrefEnv({ "set": [
+                                    ["dom.gamepad.extensions.enabled", true],
+                                    ["dom.gamepad.extensions.lightindicator", true],
+                                    ["dom.gamepad.extensions.multitouch", true]] });
   // Add a gamepad
   GamepadService.addGamepad("test gamepad", // id
                      GamepadService.standardMapping,
                      GamepadService.leftHand,
                      4,
                      2,
                      1,
                      1,
--- a/editor/libeditor/tests/test_selection_move_commands.html
+++ b/editor/libeditor/tests/test_selection_move_commands.html
@@ -12,17 +12,17 @@ SimpleTest.requestFlakyTimeout("Legacy t
 
 var winUtils = SpecialPowers.getDOMWindowUtils(window);
 
 async function setup() {
   await SpecialPowers.pushPrefEnv({set: [["general.smoothScroll", false]]});
   winUtils.advanceTimeAndRefresh(100);
 }
 
-function* runTests() {
+async function* runTests() {
   var e = document.getElementById("edit");
   var doc = e.contentDocument;
   var win = e.contentWindow;
   var root = doc.documentElement;
   var body = doc.body;
 
   body.style.fontSize = "16px";
   body.style.lineHeight = "16px";
@@ -176,32 +176,32 @@ function* runTests() {
 
     SpecialPowers.doCommand(window, "cmd_moveTop");
     is(testPageSelectCommand("cmd_selectPageDown", 0), lineNum, "cmd_selectPageDown");
     SpecialPowers.doCommand(window, "cmd_moveBottom");
     SpecialPowers.doCommand(window, "cmd_beginLine");
     is(testPageSelectCommand("cmd_selectPageUp", 0), 22 - lineNum, "cmd_selectPageUp");
   };
 
-  yield SpecialPowers.pushPrefEnv({set: [["layout.word_select.eat_space_to_next_word", false]]});
+  await SpecialPowers.pushPrefEnv({set: [["layout.word_select.eat_space_to_next_word", false]]});
   runSelectionTests(body, 1);
-  yield SpecialPowers.pushPrefEnv({set: [["layout.word_select.eat_space_to_next_word", true]]});
+  await SpecialPowers.pushPrefEnv({set: [["layout.word_select.eat_space_to_next_word", true]]});
   runSelectionTests(node(2), 0);
 }
 
 function cleanup() {
   winUtils.restoreNormalRefresh();
   SimpleTest.finish();
 }
 
 async function testRunner() {
   let curTest = runTests();
   while (true) {
     winUtils.advanceTimeAndRefresh(100);
-    if (curTest.next().done) {
+    if ((await curTest.next()).done) {
       break;
     }
     winUtils.advanceTimeAndRefresh(100);
     await new Promise(resolve => setTimeout(resolve, 20));
   }
 }
 
 setup()
--- a/gfx/tests/mochitest/test_font_whitelist.html
+++ b/gfx/tests/mochitest/test_font_whitelist.html
@@ -15,16 +15,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 <span id="serif" style="font-family: serif; font-size: 64px;">M</span>
 <div id="content" style="display: none">
 
 </div>
 <script class="testbody" type="application/javascript">
 
 /** Test for Bug 1121643 **/
 
+SimpleTest.requestFlakyTimeout("This test is flaky.");
+
 const InspectorUtils = SpecialPowers.InspectorUtils;
 
 // Given an element id, returns the first font face name encountered.
 let fontUsed = id => {
   let element = document.getElementById(id),
       range = document.createRange();
   range.selectNode(element);
   return InspectorUtils.getUsedFontFaces(range)[0].CSSFamilyName;
@@ -48,16 +50,17 @@ let testFontWhitelist = async function(u
   if (useSans) {
     whitelist.push(fonts.sans);
   }
   if (useSerif) {
     whitelist.push(fonts.serif);
   }
   await SpecialPowers.pushPrefEnv({"set": [["font.system.whitelist",
                                             whitelist.join(", ")]]});
+  await new Promise(resolve => setTimeout(resolve, 1000));
   // If whitelist is empty, then whitelisting is considered disabled
   // and all fonts are allowed.
   info("font whitelist: " + JSON.stringify(whitelist));
   let whitelistEmpty = whitelist.length === 0;
   is(useMono || whitelistEmpty, fontUsed("mono") === fonts.mono,
      "Correct mono whitelisting state; got " + fontUsed("mono") + ", requested " + fonts.mono);
   is(useSans || whitelistEmpty, fontUsed("sans") === fonts.sans,
      "Correct sans whitelisting state; got " + fontUsed("sans") + ", requested " + fonts.sans);
--- a/js/xpconnect/tests/chrome/test_bug1124898.html
+++ b/js/xpconnect/tests/chrome/test_bug1124898.html
@@ -8,37 +8,40 @@ https://bugzilla.mozilla.org/show_bug.cg
   <title>Test for Bug 1124898</title>
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
   /** Test for Bug 1124898 **/
   SimpleTest.waitForExplicitFinish();
-  SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal",
-																			true]]});
-  SimpleTest.expectAssertions(0, 1); // Dumb unrelated widget assertion - see bug 1126023.
-  var w = window.open("about:blank", "w", "chrome");
-  is(w.eval('typeof getAttention'), 'function', 'getAttention exists on regular chrome window');
-  is(w.eval('typeof messageManager'), 'object', 'messageManager exists on regular chrome window');
-  var contentURL = "http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html";
-  w.location = contentURL;
-  tryWindow();
+  (async () => {
+    await SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal", true]]});
+
+    SimpleTest.expectAssertions(0, 1); // Dumb unrelated widget assertion - see bug 1126023.
+
+    var w = window.open("about:blank", "w", "chrome");
+    is(w.eval('typeof getAttention'), 'function', 'getAttention exists on regular chrome window');
+    is(w.eval('typeof messageManager'), 'object', 'messageManager exists on regular chrome window');
+    var contentURL = "http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html";
+    w.location = contentURL;
+    tryWindow();
 
-  function tryWindow() {
-    if (w.document.title != 'empty test page') {
-      info("Document not loaded yet - retrying");
-      SimpleTest.executeSoon(tryWindow);
-      return;
+    function tryWindow() {
+      if (w.document.title != 'empty test page') {
+        info("Document not loaded yet - retrying");
+        SimpleTest.executeSoon(tryWindow);
+        return;
+      }
+      is(w.eval('typeof getAttention'), 'undefined', 'getAttention doesnt exist on content-in-chrome window');
+      is(w.eval('typeof messageManager'), 'undefined', 'messageManager doesnt exist on content-in-chrome window');
+      w.close();
+      SimpleTest.finish();
     }
-    is(w.eval('typeof getAttention'), 'undefined', 'getAttention doesnt exist on content-in-chrome window');
-    is(w.eval('typeof messageManager'), 'undefined', 'messageManager doesnt exist on content-in-chrome window');
-    w.close();
-    SimpleTest.finish();
-  }
+  })();
 
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1124898">Mozilla Bug 1124898</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
--- a/js/xpconnect/tests/chrome/test_bug596580.xul
+++ b/js/xpconnect/tests/chrome/test_bug596580.xul
@@ -9,41 +9,43 @@
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=596580"
      target="_blank">Mozilla Bug 596580</a>
   </body>
 
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
-    SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal",
-                                        true]]});
+    SimpleTest.waitForExplicitFinish();
+
     function init() {
       var f = new Function("let test = 'let is ok'; return test;");
       is(f(), 'let is ok', 'let should be ok');
       SimpleTest.finish();
     }
 
-    Test = {
-      include: function(p) {
-	var sawError = false;
-	try {
-	  Cc["@mozilla.org/moz/jssubscript-loader;1"].
-	    getService(Ci["mozIJSSubScriptLoader"]).
-	    loadSubScript(p);
-	} catch (e) {
-	  sawError = true;
-	}
-	ok(sawError, 'should receive an error loading a not-found file');
-      }
-    };
+    (async () => {
+      await SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal",
+                                               true]]});
+      Test = {
+        include: function(p) {
+          var sawError = false;
+          try {
+            Cc["@mozilla.org/moz/jssubscript-loader;1"].
+              getService(Ci["mozIJSSubScriptLoader"]).
+              loadSubScript(p);
+          } catch (e) {
+            sawError = true;
+          }
+          ok(sawError, 'should receive an error loading a not-found file');
+        }
+      };
 
-    // If the include method is defined as a global function, it works.
-    // try to load a non existing file to produce the error
-    Test.include("notfound.js");
+      // If the include method is defined as a global function, it works.
+      // try to load a non existing file to produce the error
+      Test.include("notfound.js");
 
-    // If init is called directly, it works.
-    setTimeout('init();', 0);
-
-    SimpleTest.waitForExplicitFinish();
+      // If init is called directly, it works.
+      setTimeout('init();', 0);
+    })();
 
   ]]></script>
 </window>
--- a/js/xpconnect/tests/chrome/test_bug732665.xul
+++ b/js/xpconnect/tests/chrome/test_bug732665.xul
@@ -13,18 +13,19 @@ https://bugzilla.mozilla.org/show_bug.cg
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=732665"
      target="_blank">Mozilla Bug 732665</a>
   </body>
 
   <!-- test code goes here -->
   <script type="application/javascript">
   <![CDATA[
 
-  SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal",
-                                       true]]});
+add_task(async () => {
+  await SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal",
+                                            true]]});
   //
   // Important! If this test starts failing after a tricky platform-y change,
   // the stack quota numbers in XPCJSContext probably need twiddling. We want
   // to maintain the invariants in this test (at least to some approximation)
   // for security reasons.
   //
 
   // Executes f() d steps from the probed native stack limit, and returns
@@ -71,12 +72,12 @@ https://bugzilla.mozilla.org/show_bug.cg
   //
   // If this assertion fails, the current work-around so far is to measure
   // again the worst frame size, by using the JS Shell to run
   // test_bug732665_meta.js . This script will output numbers to update
   // XPCJSContext.cpp comment, as well as the kTrustedScriptBuffer constant.
   contentSb.nnslChrome = chromeSb.nearNativeStackLimit;
   var nestedLimit = Cu.evalInSandbox("nearNativeStackLimit(1, function() { nestedLimit = nnslChrome(0);}); nestedLimit;", contentSb);
   ok(nestedLimit >= 11, "Chrome should be invokable from content script with an exhausted stack: " + nestedLimit);
-
+});
   ]]>
   </script>
 </window>
--- a/js/xpconnect/tests/chrome/test_cows.xul
+++ b/js/xpconnect/tests/chrome/test_cows.xul
@@ -12,22 +12,23 @@ https://bugzilla.mozilla.org/show_bug.cg
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=522764 "
      target="_blank">Mozilla Bug 522764 </a>
   </body>
 
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
+add_task(async () => {
 var sandbox = new Cu.Sandbox("about:blank");
 
 var test_utils = window.windowUtils;
 
-SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal",
-                                    true]]});
+await SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal",
+                                          true]]});
 
 function getCOW(x) {
   if (typeof x != 'object' && typeof x != 'function')
     return x;
   x = Cu.waiveXrays(x);
   var rval = {};
   if (typeof x == "function")
     rval = eval(uneval(x));
@@ -219,10 +220,11 @@ var unwrapped = Cu.evalInSandbox(
 
 try {
     is(unwrapped.bar, 6,
        "COWs should be unwrapped when entering chrome space");
 } catch (e) {
     todo(false, "COWs should be unwrapped when entering chrome space, " +
                 "not raise " + e);
 }
+});
   ]]></script>
 </window>
--- a/js/xpconnect/tests/chrome/test_windowProxyDeadWrapper.html
+++ b/js/xpconnect/tests/chrome/test_windowProxyDeadWrapper.html
@@ -9,20 +9,20 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
 /** Test for Bug 1223372 **/
 const {TestUtils} = ChromeUtils.import("resource://testing-common/TestUtils.jsm");
 
-function go() {
-    SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal",
-                                        true]]});
+async function go() {
     SimpleTest.waitForExplicitFinish();
+    await SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal",
+                                             true]]});
 
     var frame = $('subframe');
     frame.onload = null;
 
     var w = frame.contentWindow;
 
     w.eval("checkDead = function() { return Components.utils.isDeadWrapper(this); };");
     var checkDead = w.checkDead;
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -13,19 +13,16 @@ https://bugzilla.mozilla.org/show_bug.cg
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=933681"
      target="_blank">Mozilla Bug 933681</a>
   </body>
 
   <!-- test code goes here -->
   <script type="application/javascript">
   <![CDATA[
 
-  SpecialPowers.pushPrefEnv({"set": [["security.allow_eval_with_system_principal",
-                                       true]]});
-
   /** Test for ES constructors on Xrayed globals. **/
   SimpleTest.waitForExplicitFinish();
   let global = Cu.getGlobalForObject.bind(Cu);
 
   function checkThrows(f, rgxp, msg) {
     try {
       f();
       ok(false, "Should have thrown: " + msg);
@@ -474,83 +471,80 @@ https://bugzilla.mozilla.org/show_bug.cg
     testCtorCallables(ctorCallables, xrayCtor, localCtor);
     is(Object.getOwnPropertyNames(xrayCtor).sort().toSource(),
        ctorProps.toSource(), "getOwnPropertyNames works on Xrayed ctors");
     is(Object.getOwnPropertySymbols(xrayCtor).map(uneval).sort().toSource(),
        ctorSymbols.toSource(), "getOwnPropertySymbols works on Xrayed ctors");
   }
 
   // We will need arraysEqual and testArrayIterators both in this global scope
-  // and in sandboxes, so define them as strings up front.
-  var arraysEqualSource = `function arraysEqual(arr1, arr2, reason) {
-    is(arr1.length, arr2.length, \`\${reason}; lengths should be equal\`)
+  // and in sandboxes.
+  function arraysEqual(arr1, arr2, reason) {
+    is(arr1.length, arr2.length, `${reason}; lengths should be equal`)
     for (var i = 0; i < arr1.length; ++i) {
       if (Array.isArray(arr2[i])) {
-        arraysEqual(arr1[i], arr2[i], \`\${reason}; item at index \${i}\`);
+        arraysEqual(arr1[i], arr2[i], `${reason}; item at index ${i}`);
       } else {
-        is(arr1[i], arr2[i], \`\${reason}; item at index \${i} should be equal\`);
+        is(arr1[i], arr2[i], `${reason}; item at index ${i} should be equal`);
       }
     }
-  }`;
-  eval(arraysEqualSource);
+  }
 
-  var testArrayIteratorsSource = `
-    function testArrayIterators(arrayLike, equivalentArray, reason) {
-    arraysEqual([...arrayLike], equivalentArray, \`\${reason}; spread operator\`);
+  function testArrayIterators(arrayLike, equivalentArray, reason) {
+    arraysEqual([...arrayLike], equivalentArray, `${reason}; spread operator`);
     arraysEqual([...arrayLike.entries()], [...equivalentArray.entries()],
-                \`\${reason}; entries\`);
+                `${reason}; entries`);
     arraysEqual([...arrayLike.keys()], [...equivalentArray.keys()],
-                \`\${reason}; keys\`);
+                `${reason}; keys`);
     if (arrayLike.values) {
       arraysEqual([...arrayLike.values()], equivalentArray,
-                  \`\${reason}; values\`);
+                  `${reason}; values`);
     }
 
     var forEachCopy = [];
     arrayLike.forEach(function(arg) { forEachCopy.push(arg); });
-    arraysEqual(forEachCopy, equivalentArray, \`\${reason}; forEach copy\`);
+    arraysEqual(forEachCopy, equivalentArray, `${reason}; forEach copy`);
 
     var everyCopy = [];
     arrayLike.every(function(arg) { everyCopy.push(arg); return true; });
-    arraysEqual(everyCopy, equivalentArray, \`\${reason}; every() copy\`);
+    arraysEqual(everyCopy, equivalentArray, `${reason}; every() copy`);
 
     var filterCopy = [];
     var filterResult = arrayLike.filter(function(arg) {
       filterCopy.push(arg);
       return true;
     });
-    arraysEqual(filterCopy, equivalentArray, \`\${reason}; filter copy\`);
-    arraysEqual([...filterResult], equivalentArray, \`\${reason}; filter result\`);
+    arraysEqual(filterCopy, equivalentArray, `${reason}; filter copy`);
+    arraysEqual([...filterResult], equivalentArray, `${reason}; filter result`);
 
     var findCopy = [];
     arrayLike.find(function(arg) { findCopy.push(arg); return false; });
-    arraysEqual(findCopy, equivalentArray, \`\${reason}; find() copy\`);
+    arraysEqual(findCopy, equivalentArray, `${reason}; find() copy`);
 
     var findIndexCopy = [];
     arrayLike.findIndex(function(arg) { findIndexCopy.push(arg); return false; });
-    arraysEqual(findIndexCopy, equivalentArray, \`\${reason}; findIndex() copy\`);
+    arraysEqual(findIndexCopy, equivalentArray, `${reason}; findIndex() copy`);
 
     var mapCopy = [];
     var mapResult = arrayLike.map(function(arg) { mapCopy.push(arg); return arg});
-    arraysEqual(mapCopy, equivalentArray, \`\${reason}; map() copy\`);
-    arraysEqual([...mapResult], equivalentArray, \`\${reason}; map() result\`);
+    arraysEqual(mapCopy, equivalentArray, `${reason}; map() copy`);
+    arraysEqual([...mapResult], equivalentArray, `${reason}; map() result`);
 
     var reduceCopy = [];
     arrayLike.reduce(function(_, arg) { reduceCopy.push(arg); }, 0);
-    arraysEqual(reduceCopy, equivalentArray, \`\${reason}; reduce() copy\`);
+    arraysEqual(reduceCopy, equivalentArray, `${reason}; reduce() copy`);
 
     var reduceRightCopy = [];
     arrayLike.reduceRight(function(_, arg) { reduceRightCopy.unshift(arg); }, 0);
-    arraysEqual(reduceRightCopy, equivalentArray, \`\${reason}; reduceRight() copy\`);
+    arraysEqual(reduceRightCopy, equivalentArray, `${reason}; reduceRight() copy`);
 
     var someCopy = [];
     arrayLike.some(function(arg) { someCopy.push(arg); return false; });
-    arraysEqual(someCopy, equivalentArray, \`\${reason}; some() copy\`);
-  }`;
-  eval(testArrayIteratorsSource);
+    arraysEqual(someCopy, equivalentArray, `${reason}; some() copy`);
+  }
 
   function testDate() {
     // toGMTString is handled oddly in the engine. We don't bother to support
     // it over Xrays.
     let propsToSkip = ['toGMTString'];
 
     testXray('Date', new iwin.Date(), new iwin.Date(), propsToSkip);
 
@@ -795,31 +789,31 @@ https://bugzilla.mozilla.org/show_bug.cg
          "Only indexed properties visible over Xrays");
       Object.defineProperty(t.wrappedJSObject, 'length', {value: 42});
       is(t.wrappedJSObject.length, 42, "Set tricky expando")
       is(t.length, 10, "Length accessor works over Xrays")
       is(t.byteLength, t.length * window[c].prototype.BYTES_PER_ELEMENT, "byteLength accessor works over Xrays")
 
       // Can create TypedArray from content ArrayBuffer
       var buffer = new iwin.ArrayBuffer(8);
-      eval(`new ${c}(buffer);`);
+      new window[c](buffer);
 
       var xray = new iwin[c](0);
       var xrayTypedArrayProto = Object.getPrototypeOf(Object.getPrototypeOf(xray));
       testProtoCallables(inheritedCallables, new iwin[c](0), xrayTypedArrayProto, typedArrayProto);
 
       // When testing iterators, make sure to do so from inside our web
       // extension sandbox, since from chrome we can't poke their indices.  Note
       // that we have to actually recreate our functions that touch typed array
       // indices inside the sandbox, not just export them, because otherwise
       // they'll just run with our principal anyway.
       //
       // But we do want to export is(), since we want ours called.
-      wesb.eval(arraysEqualSource);
-      wesb.eval(testArrayIteratorsSource);
+      wesb.eval(String(arraysEqual));
+      wesb.eval(String(testArrayIterators));
       Cu.exportFunction(is, wesb,
                         { defineAs: "is" });
       wesb.eval('testArrayIterators(t, [0, 0, 3, 0, 0, 0, 0, 0, 0, 0])');
     }
   }
 
   function testErrorObjects() {
     // We only invoke testXray with Error, because that function isn't set up
--- a/testing/mochitest/tests/Harness_sanity/test_SpecialPowersExtension.html
+++ b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersExtension.html
@@ -26,45 +26,46 @@ 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();
-function starttest(){
+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
-  SpecialPowers.setBoolPref('extensions.checkCompatibility', true);
+  await SpecialPowers.setBoolPref('extensions.checkCompatibility', true);
   is(SpecialPowers.getBoolPref('extensions.checkCompatibility'), true, "Check to see if we can set a preference properly");
-  SpecialPowers.clearUserPref('extensions.checkCompatibility');
+  await SpecialPowers.clearUserPref('extensions.checkCompatibility');
 
   // Test a int pref
-  SpecialPowers.setIntPref('extensions.foobar', 42);
+  await SpecialPowers.setIntPref('extensions.foobar', 42);
   is(SpecialPowers.getIntPref('extensions.foobar'), 42, "Check int pref");
-  SpecialPowers.clearUserPref('extensions.foobar');
+  await SpecialPowers.clearUserPref('extensions.foobar');
 
   // Test a string pref
-  SpecialPowers.setCharPref("extensions.foobaz", "hi there");
+  await SpecialPowers.setCharPref("extensions.foobaz", "hi there");
   is(SpecialPowers.getCharPref("extensions.foobaz"), "hi there", "Check string pref");
-  SpecialPowers.clearUserPref("extensions.foobaz");
+  await SpecialPowers.clearUserPref("extensions.foobaz");
 
   // Test an invalid pref
   var retVal = null;
   try {
     retVal = SpecialPowers.getBoolPref('extensions.checkCompat0123456789');
   } catch (ex) {
     retVal = ex;
   }
-  is(retVal.message, "Error getting pref 'extensions.checkCompat0123456789'", "received an exception trying to get an unset preference value");
+  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);
--- a/testing/mochitest/tests/Harness_sanity/test_SpecialPowersPushPrefEnv.html
+++ b/testing/mochitest/tests/Harness_sanity/test_SpecialPowersPushPrefEnv.html
@@ -4,29 +4,29 @@
   <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">
-function starttest() {
+async function starttest() {
   try {
-    SpecialPowers.setBoolPref("test.bool", 1);
+    await SpecialPowers.setBoolPref("test.bool", 1);
   } catch(e) {
-    SpecialPowers.setBoolPref("test.bool", true);
+    await SpecialPowers.setBoolPref("test.bool", true);
   }
   try {
-    SpecialPowers.setIntPref("test.int", true);
+    await SpecialPowers.setIntPref("test.int", true);
   } catch(e) {
-    SpecialPowers.setIntPref("test.int", 1);
+    await SpecialPowers.setIntPref("test.int", 1);
   }
-  SpecialPowers.setCharPref("test.char", 'test');
-  SpecialPowers.setBoolPref("test.cleanup", false);
+  await SpecialPowers.setCharPref("test.char", 'test');
+  await SpecialPowers.setBoolPref("test.cleanup", false);
 
   setTimeout(test1, 0, 0);
 }
 
 SimpleTest.waitForExplicitFinish();
 
 function test1(aCount) {
   if (aCount >= 20) {
--- a/testing/specialpowers/content/SpecialPowersAPI.jsm
+++ b/testing/specialpowers/content/SpecialPowersAPI.jsm
@@ -995,29 +995,39 @@ class SpecialPowersAPI {
   // <object> and <embed> tags will spawn plugins if their prototype is touched,
   // so we need to get and cache the getter of |hasRunningPlugin| if we want to
   // call it without paradoxically spawning the plugin.
   do_lookupGetter(obj, name) {
     return Object.prototype.__lookupGetter__.call(obj, name);
   }
 
   // Mimic the get*Pref API
-  getBoolPref(prefName, defaultValue) {
-    return this._getPref(prefName, "BOOL", { defaultValue });
+  getBoolPref(...args) {
+    return Services.prefs.getBoolPref(...args);
   }
-  getIntPref(prefName, defaultValue) {
-    return this._getPref(prefName, "INT", { defaultValue });
+  getIntPref(...args) {
+    return Services.prefs.getIntPref(...args);
   }
-  getCharPref(prefName, defaultValue) {
-    return this._getPref(prefName, "CHAR", { defaultValue });
+  getCharPref(...args) {
+    return Services.prefs.getCharPref(...args);
   }
   getComplexValue(prefName, iid) {
     return this._getPref(prefName, "COMPLEX", { iid });
   }
 
+  getParentBoolPref(prefName, defaultValue) {
+    return this._getParentPref(prefName, "BOOL", { defaultValue });
+  }
+  getParentIntPref(prefName, defaultValue) {
+    return this._getParentPref(prefName, "INT", { defaultValue });
+  }
+  getParentCharPref(prefName, defaultValue) {
+    return this._getParentPref(prefName, "CHAR", { defaultValue });
+  }
+
   // Mimic the set*Pref API
   setBoolPref(prefName, value) {
     return this._setPref(prefName, "BOOL", value);
   }
   setIntPref(prefName, value) {
     return this._setPref(prefName, "INT", value);
   }
   setCharPref(prefName, value) {
--- a/toolkit/components/url-classifier/tests/mochitest/classifierHelper.js
+++ b/toolkit/components/url-classifier/tests/mochitest/classifierHelper.js
@@ -34,22 +34,22 @@ classifierHelper.waitForInit = function(
 
 // This function is used to allow completion for specific "list",
 // some lists like "test-malware-simple" is default disabled to ask for complete.
 // "list" is the db we would like to allow it
 // "url" is the completion server
 classifierHelper.allowCompletion = function(lists, url) {
   for (var list of lists) {
     // Add test db to provider
-    var pref = SpecialPowers.getCharPref(PREFS.PROVIDER_LISTS);
+    var pref = await SpecialPowers.getParentCharPref(PREFS.PROVIDER_LISTS);
     pref += "," + list;
     SpecialPowers.setCharPref(PREFS.PROVIDER_LISTS, pref);
 
     // Rename test db so we will not disallow it from completions
-    pref = SpecialPowers.getCharPref(PREFS.DISALLOW_COMPLETIONS);
+    pref = await SpecialPowers.getParentCharPref(PREFS.DISALLOW_COMPLETIONS);
     pref = pref.replace(list, list + "-backup");
     SpecialPowers.setCharPref(PREFS.DISALLOW_COMPLETIONS, pref);
   }
 
   // Set get hash url
   SpecialPowers.setCharPref(PREFS.PROVIDER_GETHASHURL, url);
 };
 
--- a/toolkit/components/url-classifier/tests/mochitest/test_cachemiss.html
+++ b/toolkit/components/url-classifier/tests/mochitest/test_cachemiss.html
@@ -75,18 +75,18 @@ function reset() {
   return classifierHelper.resetDatabase();
 }
 
 // This test has to come before testPositiveCache to ensure gethash server doesn't
 // contain completions.
 function testNegativeCache() {
   shouldLoad = true;
 
-  function setup() {
-    classifierHelper.allowCompletion([MALWARE_LIST, UNWANTED_LIST], GETHASH_URL);
+  async function setup() {
+    await classifierHelper.allowCompletion([MALWARE_LIST, UNWANTED_LIST], GETHASH_URL);
 
     // Only add prefix to database. not server, so gethash will not return
     // result.
     return Promise.all([
       addPrefixToDB(MALWARE_LIST, MALWARE_HOST),
       addPrefixToDB(UNWANTED_LIST, UNWANTED_HOST),
     ]);
   }
@@ -103,18 +103,18 @@ function testNegativeCache() {
        ok(gCurGethashCounter == gPreGethashCounter, "Gethash request is nottriggered.");
 })
     .then(reset);
 }
 
 function testPositiveCache() {
   shouldLoad = false;
 
-  function setup() {
-    classifierHelper.allowCompletion([MALWARE_LIST, UNWANTED_LIST], GETHASH_URL);
+  async function setup() {
+    await classifierHelper.allowCompletion([MALWARE_LIST, UNWANTED_LIST], GETHASH_URL);
 
     return Promise.all([
       addPrefixToDB(MALWARE_LIST, MALWARE_HOST),
       addPrefixToDB(UNWANTED_LIST, UNWANTED_HOST),
       addCompletionToServer(MALWARE_LIST, MALWARE_HOST, GETHASH_URL),
       addCompletionToServer(UNWANTED_LIST, UNWANTED_HOST, GETHASH_URL),
     ]);
   }
--- a/toolkit/components/url-classifier/tests/mochitest/test_classify_ping.html
+++ b/toolkit/components/url-classifier/tests/mochitest/test_classify_ping.html
@@ -18,46 +18,46 @@
 
   const timeout = 200;
   const host_nottrack = "http://not-tracking.example.com/";
   const host_track = "http://trackertest.org/";
   const path_ping = "tests/toolkit/components/url-classifier/tests/mochitest/ping.sjs";
   const TP_ENABLE_PREF = "privacy.trackingprotection.enabled";
   const RETRY_TIMEOUT_MS = 200;
 
-  function testPingNonBlacklist() {
-    SpecialPowers.setBoolPref(TP_ENABLE_PREF, true);
+  async function testPingNonBlacklist() {
+    await SpecialPowers.setBoolPref(TP_ENABLE_PREF, true);
 
     var msg = "ping should reach page not in blacklist";
     var expectPing = true;
     var id = "1111";
     ping(id, host_nottrack);
 
     return new Promise(function(resolve, reject) {
       // Retry at most 30 seconds.
       isPingedWithRetry(id, expectPing, msg, resolve, 30 * 1000 / RETRY_TIMEOUT_MS);
     });
   }
 
-  function testPingBlacklistSafebrowsingOff() {
-    SpecialPowers.setBoolPref(TP_ENABLE_PREF, false);
+  async function testPingBlacklistSafebrowsingOff() {
+    await SpecialPowers.setBoolPref(TP_ENABLE_PREF, false);
 
     var msg = "ping should reach page in blacklist when tracking protection is off";
     var expectPing = true;
     var id = "2222";
     ping(id, host_track);
 
     return new Promise(function(resolve, reject) {
       // Retry at most 30 seconds.
       isPingedWithRetry(id, expectPing, msg, resolve, 30 * 1000 / RETRY_TIMEOUT_MS);
     });
   }
 
-  function testPingBlacklistSafebrowsingOn() {
-    SpecialPowers.setBoolPref(TP_ENABLE_PREF, true);
+  async function testPingBlacklistSafebrowsingOn() {
+    await SpecialPowers.setBoolPref(TP_ENABLE_PREF, true);
 
     var msg = "ping should not reach page in blacklist when tracking protection is on";
     var expectPing = false;
     var id = "3333";
     ping(id, host_track);
 
     return new Promise(function(resolve, reject) {
       setTimeout(function() {
--- a/toolkit/content/tests/chrome/test_preferences_onsyncfrompreference.xul
+++ b/toolkit/content/tests/chrome/test_preferences_onsyncfrompreference.xul
@@ -6,42 +6,44 @@
    - You can obtain one at http://mozilla.org/MPL/2.0/.  -->
 <window title="Preferences Window beforeaccept Tests"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
 
   <script type="application/javascript">
   <![CDATA[
+    const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
     const PREFS = ['tests.onsyncfrompreference.pref1',
                    'tests.onsyncfrompreference.pref2',
                    'tests.onsyncfrompreference.pref3'];
 
     SimpleTest.waitForExplicitFinish();
 
     for (let pref of PREFS) {
-      SpecialPowers.setIntPref(pref, 1);
+      Services.prefs.setIntPref(pref, 1);
     }
 
     let counter = 0;
     let prefWindow = openDialog("window_preferences_onsyncfrompreference.xul", "", "", onSync);
 
     SimpleTest.registerCleanupFunction(() => {
       for (let pref of PREFS) {
-        SpecialPowers.clearUserPref(pref);
+        Services.prefs.clearUserPref(pref);
       }
       prefWindow.close();
     });
 
     // Onsyncfrompreference handler for the prefs
     function onSync() {
       for (let pref of PREFS) {
         // The `value` field of each <preference> element should be initialized by now.
 
-        is(SpecialPowers.getIntPref(pref), prefWindow.Preferences.get(pref).value,
+        is(Services.prefs.getIntPref(pref), prefWindow.Preferences.get(pref).value,
            "Pref constructor was called correctly")
       }
 
       counter++;
 
       if (counter == PREFS.length) {
         SimpleTest.finish();
       }
--- a/widget/tests/window_composition_text_querycontent.xul
+++ b/widget/tests/window_composition_text_querycontent.xul
@@ -39,16 +39,18 @@
 </div>
 <pre id="test">
 </pre>
 </body>
 
 <script class="testbody" type="application/javascript">
 <![CDATA[
 
+const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+
 window.opener.wrappedJSObject.SimpleTest.waitForFocus(runTest, window);
 
 function ok(aCondition, aMessage)
 {
   window.opener.wrappedJSObject.SimpleTest.ok(aCondition, aMessage);
 }
 
 function is(aLeft, aRight, aMessage)
@@ -6053,17 +6055,17 @@ function runIsComposingTest()
   textarea.addEventListener("compositionstart", onComposition, true);
   textarea.addEventListener("compositionend", onComposition, true);
 
   textarea.focus();
   textarea.value = "";
 
   // XXX These cases shouldn't occur in actual native key events because we
   //     don't dispatch key events while composition (bug 354358).
-  SpecialPowers.setBoolPref("dom.keyboardevent.dispatch_during_composition", true);
+  Services.prefs.setBoolPref("dom.keyboardevent.dispatch_during_composition", true);
   description = "events before dispatching compositionstart";
   synthesizeKey("KEY_ArrowLeft");
 
   description = "events after dispatching compositionchange";
   synthesizeCompositionChange(
     { "composition":
       { "string": "\u3042",
         "clauses":
@@ -6083,17 +6085,17 @@ function runIsComposingTest()
   synthesizeComposition({ type: "compositioncommitasis",
                           key: { key: "KEY_Enter", code: "Enter", type: "keydown" } });
 
   // input event will be fired by synthesizing compositionend event.
   // Then, its isComposing should be false.
   description = "events after dispatching compositioncommitasis";
   synthesizeKey("KEY_Enter", {type: "keyup"});
 
-  SpecialPowers.clearUserPref("dom.keyboardevent.dispatch_during_composition");
+  Services.prefs.clearUserPref("dom.keyboardevent.dispatch_during_composition");
 
   textarea.removeEventListener("keydown", eventHandler, true);
   textarea.removeEventListener("keypress", eventHandler, true);
   textarea.removeEventListener("keyup", eventHandler, true);
   textarea.removeEventListener("input", eventHandler, true);
   textarea.removeEventListener("compositionstart", onComposition, true);
   textarea.removeEventListener("compositionend", onComposition, true);
 
@@ -6381,17 +6383,17 @@ function runNativeLineBreakerTest()
     result = { compositionupdate: null, compositionend: null };
   }
 
   function handler(aEvent)
   {
     result[aEvent.type] = aEvent.data;
   }
 
-  SpecialPowers.setBoolPref("dom.compositionevent.allow_control_characters", false);
+  Services.prefs.setBoolPref("dom.compositionevent.allow_control_characters", false);
 
   textarea.addEventListener("compositionupdate", handler, true);
   textarea.addEventListener("compositionend", handler, true);
 
   // '\n' in composition string shouldn't be changed.
   clearResult();
   textarea.value = "";
   var clauses = [ "abc\n", "def\n\ng", "hi\n", "\njkl" ];
@@ -6497,17 +6499,17 @@ function runNativeLineBreakerTest()
   synthesizeComposition({ type: "compositioncommit", data: clauses.join('') });
   checkSelection(clauses.join('').replace(/\r/g, "\n").length + (kLFLen - 1) * 5, "", "runNativeLineBreakerTest", "#6");
   is(result.compositionend, clauses.join('').replace(/\r/g, "\n"), "runNativeLineBreakerTest: \\r in compositionend.data should be replaced with \\n #6");
   is(textarea.value, clauses.join('').replace(/\r/g, "\n"), "runNativeLineBreakerTest: \\r in textarea.value should be replaced with \\n #6");
 
   textarea.removeEventListener("compositionupdate", handler, true);
   textarea.removeEventListener("compositionend", handler, true);
 
-  SpecialPowers.clearUserPref("dom.compositionevent.allow_control_characters");
+  Services.prefs.clearUserPref("dom.compositionevent.allow_control_characters");
 }
 
 function runControlCharTest()
 {
   textarea.focus();
 
   var result = {};
   function clearResult()
@@ -6559,17 +6561,17 @@ function runControlCharTest()
 
   is(result.compositionend, removedData, "runControlCharTest: control characters in event.data should be removed in compositionend event #2");
   is(textarea.value, removedData, "runControlCharTest: control characters should not appear in textarea #2");
 
   textarea.value = "";
 
   clearResult();
 
-  SpecialPowers.setBoolPref("dom.compositionevent.allow_control_characters", true);
+  Services.prefs.setBoolPref("dom.compositionevent.allow_control_characters", true);
 
   // input string contains control characters, allowing control characters
   clearResult();
   synthesizeCompositionChange(
     { "composition":
       { "string": data,
         "clauses":
         [
@@ -6587,17 +6589,17 @@ function runControlCharTest()
   is(result.compositionupdate, data.replace(/\r/g, "\n"), "runControlCharTest: control characters in event.data should not be removed in compositionupdate event #3");
   is(textarea.value, data.replace(/\r/g, "\n"), "runControlCharTest: control characters should appear in textarea #3");
 
   synthesizeComposition({ type: "compositioncommit", data: data });
 
   is(result.compositionend, data.replace(/\r/g, "\n"), "runControlCharTest: control characters in event.data should not be removed in compositionend event #4");
   is(textarea.value, data.replace(/\r/g, "\n"), "runControlCharTest: control characters should appear in textarea #4");
 
-  SpecialPowers.clearUserPref("dom.compositionevent.allow_control_characters");
+  Services.prefs.clearUserPref("dom.compositionevent.allow_control_characters");
 
   textarea.removeEventListener("compositionupdate", handler, true);
   textarea.removeEventListener("compositionend", handler, true);
 }
 
 function runRemoveContentTest()
 {
   var events = [];