Merge Autoland to mozilla-central r=merge a=merge
authorarthur.iakab <aiakab@mozilla.com>
Fri, 22 Dec 2017 00:15:51 +0200
changeset 448952 d20967c26da559a7781db467741bb240af9bfe5c
parent 448880 0843e4e6cb1d6e32a35ce4d7014bd250c24f74fa (current diff)
parent 448951 d473c27fc7f8dd95408e9323661d18dfacc589b8 (diff)
child 448988 f844e99f26b1f1537c7209a020b464ac7da2574f
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone59.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
Merge Autoland to mozilla-central r=merge a=merge
browser/base/content/test/general/browser_bug580956.js
browser/base/content/test/general/browser_visibleTabs_bookmarkAllTabs.js
browser/base/content/test/general/browser_visibleTabs_contextMenu.js
devtools/client/netmonitor/src/components/RequestListColumnRemoteIp.js
ipc/chromium/src/base/cpu.cc
ipc/chromium/src/base/cpu.h
ipc/chromium/src/base/sys_info.h
ipc/chromium/src/base/sys_info_mac.cc
ipc/chromium/src/base/sys_info_posix.cc
ipc/chromium/src/base/sys_info_win.cc
layout/reftests/position-sticky/inner-table-1-ref.html
layout/reftests/position-sticky/inner-table-1.html
testing/web-platform/meta/css/css-position/position-sticky-nested-table.html.ini
testing/web-platform/meta/css/css-position/position-sticky-table-tfoot-bottom.html.ini
testing/web-platform/meta/css/css-position/position-sticky-table-th-bottom.html.ini
testing/web-platform/meta/css/css-position/position-sticky-table-th-left.html.ini
testing/web-platform/meta/css/css-position/position-sticky-table-th-right.html.ini
testing/web-platform/meta/css/css-position/position-sticky-table-th-top.html.ini
testing/web-platform/meta/css/css-position/position-sticky-table-thead-top.html.ini
testing/web-platform/meta/css/css-position/position-sticky-table-tr-bottom.html.ini
testing/web-platform/meta/css/css-position/position-sticky-table-tr-top.html.ini
testing/web-platform/meta/css/cssom/CSSNamespaceRule.html.ini
testing/web-platform/meta/dom/events/EventListener-invoke-legacy.html.ini
widget/gtk/InProcessX11CompositorWidget.cpp
widget/gtk/InProcessX11CompositorWidget.h
widget/gtk/X11CompositorWidget.cpp
widget/gtk/X11CompositorWidget.h
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -32,17 +32,16 @@ module.exports = {
       // Browser: Bug 1421379
       "browser/extensions/shield-recipe-client/test/browser/head.js",
       "browser/modules/offlineAppCache.jsm",
       "devtools/**",
       "dom/indexedDB/**",
       "dom/media/**",
       "extensions/pref/**",
       "mobile/android/**",
-      "security/**",
       "testing/**",
       "tools/profiler/**",
     ],
     "rules": {
       "mozilla/use-services": "off",
     }
   }]
 };
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1341,16 +1341,18 @@ var gBrowserInit = {
 
     this._setInitialFocus();
 
     // Hack to ensure that the about:home favicon is loaded
     // instantaneously, to avoid flickering and improve perceived performance.
     this._uriToLoadPromise.then(uriToLoad => {
       if (uriToLoad == "about:home") {
         gBrowser.setIcon(gBrowser.selectedTab, "chrome://branding/content/icon32.png");
+      } else if (uriToLoad == "about:privatebrowsing") {
+        gBrowser.setIcon(gBrowser.selectedTab, "chrome://browser/skin/privatebrowsing/favicon.svg");
       }
     });
 
     // Wait until chrome is painted before executing code not critical to making the window visible
     this._boundDelayedStartup = this._delayedStartup.bind(this);
     window.addEventListener("MozAfterPaint", this._boundDelayedStartup);
 
     this._loadHandled = true;
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -796,17 +796,26 @@
                       URLBarSetURI();
                     }
                   } else if (isSuccessful) {
                     this.mBrowser.urlbarChangeTracker.finishedLoad();
                   }
 
                   // Ignore initial about:blank to prevent flickering.
                   if (!this.mBrowser.mIconURL && !ignoreBlank) {
-                    this.mTabBrowser.useDefaultIcon(this.mTab);
+                    // Don't switch to the default icon on about:home or about:newtab,
+                    // since these pages get their favicon set in browser code to
+                    // improve perceived performance.
+                    let isNewTab = originalLocation &&
+                      (originalLocation.spec == "about:newtab" ||
+                       originalLocation.spec == "about:privatebrowsing" ||
+                       originalLocation.spec == "about:home");
+                    if (!isNewTab) {
+                      this.mTabBrowser.useDefaultIcon(this.mTab);
+                    }
                   }
                 }
 
                 // For keyword URIs clear the user typed value since they will be changed into real URIs
                 if (location.scheme == "keyword")
                   this.mBrowser.userTypedValue = null;
 
                 if (this.mTab.selected)
@@ -2808,18 +2817,20 @@
                 let notificationbox = this.getNotificationBox(t.linkedBrowser);
                 notificationbox.remove();
               }
               throw e;
             }
 
             // Hack to ensure that the about:newtab favicon is loaded
             // instantaneously, to avoid flickering and improve perceived performance.
-            if (aURI == BROWSER_NEW_TAB_URL) {
+            if (aURI == "about:newtab") {
               this.setIcon(t, "chrome://branding/content/icon32.png");
+            } else if (aURI == "about:privatebrowsing") {
+              this.setIcon(t, "chrome://browser/skin/privatebrowsing/favicon.svg");
             }
 
             // Dispatch a new tab notification.  We do this once we're
             // entirely done, so that things are in a consistent state
             // even if the event listener opens or closes tabs.
             var detail = aEventDetail || {};
             var evt = new CustomEvent("TabOpen", { bubbles: true, detail });
             t.dispatchEvent(evt);
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -211,18 +211,16 @@ skip-if = toolkit != "cocoa" # Because o
 [browser_bug575830.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_bug577121.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_bug578534.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_bug579872.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
-[browser_bug580956.js]
-# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_bug581242.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_bug581253.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_bug585785.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_bug585830.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
@@ -535,20 +533,16 @@ support-files =
 [browser_visibleFindSelection.js]
 skip-if = true # Bug 1409184 disabled because interactive find next is not automating properly
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_visibleTabs.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_visibleTabs_bookmarkAllPages.js]
 skip-if = true # Bug 1005420 - fails intermittently. also with e10s enabled: bizarre problem with hidden tab having _mouseenter called, via _setPositionalAttributes, and tab not being found resulting in 'candidate is undefined'
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
-[browser_visibleTabs_bookmarkAllTabs.js]
-# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
-[browser_visibleTabs_contextMenu.js]
-# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_visibleTabs_tabPreview.js]
 skip-if = (os == "win" && !debug)
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_web_channel.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_zbug569342.js]
 skip-if = e10s || debug # Bug 1094240 - has findbar-related failures
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -52,35 +52,16 @@ function whenDelayedStartupFinished(aWin
   Services.obs.addObserver(function observer(aSubject, aTopic) {
     if (aWindow == aSubject) {
       Services.obs.removeObserver(observer, aTopic);
       executeSoon(aCallback);
     }
   }, "browser-delayed-startup-finished");
 }
 
-function updateTabContextMenu(tab, onOpened) {
-  let menu = document.getElementById("tabContextMenu");
-  if (!tab)
-    tab = gBrowser.selectedTab;
-  var evt = new Event("");
-  tab.dispatchEvent(evt);
-  menu.openPopup(tab, "end_after", 0, 0, true, false, evt);
-  is(TabContextMenu.contextTab, tab, "TabContextMenu context is the expected tab");
-  const onFinished = () => menu.hidePopup();
-  if (onOpened) {
-    return (async function() {
-      await onOpened();
-      onFinished();
-    })();
-  }
-  onFinished();
-  return Promise.resolve();
-}
-
 function openToolbarCustomizationUI(aCallback, aBrowserWin) {
   if (!aBrowserWin)
     aBrowserWin = window;
 
   aBrowserWin.gCustomizeMode.enter();
 
   aBrowserWin.gNavToolbox.addEventListener("customizationready", function() {
     executeSoon(function() {
--- a/browser/base/content/test/sync/browser_contextmenu_sendtab.js
+++ b/browser/base/content/test/sync/browser_contextmenu_sendtab.js
@@ -6,81 +6,92 @@
 const chrome_base = "chrome://mochitests/content/browser/browser/base/content/test/general/";
 Services.scriptloader.loadSubScript(chrome_base + "head.js", this);
 /* import-globals-from ../general/head.js */
 
 const remoteClientsFixture = [ { id: 1, name: "Foo"}, { id: 2, name: "Bar"} ];
 
 let [testTab] = gBrowser.visibleTabs;
 
+function updateTabContextMenu(tab) {
+  let menu = document.getElementById("tabContextMenu");
+  if (!tab)
+    tab = gBrowser.selectedTab;
+  var evt = new Event("");
+  tab.dispatchEvent(evt);
+  menu.openPopup(tab, "end_after", 0, 0, true, false, evt);
+  is(TabContextMenu.contextTab, tab, "TabContextMenu context is the expected tab");
+  menu.hidePopup();
+}
+
 add_task(async function setup() {
   await promiseSyncReady();
   is(gBrowser.visibleTabs.length, 1, "there is one visible tab");
 });
 
 // We are not testing the devices popup contents, since it is already tested by
 // browser_contextmenu_sendpage.js and the code to populate it is the same.
 
 add_task(async function test_tab_contextmenu() {
   const sandbox = setupSendTabMocks({ syncReady: true, clientsSynced: true, remoteClients: remoteClientsFixture,
                                       state: UIState.STATUS_SIGNED_IN, isSendableURI: true });
 
-  await updateTabContextMenu(testTab);
+  updateTabContextMenu(testTab);
   is(document.getElementById("context_sendTabToDevice").hidden, false, "Send tab to device is shown");
   is(document.getElementById("context_sendTabToDevice").disabled, false, "Send tab to device is enabled");
 
   sandbox.restore();
 });
 
 add_task(async function test_tab_contextmenu_unconfigured() {
   const sandbox = setupSendTabMocks({ syncReady: true, clientsSynced: true, remoteClients: remoteClientsFixture,
                                       state: UIState.STATUS_NOT_CONFIGURED, isSendableURI: true });
 
-  await updateTabContextMenu(testTab);
+  updateTabContextMenu(testTab);
   is(document.getElementById("context_sendTabToDevice").hidden, false, "Send tab to device is shown");
   is(document.getElementById("context_sendTabToDevice").disabled, false, "Send tab to device is enabled");
 
   sandbox.restore();
 });
 
 add_task(async function test_tab_contextmenu_not_sendable() {
   const sandbox = setupSendTabMocks({ syncReady: true, clientsSynced: true, remoteClients: [{ id: 1, name: "Foo"}],
                                       state: UIState.STATUS_SIGNED_IN, isSendableURI: false });
 
-  await updateTabContextMenu(testTab);
+  updateTabContextMenu(testTab);
   is(document.getElementById("context_sendTabToDevice").hidden, false, "Send tab to device is shown");
   is(document.getElementById("context_sendTabToDevice").disabled, true, "Send tab to device is disabled");
 
   sandbox.restore();
 });
 
 add_task(async function test_tab_contextmenu_not_synced_yet() {
   const sandbox = setupSendTabMocks({ syncReady: true, clientsSynced: false, remoteClients: [],
                                       state: UIState.STATUS_SIGNED_IN, isSendableURI: true });
 
-  await updateTabContextMenu(testTab);
+  updateTabContextMenu(testTab);
   is(document.getElementById("context_sendTabToDevice").hidden, false, "Send tab to device is shown");
   is(document.getElementById("context_sendTabToDevice").disabled, true, "Send tab to device is disabled");
 
   sandbox.restore();
 });
 
 add_task(async function test_tab_contextmenu_sync_not_ready_configured() {
   const sandbox = setupSendTabMocks({ syncReady: false, clientsSynced: false, remoteClients: null,
                                       state: UIState.STATUS_SIGNED_IN, isSendableURI: true });
 
-  await updateTabContextMenu(testTab);
+  updateTabContextMenu(testTab);
   is(document.getElementById("context_sendTabToDevice").hidden, false, "Send tab to device is shown");
   is(document.getElementById("context_sendTabToDevice").disabled, true, "Send tab to device is disabled");
 
   sandbox.restore();
 });
 
 add_task(async function test_tab_contextmenu_sync_not_ready_other_state() {
   const sandbox = setupSendTabMocks({ syncReady: false, clientsSynced: false, remoteClients: null,
                                       state: UIState.STATUS_NOT_VERIFIED, isSendableURI: true });
 
-  await updateTabContextMenu(testTab);
+  updateTabContextMenu(testTab);
   is(document.getElementById("context_sendTabToDevice").hidden, false, "Send tab to device is shown");
   is(document.getElementById("context_sendTabToDevice").disabled, false, "Send tab to device is enabled");
 
   sandbox.restore();
 });
--- a/browser/base/content/test/tabs/browser.ini
+++ b/browser/base/content/test/tabs/browser.ini
@@ -1,17 +1,20 @@
 [DEFAULT]
 support-files =
+  head.js
   dummy_page.html
-  test_bug1358314.html
 
 [browser_abandonment_telemetry.js]
 [browser_accessibility_indicator.js]
 [browser_allow_process_switches_despite_related_browser.js]
+[browser_bug580956.js]
 [browser_contextmenu_openlink_after_tabnavigated.js]
+support-files =
+  test_bug1358314.html
 [browser_isLocalAboutURI.js]
 [browser_tabCloseProbes.js]
 [browser_tabSpinnerProbe.js]
 skip-if = !e10s # Tab spinner is e10s only.
 [browser_tabSwitchPrintPreview.js]
 skip-if = os == 'mac'
 [browser_navigatePinnedTab.js]
 [browser_new_file_whitelisted_http_tab.js]
@@ -22,9 +25,11 @@ skip-if = !e10s # Pref and test only rel
 [browser_overflowScroll.js]
 [browser_pinnedTabs.js]
 [browser_pinnedTabs_closeByKeyboard.js]
 [browser_positional_attributes.js]
 [browser_preloadedBrowser_zoom.js]
 [browser_reload_deleted_file.js]
 [browser_tabswitch_updatecommands.js]
 [browser_viewsource_of_data_URI_in_file_process.js]
+[browser_visibleTabs_bookmarkAllTabs.js]
+[browser_visibleTabs_contextMenu.js]
 [browser_open_newtab_start_observer_notification.js]
rename from browser/base/content/test/general/browser_bug580956.js
rename to browser/base/content/test/tabs/browser_bug580956.js
rename from browser/base/content/test/general/browser_visibleTabs_bookmarkAllTabs.js
rename to browser/base/content/test/tabs/browser_visibleTabs_bookmarkAllTabs.js
rename from browser/base/content/test/general/browser_visibleTabs_contextMenu.js
rename to browser/base/content/test/tabs/browser_visibleTabs_contextMenu.js
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabs/head.js
@@ -0,0 +1,11 @@
+function updateTabContextMenu(tab) {
+  let menu = document.getElementById("tabContextMenu");
+  if (!tab)
+    tab = gBrowser.selectedTab;
+  var evt = new Event("");
+  tab.dispatchEvent(evt);
+  menu.openPopup(tab, "end_after", 0, 0, true, false, evt);
+  is(TabContextMenu.contextTab, tab, "TabContextMenu context is the expected tab");
+  menu.hidePopup();
+}
+
--- a/browser/components/privatebrowsing/content/aboutPrivateBrowsing.js
+++ b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.js
@@ -4,17 +4,16 @@
 
 var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const FAVICON_QUESTION = "chrome://global/skin/icons/question-32.png";
-const FAVICON_PRIVACY = "chrome://browser/skin/privatebrowsing/favicon.svg";
 
 var stringBundle = Services.strings.createBundle(
                     "chrome://browser/locale/aboutPrivateBrowsing.properties");
 
 var prefBranch = Services.prefs.getBranch("privacy.trackingprotection.");
 var prefObserver = {
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                         Ci.nsISupportsWeakReference]),
@@ -33,20 +32,16 @@ var prefObserver = {
    title.classList.toggle("hide", trackingEnabled);
    titleTracking.classList.toggle("hide", !trackingEnabled);
    tpSubHeader.classList.toggle("tp-off", !trackingEnabled);
  }
 };
 prefBranch.addObserver("pbmode.enabled", prefObserver, true);
 prefBranch.addObserver("enabled", prefObserver, true);
 
-function setFavIcon(url) {
- document.getElementById("favicon").setAttribute("href", url);
-}
-
 document.addEventListener("DOMContentLoaded", function() {
  if (!PrivateBrowsingUtils.isContentWindowPrivate(window)) {
    document.documentElement.classList.remove("private");
    document.documentElement.classList.add("normal");
    document.title = stringBundle.GetStringFromName("title.normal");
    document.getElementById("favicon")
            .setAttribute("href", FAVICON_QUESTION);
    document.getElementById("startPrivateBrowsing")
@@ -55,18 +50,16 @@ document.addEventListener("DOMContentLoa
  }
 
  let tpToggle = document.getElementById("tpToggle");
  document.getElementById("tpButton").addEventListener("click", () => {
    tpToggle.click();
  });
 
  document.title = stringBundle.GetStringFromName("title.head");
- document.getElementById("favicon")
-         .setAttribute("href", FAVICON_PRIVACY);
  tpToggle.addEventListener("change", toggleTrackingProtection);
  document.getElementById("startTour")
          .addEventListener("click", dontShowIntroPanelAgain);
 
  document.getElementById("startTour").setAttribute("href",
    Services.urlFormatter.formatURLPref("privacy.trackingprotection.introURL"));
  document.getElementById("learnMore").setAttribute("href",
    Services.urlFormatter.formatURLPref("app.support.baseURL") + "private-browsing");
--- a/browser/extensions/shield-recipe-client/lib/AddonStudies.jsm
+++ b/browser/extensions/shield-recipe-client/lib/AddonStudies.jsm
@@ -115,21 +115,22 @@ this.AddonStudies = {
     return function wrapper(testFunction) {
       return async function wrappedTestFunction(...args) {
         const oldStudies = await AddonStudies.getAll();
         let db = await getDatabase();
         await AddonStudies.clear();
         for (const study of studies) {
           await getStore(db).add(study);
         }
+        await AddonStudies.close();
 
         try {
           await testFunction(...args, studies);
         } finally {
-          db = await getDatabase(); // Re-acquire in case the test closed the connection.
+          db = await getDatabase();
           await AddonStudies.clear();
           for (const study of oldStudies) {
             await getStore(db).add(study);
           }
 
           await AddonStudies.close();
         }
       };
--- a/browser/extensions/shield-recipe-client/test/browser/browser_AddonStudies.js
+++ b/browser/extensions/shield-recipe-client/test/browser/browser_AddonStudies.js
@@ -4,16 +4,26 @@ Cu.import("resource://gre/modules/Indexe
 Cu.import("resource://testing-common/TestUtils.jsm", this);
 Cu.import("resource://testing-common/AddonTestUtils.jsm", this);
 Cu.import("resource://shield-recipe-client/lib/Addons.jsm", this);
 Cu.import("resource://shield-recipe-client/lib/AddonStudies.jsm", this);
 
 // Initialize test utils
 AddonTestUtils.initMochitest(this);
 
+let _startArgsFactoryId = 1;
+function startArgsFactory(args) {
+  return Object.assign({
+    recipeId: _startArgsFactoryId++,
+    name: "Test",
+    description: "Test",
+    addonUrl: "http://test/addon.xpi",
+  }, args);
+}
+
 decorate_task(
   AddonStudies.withStudies(),
   async function testGetMissing() {
     is(
       await AddonStudies.get("does-not-exist"),
       null,
       "get returns null when the requested study does not exist"
     );
@@ -98,26 +108,16 @@ decorate_task(
     const hasAny = (
       (await AddonStudies.has(study1.recipeId)) ||
       (await AddonStudies.has(study2.recipeId))
     );
     ok(!hasAny, "After calling clear, all studies are removed from storage.");
   }
 );
 
-let _startArgsFactoryId = 0;
-function startArgsFactory(args) {
-  return Object.assign({
-    recipeId: _startArgsFactoryId++,
-    name: "Test",
-    description: "Test",
-    addonUrl: "http://test/addon.xpi",
-  }, args);
-}
-
 add_task(async function testStartRequiredArguments() {
   const requiredArguments = startArgsFactory();
   for (const key in requiredArguments) {
     const args = Object.assign({}, requiredArguments);
     delete args[key];
     Assert.rejects(
       AddonStudies.start(args),
       /Required arguments/,
@@ -170,16 +170,17 @@ decorate_task(
     );
 
     await Addons.uninstall(testOverwriteId);
   }
 );
 
 decorate_task(
   withWebExtension({version: "2.0"}),
+  AddonStudies.withStudies(),
   async function testStart([addonId, addonFile]) {
     const startupPromise = AddonTestUtils.promiseWebExtensionStartup(addonId);
     const addonUrl = Services.io.newFileURI(addonFile).spec;
 
     let addon = await Addons.get(addonId);
     is(addon, null, "Before start is called, the add-on is not installed.");
 
     const args = startArgsFactory({
@@ -205,17 +206,17 @@ decorate_task(
         addonUrl,
         active: true,
         studyStartDate: study.studyStartDate,
       },
       "start saves study data to storage",
     );
     ok(study.studyStartDate, "start assigns a value to the study start date.");
 
-    await Addons.uninstall(addonId);
+    await AddonStudies.stop(args.recipeId);
   }
 );
 
 decorate_task(
   AddonStudies.withStudies(),
   async function testStopNoStudy() {
     await Assert.rejects(
       AddonStudies.stop("does-not-exist"),
--- a/browser/extensions/shield-recipe-client/test/browser/browser_NormandyDriver.js
+++ b/browser/extensions/shield-recipe-client/test/browser/browser_NormandyDriver.js
@@ -1,14 +1,15 @@
 "use strict";
 
 Cu.import("resource://gre/modules/AppConstants.jsm");
 Cu.import("resource://testing-common/AddonTestUtils.jsm", this);
 Cu.import("resource://shield-recipe-client/lib/AddonStudies.jsm", this);
 Cu.import("resource://shield-recipe-client/lib/NormandyDriver.jsm", this);
+Cu.import("resource://shield-recipe-client/lib/PreferenceExperiments.jsm", this);
 
 add_task(withDriver(Assert, async function uuids(driver) {
   // Test that it is a UUID
   const uuid1 = driver.uuid();
   ok(UUID_REGEX.test(uuid1), "valid uuid format");
 
   // Test that UUIDs are different each time
   const uuid2 = driver.uuid();
@@ -187,16 +188,17 @@ decorate_task(
 
     await driver.addons.uninstall(ADDON_ID);
   }
 );
 
 decorate_task(
   withSandboxManager(Assert),
   withWebExtension({id: "driver-addon-studies@example.com"}),
+  AddonStudies.withStudies(),
   async function testAddonStudies(sandboxManager, [addonId, addonFile]) {
     const addonUrl = Services.io.newFileURI(addonFile).spec;
     const driver = new NormandyDriver(sandboxManager);
     sandboxManager.cloneIntoGlobal("driver", driver, {cloneFunctions: true});
 
     // Assertion helpers
     sandboxManager.addGlobal("is", is);
     sandboxManager.addGlobal("ok", ok);
@@ -314,17 +316,18 @@ decorate_task(
       })();
     `);
   })
 );
 
 decorate_task(
   withSandboxManager(Assert),
   withMockPreferences,
-  async function testAddonStudies(sandboxManager) {
+  PreferenceExperiments.withMockExperiments,
+  async function testPreferenceStudies(sandboxManager) {
     const driver = new NormandyDriver(sandboxManager);
     sandboxManager.cloneIntoGlobal("driver", driver, {cloneFunctions: true});
 
     // Assertion helpers
     sandboxManager.addGlobal("is", is);
     sandboxManager.addGlobal("ok", ok);
 
     await sandboxManager.evalInSandbox(`
--- a/browser/extensions/shield-recipe-client/test/browser/browser_PreferenceExperiments.js
+++ b/browser/extensions/shield-recipe-client/test/browser/browser_PreferenceExperiments.js
@@ -1,13 +1,14 @@
 "use strict";
 
 Cu.import("resource://gre/modules/Preferences.jsm", this);
 Cu.import("resource://gre/modules/TelemetryEnvironment.jsm", this);
 Cu.import("resource://shield-recipe-client/lib/PreferenceExperiments.jsm", this);
+Cu.import("resource://shield-recipe-client/lib/CleanupManager.jsm", this);
 
 // Save ourselves some typing
 const {withMockExperiments} = PreferenceExperiments;
 const DefaultPreferences = new Preferences({defaultBranch: true});
 const startupPrefs = "extensions.shield-recipe-client.startupExperimentPrefs";
 
 function experimentFactory(attrs) {
   return Object.assign({
@@ -20,72 +21,84 @@ function experimentFactory(attrs) {
     preferenceType: "string",
     previousPreferenceValue: "oldfakevalue",
     preferenceBranchType: "default",
     experimentType: "exp",
   }, attrs);
 }
 
 // clearAllExperimentStorage
-add_task(withMockExperiments(async function(experiments) {
-  experiments.test = experimentFactory({name: "test"});
-  ok(await PreferenceExperiments.has("test"), "Mock experiment is detected.");
-  await PreferenceExperiments.clearAllExperimentStorage();
-  ok(
-    !(await PreferenceExperiments.has("test")),
-    "clearAllExperimentStorage removed all stored experiments",
-  );
-}));
+decorate_task(
+  withMockExperiments,
+  async function(experiments) {
+    experiments.test = experimentFactory({name: "test"});
+    ok(await PreferenceExperiments.has("test"), "Mock experiment is detected.");
+    await PreferenceExperiments.clearAllExperimentStorage();
+    ok(
+      !(await PreferenceExperiments.has("test")),
+      "clearAllExperimentStorage removed all stored experiments",
+    );
+  }
+);
 
 // start should throw if an experiment with the given name already exists
-add_task(withMockExperiments(async function(experiments) {
-  experiments.test = experimentFactory({name: "test"});
-  await Assert.rejects(
-    PreferenceExperiments.start({
-      name: "test",
-      branch: "branch",
-      preferenceName: "fake.preference",
-      preferenceValue: "value",
-      preferenceType: "string",
-      preferenceBranchType: "default",
-    }),
-    "start threw an error due to a conflicting experiment name",
-  );
-}));
+decorate_task(
+  withMockExperiments,
+  async function(experiments) {
+    experiments.test = experimentFactory({name: "test"});
+    await Assert.rejects(
+      PreferenceExperiments.start({
+        name: "test",
+        branch: "branch",
+        preferenceName: "fake.preference",
+        preferenceValue: "value",
+        preferenceType: "string",
+        preferenceBranchType: "default",
+      }),
+      "start threw an error due to a conflicting experiment name",
+    );
+  }
+);
 
 // start should throw if an experiment for the given preference is active
-add_task(withMockExperiments(async function(experiments) {
-  experiments.test = experimentFactory({name: "test", preferenceName: "fake.preference"});
-  await Assert.rejects(
-    PreferenceExperiments.start({
-      name: "different",
-      branch: "branch",
-      preferenceName: "fake.preference",
-      preferenceValue: "value",
-      preferenceType: "string",
-      preferenceBranchType: "default",
-    }),
-    "start threw an error due to an active experiment for the given preference",
-  );
-}));
+decorate_task(
+  withMockExperiments,
+  async function(experiments) {
+    experiments.test = experimentFactory({name: "test", preferenceName: "fake.preference"});
+    await Assert.rejects(
+      PreferenceExperiments.start({
+        name: "different",
+        branch: "branch",
+        preferenceName: "fake.preference",
+        preferenceValue: "value",
+        preferenceType: "string",
+        preferenceBranchType: "default",
+      }),
+      "start threw an error due to an active experiment for the given preference",
+    );
+  }
+);
 
 // start should throw if an invalid preferenceBranchType is given
-add_task(withMockExperiments(async function() {
-  await Assert.rejects(
-    PreferenceExperiments.start({
-      name: "test",
-      branch: "branch",
-      preferenceName: "fake.preference",
-      preferenceValue: "value",
-      preferenceType: "string",
-      preferenceBranchType: "invalid",
-    }),
-    "start threw an error due to an invalid preference branch type",
-  );
-}));
+decorate_task(
+  withMockExperiments,
+  async function() {
+    await Assert.rejects(
+      PreferenceExperiments.start({
+        name: "test",
+        branch: "branch",
+        preferenceName: "fake.preference",
+        preferenceValue: "value",
+        preferenceType: "string",
+        preferenceBranchType: "invalid",
+      }),
+      "start threw an error due to an invalid preference branch type",
+    );
+  }
+);
 
 // start should save experiment data, modify the preference, and register a
 // watcher.
 decorate_task(
   withMockExperiments,
   withMockPreferences,
   withStub(PreferenceExperiments, "startObserver"),
   async function testStart(experiments, mockPreferences, startObserverStub) {
@@ -134,235 +147,273 @@ decorate_task(
       Preferences.get(`${startupPrefs}.fake.preference`),
       "newvalue",
       "start saved the experiment value to the startup prefs tree",
     );
   },
 );
 
 // start should modify the user preference for the user branch type
-add_task(withMockExperiments(withMockPreferences(async function(experiments, mockPreferences) {
-  const startObserver = sinon.stub(PreferenceExperiments, "startObserver");
-  mockPreferences.set("fake.preference", "oldvalue", "user");
-  mockPreferences.set("fake.preference", "olddefaultvalue", "default");
+decorate_task(
+  withMockExperiments,
+  withMockPreferences,
+  withStub(PreferenceExperiments, "startObserver"),
+  async function(experiments, mockPreferences, startObserver) {
+    mockPreferences.set("fake.preference", "olddefaultvalue", "default");
+    mockPreferences.set("fake.preference", "oldvalue", "user");
 
-  await PreferenceExperiments.start({
-    name: "test",
-    branch: "branch",
-    preferenceName: "fake.preference",
-    preferenceValue: "newvalue",
-    preferenceType: "string",
-    preferenceBranchType: "user",
-  });
-  ok(
-    startObserver.calledWith("test", "fake.preference", "string", "newvalue"),
-    "start registered an observer",
-  );
+    await PreferenceExperiments.start({
+      name: "test",
+      branch: "branch",
+      preferenceName: "fake.preference",
+      preferenceValue: "newvalue",
+      preferenceType: "string",
+      preferenceBranchType: "user",
+    });
+    ok(
+      startObserver.calledWith("test", "fake.preference", "string", "newvalue"),
+      "start registered an observer",
+    );
 
-  const expectedExperiment = {
-    name: "test",
-    branch: "branch",
-    expired: false,
-    preferenceName: "fake.preference",
-    preferenceValue: "newvalue",
-    preferenceType: "string",
-    previousPreferenceValue: "oldvalue",
-    preferenceBranchType: "user",
-  };
+    const expectedExperiment = {
+      name: "test",
+      branch: "branch",
+      expired: false,
+      preferenceName: "fake.preference",
+      preferenceValue: "newvalue",
+      preferenceType: "string",
+      previousPreferenceValue: "oldvalue",
+      preferenceBranchType: "user",
+    };
 
-  const experiment = {};
-  Object.keys(expectedExperiment).forEach(key => experiment[key] = experiments.test[key]);
-  Assert.deepEqual(experiment, expectedExperiment, "start saved the experiment");
+    const experiment = {};
+    Object.keys(expectedExperiment).forEach(key => experiment[key] = experiments.test[key]);
+    Assert.deepEqual(experiment, expectedExperiment, "start saved the experiment");
 
-  Assert.notEqual(
-    DefaultPreferences.get("fake.preference"),
-    "newvalue",
-    "start did not modify the default preference",
-  );
-  is(Preferences.get("fake.preference"), "newvalue", "start modified the user preference");
-
-  startObserver.restore();
-})));
+    Assert.notEqual(
+      DefaultPreferences.get("fake.preference"),
+      "newvalue",
+      "start did not modify the default preference",
+    );
+    is(Preferences.get("fake.preference"), "newvalue", "start modified the user preference");
+  }
+);
 
 // start should detect if a new preference value type matches the previous value type
-add_task(withMockPreferences(async function(mockPreferences) {
-  mockPreferences.set("fake.type_preference", "oldvalue");
+decorate_task(
+  withMockPreferences,
+  async function(mockPreferences) {
+    mockPreferences.set("fake.type_preference", "oldvalue");
 
-  await Assert.rejects(
-    PreferenceExperiments.start({
-      name: "test",
-      branch: "branch",
-      preferenceName: "fake.type_preference",
-      preferenceBranchType: "user",
-      preferenceValue: 12345,
-      preferenceType: "integer",
-    }),
-    "start threw error for incompatible preference type"
-  );
-}));
-
+    await Assert.rejects(
+      PreferenceExperiments.start({
+        name: "test",
+        branch: "branch",
+        preferenceName: "fake.type_preference",
+        preferenceBranchType: "user",
+        preferenceValue: 12345,
+        preferenceType: "integer",
+      }),
+      "start threw error for incompatible preference type"
+    );
+  }
+);
 
 // startObserver should throw if an observer for the experiment is already
 // active.
-add_task(withMockExperiments(async function() {
-  PreferenceExperiments.startObserver("test", "fake.preference", "string", "newvalue");
-  Assert.throws(
-    () => PreferenceExperiments.startObserver("test", "another.fake", "string", "othervalue"),
-    "startObserver threw due to a conflicting active observer",
-  );
-  PreferenceExperiments.stopAllObservers();
-}));
+decorate_task(
+  withMockExperiments,
+  async function() {
+    PreferenceExperiments.startObserver("test", "fake.preference", "string", "newvalue");
+    Assert.throws(
+      () => PreferenceExperiments.startObserver("test", "another.fake", "string", "othervalue"),
+      "startObserver threw due to a conflicting active observer",
+    );
+    PreferenceExperiments.stopAllObservers();
+  }
+);
 
 // startObserver should register an observer that calls stop when a preference
 // changes from its experimental value.
-add_task(withMockExperiments(withMockPreferences(async function(mockExperiments, mockPreferences) {
-  const tests = [
-    ["string", "startvalue", "experimentvalue", "newvalue"],
-    ["boolean", false, true, false],
-    ["integer", 1, 2, 42],
-  ];
+decorate_task(
+  withMockExperiments,
+  withMockPreferences,
+  async function(mockExperiments, mockPreferences) {
+    const tests = [
+      ["string", "startvalue", "experimentvalue", "newvalue"],
+      ["boolean", false, true, false],
+      ["integer", 1, 2, 42],
+    ];
+
+    for (const [type, startvalue, experimentvalue, newvalue] of tests) {
+      const stop = sinon.stub(PreferenceExperiments, "stop");
+      mockPreferences.set("fake.preference" + type, startvalue);
+
+      // NOTE: startObserver does not modify the pref
+      PreferenceExperiments.startObserver("test" + type, "fake.preference" + type, type, experimentvalue);
 
-  for (const [type, startvalue, experimentvalue, newvalue] of tests) {
-    const stop = sinon.stub(PreferenceExperiments, "stop");
-    mockPreferences.set("fake.preference" + type, startvalue);
+      // Setting it to the experimental value should not trigger the call.
+      mockPreferences.set("fake.preference" + type, experimentvalue);
+      ok(!stop.called, "Changing to the experimental pref value did not trigger the observer");
+
+      // Setting it to something different should trigger the call.
+      mockPreferences.set("fake.preference" + type, newvalue);
+      ok(stop.called, "Changing to a different value triggered the observer");
+
+      PreferenceExperiments.stopAllObservers();
+      stop.restore();
+    }
+  }
+);
+
+decorate_task(
+  withMockExperiments,
+  async function testHasObserver() {
+    PreferenceExperiments.startObserver("test", "fake.preference", "string", "experimentValue");
 
-    // NOTE: startObserver does not modify the pref
-    PreferenceExperiments.startObserver("test" + type, "fake.preference" + type, type, experimentvalue);
+    ok(await PreferenceExperiments.hasObserver("test"), "hasObserver should detect active observers");
+    ok(
+      !(await PreferenceExperiments.hasObserver("missing")),
+      "hasObserver shouldn't detect inactive observers",
+    );
+
+    PreferenceExperiments.stopAllObservers();
+  }
+);
+
+// stopObserver should throw if there is no observer active for it to stop.
+decorate_task(
+  withMockExperiments,
+  async function() {
+    Assert.throws(
+      () => PreferenceExperiments.stopObserver("neveractive", "another.fake", "othervalue"),
+      "stopObserver threw because there was not matching active observer",
+    );
+  }
+);
 
-    // Setting it to the experimental value should not trigger the call.
-    Preferences.set("fake.preference" + type, experimentvalue);
-    ok(!stop.called, "Changing to the experimental pref value did not trigger the observer");
+// stopObserver should cancel an active observer.
+decorate_task(
+  withMockExperiments,
+  withMockPreferences,
+  async function(mockExperiments, mockPreferences) {
+    const stop = sinon.stub(PreferenceExperiments, "stop");
+    mockPreferences.set("fake.preference", "startvalue");
+
+    PreferenceExperiments.startObserver("test", "fake.preference", "string", "experimentvalue");
+    PreferenceExperiments.stopObserver("test");
 
-    // Setting it to something different should trigger the call.
-    Preferences.set("fake.preference" + type, newvalue);
-    ok(stop.called, "Changing to a different value triggered the observer");
+    // Setting the preference now that the observer is stopped should not call
+    // stop.
+    mockPreferences.set("fake.preference", "newvalue");
+    ok(!stop.called, "stopObserver successfully removed the observer");
+
+    // Now that the observer is stopped, start should be able to start a new one
+    // without throwing.
+    try {
+      PreferenceExperiments.startObserver("test", "fake.preference", "string", "experimentvalue");
+    } catch (err) {
+      ok(false, "startObserver did not throw an error for an observer that was already stopped");
+    }
 
     PreferenceExperiments.stopAllObservers();
     stop.restore();
   }
-})));
-
-add_task(withMockExperiments(async function testHasObserver() {
-  PreferenceExperiments.startObserver("test", "fake.preference", "string", "experimentValue");
-
-  ok(await PreferenceExperiments.hasObserver("test"), "hasObserver detects active observers");
-  ok(
-    !(await PreferenceExperiments.hasObserver("missing")),
-    "hasObserver doesn't detect inactive observers",
-  );
-
-  PreferenceExperiments.stopAllObservers();
-}));
-
-// stopObserver should throw if there is no observer active for it to stop.
-add_task(withMockExperiments(async function() {
-  Assert.throws(
-    () => PreferenceExperiments.stopObserver("neveractive", "another.fake", "othervalue"),
-    "stopObserver threw because there was not matching active observer",
-  );
-}));
-
-// stopObserver should cancel an active observer.
-add_task(withMockExperiments(withMockPreferences(async function(mockExperiments, mockPreferences) {
-  const stop = sinon.stub(PreferenceExperiments, "stop");
-  mockPreferences.set("fake.preference", "startvalue");
-
-  PreferenceExperiments.startObserver("test", "fake.preference", "string", "experimentvalue");
-  PreferenceExperiments.stopObserver("test");
-
-  // Setting the preference now that the observer is stopped should not call
-  // stop.
-  Preferences.set("fake.preference", "newvalue");
-  ok(!stop.called, "stopObserver successfully removed the observer");
-
-  // Now that the observer is stopped, start should be able to start a new one
-  // without throwing.
-  try {
-    PreferenceExperiments.startObserver("test", "fake.preference", "string", "experimentvalue");
-  } catch (err) {
-    ok(false, "startObserver did not throw an error for an observer that was already stopped");
-  }
-
-  PreferenceExperiments.stopAllObservers();
-  stop.restore();
-})));
+);
 
 // stopAllObservers
-add_task(withMockExperiments(withMockPreferences(async function(mockExperiments, mockPreferences) {
-  const stop = sinon.stub(PreferenceExperiments, "stop");
-  mockPreferences.set("fake.preference", "startvalue");
-  mockPreferences.set("other.fake.preference", "startvalue");
-
-  PreferenceExperiments.startObserver("test", "fake.preference", "string", "experimentvalue");
-  PreferenceExperiments.startObserver("test2", "other.fake.preference", "string", "experimentvalue");
-  PreferenceExperiments.stopAllObservers();
+decorate_task(
+  withMockExperiments,
+  withMockPreferences,
+  async function(mockExperiments, mockPreferences) {
+    const stop = sinon.stub(PreferenceExperiments, "stop");
+    mockPreferences.set("fake.preference", "startvalue");
+    mockPreferences.set("other.fake.preference", "startvalue");
 
-  // Setting the preference now that the observers are stopped should not call
-  // stop.
-  Preferences.set("fake.preference", "newvalue");
-  Preferences.set("other.fake.preference", "newvalue");
-  ok(!stop.called, "stopAllObservers successfully removed all observers");
-
-  // Now that the observers are stopped, start should be able to start new
-  // observers without throwing.
-  try {
     PreferenceExperiments.startObserver("test", "fake.preference", "string", "experimentvalue");
     PreferenceExperiments.startObserver("test2", "other.fake.preference", "string", "experimentvalue");
-  } catch (err) {
-    ok(false, "startObserver did not throw an error for an observer that was already stopped");
-  }
+    PreferenceExperiments.stopAllObservers();
+
+    // Setting the preference now that the observers are stopped should not call
+    // stop.
+    mockPreferences.set("fake.preference", "newvalue");
+    mockPreferences.set("other.fake.preference", "newvalue");
+    ok(!stop.called, "stopAllObservers successfully removed all observers");
 
-  PreferenceExperiments.stopAllObservers();
-  stop.restore();
-})));
+    // Now that the observers are stopped, start should be able to start new
+    // observers without throwing.
+    try {
+      PreferenceExperiments.startObserver("test", "fake.preference", "string", "experimentvalue");
+      PreferenceExperiments.startObserver("test2", "other.fake.preference", "string", "experimentvalue");
+    } catch (err) {
+      ok(false, "startObserver did not throw an error for an observer that was already stopped");
+    }
+
+    PreferenceExperiments.stopAllObservers();
+    stop.restore();
+  }
+);
 
 // markLastSeen should throw if it can't find a matching experiment
-add_task(withMockExperiments(async function() {
-  await Assert.rejects(
-    PreferenceExperiments.markLastSeen("neveractive"),
-    "markLastSeen threw because there was not a matching experiment",
-  );
-}));
+decorate_task(
+  withMockExperiments,
+  async function() {
+    await Assert.rejects(
+      PreferenceExperiments.markLastSeen("neveractive"),
+      "markLastSeen threw because there was not a matching experiment",
+    );
+  }
+);
 
 // markLastSeen should update the lastSeen date
-add_task(withMockExperiments(async function(experiments) {
-  const oldDate = new Date(1988, 10, 1).toJSON();
-  experiments.test = experimentFactory({name: "test", lastSeen: oldDate});
-  await PreferenceExperiments.markLastSeen("test");
-  Assert.notEqual(
-    experiments.test.lastSeen,
-    oldDate,
-    "markLastSeen updated the experiment lastSeen date",
-  );
-}));
+decorate_task(
+  withMockExperiments,
+  async function(experiments) {
+    const oldDate = new Date(1988, 10, 1).toJSON();
+    experiments.test = experimentFactory({name: "test", lastSeen: oldDate});
+    await PreferenceExperiments.markLastSeen("test");
+    Assert.notEqual(
+      experiments.test.lastSeen,
+      oldDate,
+      "markLastSeen updated the experiment lastSeen date",
+    );
+  }
+);
 
 // stop should throw if an experiment with the given name doesn't exist
-add_task(withMockExperiments(async function() {
-  await Assert.rejects(
-    PreferenceExperiments.stop("test"),
-    "stop threw an error because there are no experiments with the given name",
-  );
-}));
+decorate_task(
+  withMockExperiments,
+  async function() {
+    await Assert.rejects(
+      PreferenceExperiments.stop("test"),
+      "stop threw an error because there are no experiments with the given name",
+    );
+  }
+);
 
 // stop should throw if the experiment is already expired
-add_task(withMockExperiments(async function(experiments) {
-  experiments.test = experimentFactory({name: "test", expired: true});
-  await Assert.rejects(
-    PreferenceExperiments.stop("test"),
-    "stop threw an error because the experiment was already expired",
-  );
-}));
+decorate_task(
+  withMockExperiments,
+  async function(experiments) {
+    experiments.test = experimentFactory({name: "test", expired: true});
+    await Assert.rejects(
+      PreferenceExperiments.stop("test"),
+      "stop threw an error because the experiment was already expired",
+    );
+  }
+);
 
 // stop should mark the experiment as expired, stop its observer, and revert the
 // preference value.
 decorate_task(
   withMockExperiments,
   withMockPreferences,
   withSpy(PreferenceExperiments, "stopObserver"),
   async function testStop(experiments, mockPreferences, stopObserverSpy) {
+    is(Preferences.get("fake.preference"), null, "preference should start unset");
     mockPreferences.set(`${startupPrefs}.fake.preference`, "experimentvalue", "user");
     mockPreferences.set("fake.preference", "experimentvalue", "default");
     experiments.test = experimentFactory({
       name: "test",
       expired: false,
       preferenceName: "fake.preference",
       preferenceValue: "experimentvalue",
       preferenceType: "string",
@@ -384,168 +435,197 @@ decorate_task(
       "stop cleared the startup preference for fake.preference.",
     );
 
     PreferenceExperiments.stopAllObservers();
   },
 );
 
 // stop should also support user pref experiments
-add_task(withMockExperiments(withMockPreferences(async function(experiments, mockPreferences) {
-  const stopObserver = sinon.stub(PreferenceExperiments, "stopObserver");
-  const hasObserver = sinon.stub(PreferenceExperiments, "hasObserver");
-  hasObserver.returns(true);
+decorate_task(
+  withMockExperiments,
+  withMockPreferences,
+  withStub(PreferenceExperiments, "stopObserver"),
+  withStub(PreferenceExperiments, "hasObserver"),
+  async function testStopUserPrefs(experiments, mockPreferences, stopObserver, hasObserver) {
+    hasObserver.returns(true);
 
-  mockPreferences.set("fake.preference", "experimentvalue", "user");
-  experiments.test = experimentFactory({
-    name: "test",
-    expired: false,
-    preferenceName: "fake.preference",
-    preferenceValue: "experimentvalue",
-    preferenceType: "string",
-    previousPreferenceValue: "oldvalue",
-    preferenceBranchType: "user",
-  });
-  PreferenceExperiments.startObserver("test", "fake.preference", "string", "experimentvalue");
+    mockPreferences.set("fake.preference", "experimentvalue", "user");
+    experiments.test = experimentFactory({
+      name: "test",
+      expired: false,
+      preferenceName: "fake.preference",
+      preferenceValue: "experimentvalue",
+      preferenceType: "string",
+      previousPreferenceValue: "oldvalue",
+      preferenceBranchType: "user",
+    });
+    PreferenceExperiments.startObserver("test", "fake.preference", "string", "experimentvalue");
 
-  await PreferenceExperiments.stop("test");
-  ok(stopObserver.calledWith("test"), "stop removed an observer");
-  is(experiments.test.expired, true, "stop marked the experiment as expired");
-  is(
-    Preferences.get("fake.preference"),
-    "oldvalue",
-    "stop reverted the preference to its previous value",
-  );
-  stopObserver.restore();
-  PreferenceExperiments.stopAllObservers();
-})));
+    await PreferenceExperiments.stop("test");
+    ok(stopObserver.calledWith("test"), "stop removed an observer");
+    is(experiments.test.expired, true, "stop marked the experiment as expired");
+    is(
+      Preferences.get("fake.preference"),
+      "oldvalue",
+      "stop reverted the preference to its previous value",
+    );
+    stopObserver.restore();
+    PreferenceExperiments.stopAllObservers();
+  }
+);
 
 // stop should remove a preference that had no value prior to an experiment for user prefs
-add_task(withMockExperiments(withMockPreferences(async function(experiments, mockPreferences) {
-  const stopObserver = sinon.stub(PreferenceExperiments, "stopObserver");
-  mockPreferences.set("fake.preference", "experimentvalue", "user");
-  experiments.test = experimentFactory({
-    name: "test",
-    expired: false,
-    preferenceName: "fake.preference",
-    preferenceValue: "experimentvalue",
-    preferenceType: "string",
-    previousPreferenceValue: null,
-    preferenceBranchType: "user",
-  });
+decorate_task(
+  withMockExperiments,
+  withMockPreferences,
+  async function(experiments, mockPreferences) {
+    const stopObserver = sinon.stub(PreferenceExperiments, "stopObserver");
+    mockPreferences.set("fake.preference", "experimentvalue", "user");
+    experiments.test = experimentFactory({
+      name: "test",
+      expired: false,
+      preferenceName: "fake.preference",
+      preferenceValue: "experimentvalue",
+      preferenceType: "string",
+      previousPreferenceValue: null,
+      preferenceBranchType: "user",
+    });
 
-  await PreferenceExperiments.stop("test");
-  ok(
-    !Preferences.isSet("fake.preference"),
-    "stop removed the preference that had no value prior to the experiment",
-  );
+    await PreferenceExperiments.stop("test");
+    ok(
+      !Preferences.isSet("fake.preference"),
+      "stop removed the preference that had no value prior to the experiment",
+    );
 
-  stopObserver.restore();
-})));
+    stopObserver.restore();
+  }
+);
 
 // stop should not modify a preference if resetValue is false
-add_task(withMockExperiments(withMockPreferences(async function(experiments, mockPreferences) {
-  const stopObserver = sinon.stub(PreferenceExperiments, "stopObserver");
-  mockPreferences.set("fake.preference", "customvalue", "default");
-  experiments.test = experimentFactory({
-    name: "test",
-    expired: false,
-    preferenceName: "fake.preference",
-    preferenceValue: "experimentvalue",
-    preferenceType: "string",
-    previousPreferenceValue: "oldvalue",
-    peferenceBranchType: "default",
-  });
+decorate_task(
+  withMockExperiments,
+  withMockPreferences,
+  withStub(PreferenceExperiments, "stopObserver"),
 
-  await PreferenceExperiments.stop("test", false);
-  is(
-    DefaultPreferences.get("fake.preference"),
-    "customvalue",
-    "stop did not modify the preference",
-  );
+  async function(experiments, mockPreferences, stopObserver) {
+    mockPreferences.set("fake.preference", "customvalue", "default");
+    experiments.test = experimentFactory({
+      name: "test",
+      expired: false,
+      preferenceName: "fake.preference",
+      preferenceValue: "experimentvalue",
+      preferenceType: "string",
+      previousPreferenceValue: "oldvalue",
+      peferenceBranchType: "default",
+    });
 
-  stopObserver.restore();
-})));
+    await PreferenceExperiments.stop("test", false);
+    is(
+      DefaultPreferences.get("fake.preference"),
+      "customvalue",
+      "stop did not modify the preference",
+    );
+  }
+);
 
 // get should throw if no experiment exists with the given name
-add_task(withMockExperiments(async function() {
-  await Assert.rejects(
-    PreferenceExperiments.get("neverexisted"),
-    "get rejects if no experiment with the given name is found",
-  );
-}));
+decorate_task(
+  withMockExperiments,
+  async function() {
+    await Assert.rejects(
+      PreferenceExperiments.get("neverexisted"),
+      "get rejects if no experiment with the given name is found",
+    );
+  }
+);
 
 // get
-add_task(withMockExperiments(async function(experiments) {
-  const experiment = experimentFactory({name: "test"});
-  experiments.test = experiment;
+decorate_task(
+  withMockExperiments,
+  async function(experiments) {
+    const experiment = experimentFactory({name: "test"});
+    experiments.test = experiment;
 
-  const fetchedExperiment = await PreferenceExperiments.get("test");
-  Assert.deepEqual(fetchedExperiment, experiment, "get fetches the correct experiment");
+    const fetchedExperiment = await PreferenceExperiments.get("test");
+    Assert.deepEqual(fetchedExperiment, experiment, "get fetches the correct experiment");
 
-  // Modifying the fetched experiment must not edit the data source.
-  fetchedExperiment.name = "othername";
-  is(experiments.test.name, "test", "get returns a copy of the experiment");
-}));
+    // Modifying the fetched experiment must not edit the data source.
+    fetchedExperiment.name = "othername";
+    is(experiments.test.name, "test", "get returns a copy of the experiment");
+  }
+);
 
-add_task(withMockExperiments(async function testGetAll(experiments) {
-  const experiment1 = experimentFactory({name: "experiment1"});
-  const experiment2 = experimentFactory({name: "experiment2", disabled: true});
-  experiments.experiment1 = experiment1;
-  experiments.experiment2 = experiment2;
+// get all
+decorate_task(
+  withMockExperiments,
+  async function testGetAll(experiments) {
+    const experiment1 = experimentFactory({name: "experiment1"});
+    const experiment2 = experimentFactory({name: "experiment2", disabled: true});
+    experiments.experiment1 = experiment1;
+    experiments.experiment2 = experiment2;
 
-  const fetchedExperiments = await PreferenceExperiments.getAll();
-  is(fetchedExperiments.length, 2, "getAll returns a list of all stored experiments");
-  Assert.deepEqual(
-    fetchedExperiments.find(e => e.name === "experiment1"),
-    experiment1,
-    "getAll returns a list with the correct experiments",
-  );
-  const fetchedExperiment2 = fetchedExperiments.find(e => e.name === "experiment2");
-  Assert.deepEqual(
-    fetchedExperiment2,
-    experiment2,
-    "getAll returns a list with the correct experiments, including disabled ones",
-  );
+    const fetchedExperiments = await PreferenceExperiments.getAll();
+    is(fetchedExperiments.length, 2, "getAll returns a list of all stored experiments");
+    Assert.deepEqual(
+      fetchedExperiments.find(e => e.name === "experiment1"),
+      experiment1,
+      "getAll returns a list with the correct experiments",
+    );
+    const fetchedExperiment2 = fetchedExperiments.find(e => e.name === "experiment2");
+    Assert.deepEqual(
+      fetchedExperiment2,
+      experiment2,
+      "getAll returns a list with the correct experiments, including disabled ones",
+    );
 
-  fetchedExperiment2.name = "othername";
-  is(experiment2.name, "experiment2", "getAll returns copies of the experiments");
-}));
+    fetchedExperiment2.name = "othername";
+    is(experiment2.name, "experiment2", "getAll returns copies of the experiments");
+  }
+);
 
-add_task(withMockExperiments(withMockPreferences(async function testGetAllActive(experiments) {
-  experiments.active = experimentFactory({
-    name: "active",
-    expired: false,
-  });
-  experiments.inactive = experimentFactory({
-    name: "inactive",
-    expired: true,
-  });
+// get all active
+decorate_task(
+  withMockExperiments,
+  withMockPreferences,
+  async function testGetAllActive(experiments) {
+    experiments.active = experimentFactory({
+      name: "active",
+      expired: false,
+    });
+    experiments.inactive = experimentFactory({
+      name: "inactive",
+      expired: true,
+    });
 
-  const activeExperiments = await PreferenceExperiments.getAllActive();
-  Assert.deepEqual(
-    activeExperiments,
-    [experiments.active],
-    "getAllActive only returns active experiments",
-  );
+    const activeExperiments = await PreferenceExperiments.getAllActive();
+    Assert.deepEqual(
+      activeExperiments,
+      [experiments.active],
+      "getAllActive only returns active experiments",
+    );
 
-  activeExperiments[0].name = "newfakename";
-  Assert.notEqual(
-    experiments.active.name,
-    "newfakename",
-    "getAllActive returns copies of stored experiments",
-  );
-})));
+    activeExperiments[0].name = "newfakename";
+    Assert.notEqual(
+      experiments.active.name,
+      "newfakename",
+      "getAllActive returns copies of stored experiments",
+    );
+  }
+);
 
 // has
-add_task(withMockExperiments(async function(experiments) {
-  experiments.test = experimentFactory({name: "test"});
-  ok(await PreferenceExperiments.has("test"), "has returned true for a stored experiment");
-  ok(!(await PreferenceExperiments.has("missing")), "has returned false for a missing experiment");
-}));
+decorate_task(
+  withMockExperiments,
+  async function(experiments) {
+    experiments.test = experimentFactory({name: "test"});
+    ok(await PreferenceExperiments.has("test"), "has returned true for a stored experiment");
+    ok(!(await PreferenceExperiments.has("missing")), "has returned false for a missing experiment");
+  }
+);
 
 // init should register telemetry experiments
 decorate_task(
   withMockExperiments,
   withMockPreferences,
   withStub(TelemetryEnvironment, "setExperimentActive"),
   withStub(PreferenceExperiments, "startObserver"),
   async function testInit(experiments, mockPreferences, setActiveStub, startObserverStub) {
@@ -634,67 +714,81 @@ decorate_task(
       experimentType: "pref-test",
     });
 
     Assert.deepEqual(
       setActiveStub.getCall(0).args,
       ["test", "branch", {type: "normandy-pref-test"}],
       "start() should register the experiment with the provided type",
     );
+
+    // start sets the passed preference in a way that is hard to mock.
+    // Reset the preference so it doesn't interfere with other tests.
+    Services.prefs.getDefaultBranch("fake.preference").deleteBranch("");
   },
 );
 
-
 // Experiments shouldn't be recorded by init() in telemetry if they are expired
 decorate_task(
   withMockExperiments,
   withStub(TelemetryEnvironment, "setExperimentActive"),
   async function testInitTelemetryExpired(experiments, setActiveStub) {
     experiments.experiment1 = experimentFactory({name: "expired", branch: "branch", expired: true});
     await PreferenceExperiments.init();
     ok(!setActiveStub.called, "Expired experiment is not registered by init");
   },
 );
 
 // Experiments should end if the preference has been changed when init() is called
-add_task(withMockExperiments(withMockPreferences(async function testInitChanges(experiments, mockPreferences) {
-  const stopStub = sinon.stub(PreferenceExperiments, "stop");
-  mockPreferences.set("fake.preference", "experiment value", "default");
-  experiments.test = experimentFactory({
-    name: "test",
-    preferenceName: "fake.preference",
-    preferenceValue: "experiment value",
-  });
-  mockPreferences.set("fake.preference", "changed value");
-  await PreferenceExperiments.init();
-  ok(stopStub.calledWith("test"), "Experiment is stopped because value changed");
-  ok(Preferences.get("fake.preference"), "changed value", "Preference value was not changed");
-  stopStub.restore();
-})));
-
+decorate_task(
+  withMockExperiments,
+  withMockPreferences,
+  withStub(PreferenceExperiments, "stop"),
+  async function testInitChanges(experiments, mockPreferences, stopStub) {
+    mockPreferences.set("fake.preference", "experiment value", "default");
+    experiments.test = experimentFactory({
+      name: "test",
+      preferenceName: "fake.preference",
+      preferenceValue: "experiment value",
+    });
+    mockPreferences.set("fake.preference", "changed value");
+    await PreferenceExperiments.init();
+    ok(stopStub.calledWith("test"), "Experiment is stopped because value changed");
+    is(Preferences.get("fake.preference"), "changed value", "Preference value was not changed");
+  }
+);
 
 // init should register an observer for experiments
-add_task(withMockExperiments(withMockPreferences(async function testInitRegistersObserver(experiments, mockPreferences) {
-  const startObserver = sinon.stub(PreferenceExperiments, "startObserver");
-  mockPreferences.set("fake.preference", "experiment value", "default");
-  experiments.test = experimentFactory({
-    name: "test",
-    preferenceName: "fake.preference",
-    preferenceValue: "experiment value",
-  });
-  await PreferenceExperiments.init();
+decorate_task(
+  withMockExperiments,
+  withMockPreferences,
+  withStub(PreferenceExperiments, "startObserver"),
+  withStub(PreferenceExperiments, "stop"),
+  withStub(CleanupManager, "addCleanupHandler"),
+  async function testInitRegistersObserver(experiments, mockPreferences, startObserver, stop) {
+    stop.throws("Stop should not be called");
+    mockPreferences.set("fake.preference", "experiment value", "default");
+    is(Preferences.get("fake.preference"), "experiment value", "pref shouldn't have a user value");
+    experiments.test = experimentFactory({
+      name: "test",
+      preferenceName: "fake.preference",
+      preferenceValue: "experiment value",
+    });
+    await PreferenceExperiments.init();
 
-  ok(
-    startObserver.calledWith("test", "fake.preference", "string", "experiment value"),
-    "init registered an observer",
-  );
+    ok(startObserver.calledOnce, "init should register an observer");
+    Assert.deepEqual(
+      startObserver.getCall(0).args,
+      ["test", "fake.preference", "string", "experiment value"],
+      "init should register an observer with the right args",
+    );
+  }
+);
 
-  startObserver.restore();
-})));
-
+// saveStartupPrefs
 decorate_task(
   withMockExperiments,
   async function testSaveStartupPrefs(experiments) {
     const experimentPrefs = {
       char: "string",
       int: 2,
       bool: true,
     };
@@ -726,16 +820,17 @@ decorate_task(
     );
     ok(
       !Services.prefs.prefHasUserValue(`${startupPrefs}.fake.old`),
       "saveStartupPrefs deleted old startup pref values.",
     );
   },
 );
 
+// saveStartupPrefs errors for invalid pref type
 decorate_task(
   withMockExperiments,
   async function testSaveStartupPrefsError(experiments) {
     experiments.test = experimentFactory({
       preferenceName: "fake.invalidValue",
       preferenceValue: new Date(),
     });
 
--- a/browser/extensions/shield-recipe-client/test/browser/browser_RecipeRunner.js
+++ b/browser/extensions/shield-recipe-client/test/browser/browser_RecipeRunner.js
@@ -61,44 +61,45 @@ add_task(async function checkFilter() {
 
   // The given recipe must be available to the filter context.
   const recipe = {filter_expression: "normandy.recipe.id == 7", id: 7};
   ok(await RecipeRunner.checkFilter(recipe), "The recipe is available in the filter context");
   recipe.id = 4;
   ok(!(await RecipeRunner.checkFilter(recipe)), "The recipe is available in the filter context");
 });
 
-add_task(withMockNormandyApi(async function testClientClassificationCache() {
-  const getStub = sinon.stub(ClientEnvironment, "getClientClassification")
-    .returns(Promise.resolve(false));
+decorate_task(
+  withMockNormandyApi,
+  withStub(ClientEnvironment, "getClientClassification"),
+  async function testClientClassificationCache(api, getStub) {
+    getStub.returns(Promise.resolve(false));
 
-  await SpecialPowers.pushPrefEnv({set: [
-    ["extensions.shield-recipe-client.api_url",
-      "https://example.com/selfsupport-dummy"],
-  ]});
+    await SpecialPowers.pushPrefEnv({set: [
+      ["extensions.shield-recipe-client.api_url",
+       "https://example.com/selfsupport-dummy"],
+    ]});
 
-  // When the experiment pref is false, eagerly call getClientClassification.
-  await SpecialPowers.pushPrefEnv({set: [
-    ["extensions.shield-recipe-client.experiments.lazy_classify", false],
-  ]});
-  ok(!getStub.called, "getClientClassification hasn't been called");
-  await RecipeRunner.run();
-  ok(getStub.called, "getClientClassification was called eagerly");
+    // When the experiment pref is false, eagerly call getClientClassification.
+    await SpecialPowers.pushPrefEnv({set: [
+      ["extensions.shield-recipe-client.experiments.lazy_classify", false],
+    ]});
+    ok(!getStub.called, "getClientClassification hasn't been called");
+    await RecipeRunner.run();
+    ok(getStub.called, "getClientClassification was called eagerly");
 
-  // When the experiment pref is true, do not eagerly call getClientClassification.
-  await SpecialPowers.pushPrefEnv({set: [
-    ["extensions.shield-recipe-client.experiments.lazy_classify", true],
-  ]});
-  getStub.reset();
-  ok(!getStub.called, "getClientClassification hasn't been called");
-  await RecipeRunner.run();
-  ok(!getStub.called, "getClientClassification was not called eagerly");
-
-  getStub.restore();
-}));
+    // When the experiment pref is true, do not eagerly call getClientClassification.
+    await SpecialPowers.pushPrefEnv({set: [
+      ["extensions.shield-recipe-client.experiments.lazy_classify", true],
+    ]});
+    getStub.reset();
+    ok(!getStub.called, "getClientClassification hasn't been called");
+    await RecipeRunner.run();
+    ok(!getStub.called, "getClientClassification was not called eagerly");
+  }
+);
 
 /**
  * Mocks RecipeRunner.loadActionSandboxManagers for testing run.
  */
 async function withMockActionSandboxManagers(actions, testFunction) {
   const managers = {};
   for (const action of actions) {
     const manager = new ActionSandboxManager("");
@@ -113,221 +114,233 @@ async function withMockActionSandboxMana
   loadActionSandboxManagers.restore();
 
   for (const manager of Object.values(managers)) {
     manager.removeHold("testing");
     await manager.isNuked();
   }
 }
 
-add_task(withMockNormandyApi(async function testRun(mockApi) {
-  const closeSpy = sinon.spy(AddonStudies, "close");
-  const reportRunner = sinon.stub(Uptake, "reportRunner");
-  const reportAction = sinon.stub(Uptake, "reportAction");
-  const reportRecipe = sinon.stub(Uptake, "reportRecipe");
+decorate_task(
+  withMockNormandyApi,
+  withSpy(AddonStudies, "close"),
+  withStub(Uptake, "reportRunner"),
+  withStub(Uptake, "reportAction"),
+  withStub(Uptake, "reportRecipe"),
+  async function testRun(mockApi, closeSpy, reportRunner, reportAction, reportRecipe) {
+    const matchAction = {name: "matchAction"};
+    const noMatchAction = {name: "noMatchAction"};
+    mockApi.actions = [matchAction, noMatchAction];
 
-  const matchAction = {name: "matchAction"};
-  const noMatchAction = {name: "noMatchAction"};
-  mockApi.actions = [matchAction, noMatchAction];
+    const matchRecipe = {id: "match", action: "matchAction", filter_expression: "true"};
+    const noMatchRecipe = {id: "noMatch", action: "noMatchAction", filter_expression: "false"};
+    const missingRecipe = {id: "missing", action: "missingAction", filter_expression: "true"};
+    mockApi.recipes = [matchRecipe, noMatchRecipe, missingRecipe];
 
-  const matchRecipe = {id: "match", action: "matchAction", filter_expression: "true"};
-  const noMatchRecipe = {id: "noMatch", action: "noMatchAction", filter_expression: "false"};
-  const missingRecipe = {id: "missing", action: "missingAction", filter_expression: "true"};
-  mockApi.recipes = [matchRecipe, noMatchRecipe, missingRecipe];
+    await withMockActionSandboxManagers(mockApi.actions, async managers => {
+      const matchManager = managers.matchAction;
+      const noMatchManager = managers.noMatchAction;
+
+      await RecipeRunner.run();
+
+      // match should be called for preExecution, action, and postExecution
+      sinon.assert.calledWith(matchManager.runAsyncCallback, "preExecution");
+      sinon.assert.calledWith(matchManager.runAsyncCallback, "action", matchRecipe);
+      sinon.assert.calledWith(matchManager.runAsyncCallback, "postExecution");
 
-  await withMockActionSandboxManagers(mockApi.actions, async managers => {
-    const matchManager = managers.matchAction;
-    const noMatchManager = managers.noMatchAction;
+      // noMatch should be called for preExecution and postExecution, and skipped
+      // for action since the filter expression does not match.
+      sinon.assert.calledWith(noMatchManager.runAsyncCallback, "preExecution");
+      sinon.assert.neverCalledWith(noMatchManager.runAsyncCallback, "action", noMatchRecipe);
+      sinon.assert.calledWith(noMatchManager.runAsyncCallback, "postExecution");
 
-    await RecipeRunner.run();
+      // missing is never called at all due to no matching action/manager.
 
-    // match should be called for preExecution, action, and postExecution
-    sinon.assert.calledWith(matchManager.runAsyncCallback, "preExecution");
-    sinon.assert.calledWith(matchManager.runAsyncCallback, "action", matchRecipe);
-    sinon.assert.calledWith(matchManager.runAsyncCallback, "postExecution");
+      // Test uptake reporting
+      sinon.assert.calledWith(reportRunner, Uptake.RUNNER_SUCCESS);
+      sinon.assert.calledWith(reportAction, "matchAction", Uptake.ACTION_SUCCESS);
+      sinon.assert.calledWith(reportAction, "noMatchAction", Uptake.ACTION_SUCCESS);
+      sinon.assert.calledWith(reportRecipe, "match", Uptake.RECIPE_SUCCESS);
+      sinon.assert.neverCalledWith(reportRecipe, "noMatch", Uptake.RECIPE_SUCCESS);
+      sinon.assert.calledWith(reportRecipe, "missing", Uptake.RECIPE_INVALID_ACTION);
+    });
 
-    // noMatch should be called for preExecution and postExecution, and skipped
-    // for action since the filter expression does not match.
-    sinon.assert.calledWith(noMatchManager.runAsyncCallback, "preExecution");
-    sinon.assert.neverCalledWith(noMatchManager.runAsyncCallback, "action", noMatchRecipe);
-    sinon.assert.calledWith(noMatchManager.runAsyncCallback, "postExecution");
+    // Ensure storage is closed after the run.
+    sinon.assert.calledOnce(closeSpy);
+  }
+);
 
-    // missing is never called at all due to no matching action/manager.
+decorate_task(
+  withMockNormandyApi,
+  async function testRunRecipeError(mockApi) {
+    const reportRecipe = sinon.stub(Uptake, "reportRecipe");
 
-    // Test uptake reporting
-    sinon.assert.calledWith(reportRunner, Uptake.RUNNER_SUCCESS);
-    sinon.assert.calledWith(reportAction, "matchAction", Uptake.ACTION_SUCCESS);
-    sinon.assert.calledWith(reportAction, "noMatchAction", Uptake.ACTION_SUCCESS);
-    sinon.assert.calledWith(reportRecipe, "match", Uptake.RECIPE_SUCCESS);
-    sinon.assert.neverCalledWith(reportRecipe, "noMatch", Uptake.RECIPE_SUCCESS);
-    sinon.assert.calledWith(reportRecipe, "missing", Uptake.RECIPE_INVALID_ACTION);
-  });
+    const action = {name: "action"};
+    mockApi.actions = [action];
+
+    const recipe = {id: "recipe", action: "action", filter_expression: "true"};
+    mockApi.recipes = [recipe];
 
-  // Ensure storage is closed after the run.
-  sinon.assert.calledOnce(closeSpy);
+    await withMockActionSandboxManagers(mockApi.actions, async managers => {
+      const manager = managers.action;
+      manager.runAsyncCallback.callsFake(async callbackName => {
+        if (callbackName === "action") {
+          throw new Error("Action execution failure");
+        }
+      });
+
+      await RecipeRunner.run();
+
+      // Uptake should report that the recipe threw an exception
+      sinon.assert.calledWith(reportRecipe, "recipe", Uptake.RECIPE_EXECUTION_ERROR);
+    });
+
+    reportRecipe.restore();
+  }
+);
 
-  closeSpy.restore();
-  reportRunner.restore();
-  reportAction.restore();
-  reportRecipe.restore();
-}));
+decorate_task(
+  withMockNormandyApi,
+  async function testRunFetchFail(mockApi) {
+    const closeSpy = sinon.spy(AddonStudies, "close");
+    const reportRunner = sinon.stub(Uptake, "reportRunner");
 
-add_task(withMockNormandyApi(async function testRunRecipeError(mockApi) {
-  const reportRecipe = sinon.stub(Uptake, "reportRecipe");
+    const action = {name: "action"};
+    mockApi.actions = [action];
+    mockApi.fetchRecipes.rejects(new Error("Signature not valid"));
+
+    await withMockActionSandboxManagers(mockApi.actions, async managers => {
+      const manager = managers.action;
+      await RecipeRunner.run();
 
-  const action = {name: "action"};
-  mockApi.actions = [action];
-
-  const recipe = {id: "recipe", action: "action", filter_expression: "true"};
-  mockApi.recipes = [recipe];
+      // If the recipe fetch failed, do not run anything.
+      sinon.assert.notCalled(manager.runAsyncCallback);
+      sinon.assert.calledWith(reportRunner, Uptake.RUNNER_SERVER_ERROR);
 
-  await withMockActionSandboxManagers(mockApi.actions, async managers => {
-    const manager = managers.action;
-    manager.runAsyncCallback.callsFake(async callbackName => {
-      if (callbackName === "action") {
-        throw new Error("Action execution failure");
-      }
+      // Test that network errors report a specific uptake error
+      reportRunner.reset();
+      mockApi.fetchRecipes.rejects(new Error("NetworkError: The system was down"));
+      await RecipeRunner.run();
+      sinon.assert.calledWith(reportRunner, Uptake.RUNNER_NETWORK_ERROR);
+
+      // Test that signature issues report a specific uptake error
+      reportRunner.reset();
+      mockApi.fetchRecipes.rejects(new NormandyApi.InvalidSignatureError("Signature fail"));
+      await RecipeRunner.run();
+      sinon.assert.calledWith(reportRunner, Uptake.RUNNER_INVALID_SIGNATURE);
     });
 
-    await RecipeRunner.run();
-
-    // Uptake should report that the recipe threw an exception
-    sinon.assert.calledWith(reportRecipe, "recipe", Uptake.RECIPE_EXECUTION_ERROR);
-  });
-
-  reportRecipe.restore();
-}));
+    // If the recipe fetch failed, we don't need to call close since nothing
+    // opened a connection in the first place.
+    sinon.assert.notCalled(closeSpy);
 
-add_task(withMockNormandyApi(async function testRunFetchFail(mockApi) {
-  const closeSpy = sinon.spy(AddonStudies, "close");
-  const reportRunner = sinon.stub(Uptake, "reportRunner");
-
-  const action = {name: "action"};
-  mockApi.actions = [action];
-  mockApi.fetchRecipes.rejects(new Error("Signature not valid"));
-
-  await withMockActionSandboxManagers(mockApi.actions, async managers => {
-    const manager = managers.action;
-    await RecipeRunner.run();
-
-    // If the recipe fetch failed, do not run anything.
-    sinon.assert.notCalled(manager.runAsyncCallback);
-    sinon.assert.calledWith(reportRunner, Uptake.RUNNER_SERVER_ERROR);
+    closeSpy.restore();
+    reportRunner.restore();
+  }
+);
 
-    // Test that network errors report a specific uptake error
-    reportRunner.reset();
-    mockApi.fetchRecipes.rejects(new Error("NetworkError: The system was down"));
-    await RecipeRunner.run();
-    sinon.assert.calledWith(reportRunner, Uptake.RUNNER_NETWORK_ERROR);
-
-    // Test that signature issues report a specific uptake error
-    reportRunner.reset();
-    mockApi.fetchRecipes.rejects(new NormandyApi.InvalidSignatureError("Signature fail"));
-    await RecipeRunner.run();
-    sinon.assert.calledWith(reportRunner, Uptake.RUNNER_INVALID_SIGNATURE);
-  });
+decorate_task(
+  withMockNormandyApi,
+  async function testRunPreExecutionFailure(mockApi) {
+    const closeSpy = sinon.spy(AddonStudies, "close");
+    const reportAction = sinon.stub(Uptake, "reportAction");
+    const reportRecipe = sinon.stub(Uptake, "reportRecipe");
 
-  // If the recipe fetch failed, we don't need to call close since nothing
-  // opened a connection in the first place.
-  sinon.assert.notCalled(closeSpy);
-
-  closeSpy.restore();
-  reportRunner.restore();
-}));
-
-add_task(withMockNormandyApi(async function testRunPreExecutionFailure(mockApi) {
-  const closeSpy = sinon.spy(AddonStudies, "close");
-  const reportAction = sinon.stub(Uptake, "reportAction");
-  const reportRecipe = sinon.stub(Uptake, "reportRecipe");
+    const passAction = {name: "passAction"};
+    const failAction = {name: "failAction"};
+    mockApi.actions = [passAction, failAction];
 
-  const passAction = {name: "passAction"};
-  const failAction = {name: "failAction"};
-  mockApi.actions = [passAction, failAction];
-
-  const passRecipe = {id: "pass", action: "passAction", filter_expression: "true"};
-  const failRecipe = {id: "fail", action: "failAction", filter_expression: "true"};
-  mockApi.recipes = [passRecipe, failRecipe];
+    const passRecipe = {id: "pass", action: "passAction", filter_expression: "true"};
+    const failRecipe = {id: "fail", action: "failAction", filter_expression: "true"};
+    mockApi.recipes = [passRecipe, failRecipe];
 
-  await withMockActionSandboxManagers(mockApi.actions, async managers => {
-    const passManager = managers.passAction;
-    const failManager = managers.failAction;
-    failManager.runAsyncCallback.returns(Promise.reject(new Error("oh no")));
-
-    await RecipeRunner.run();
+    await withMockActionSandboxManagers(mockApi.actions, async managers => {
+      const passManager = managers.passAction;
+      const failManager = managers.failAction;
+      failManager.runAsyncCallback.returns(Promise.reject(new Error("oh no")));
 
-    // pass should be called for preExecution, action, and postExecution
-    sinon.assert.calledWith(passManager.runAsyncCallback, "preExecution");
-    sinon.assert.calledWith(passManager.runAsyncCallback, "action", passRecipe);
-    sinon.assert.calledWith(passManager.runAsyncCallback, "postExecution");
-
-    // fail should only be called for preExecution, since it fails during that
-    sinon.assert.calledWith(failManager.runAsyncCallback, "preExecution");
-    sinon.assert.neverCalledWith(failManager.runAsyncCallback, "action", failRecipe);
-    sinon.assert.neverCalledWith(failManager.runAsyncCallback, "postExecution");
+      await RecipeRunner.run();
 
-    sinon.assert.calledWith(reportAction, "passAction", Uptake.ACTION_SUCCESS);
-    sinon.assert.calledWith(reportAction, "failAction", Uptake.ACTION_PRE_EXECUTION_ERROR);
-    sinon.assert.calledWith(reportRecipe, "fail", Uptake.RECIPE_ACTION_DISABLED);
-  });
-
-  // Ensure storage is closed after the run, despite the failures.
-  sinon.assert.calledOnce(closeSpy);
-  closeSpy.restore();
-  reportAction.restore();
-  reportRecipe.restore();
-}));
+      // pass should be called for preExecution, action, and postExecution
+      sinon.assert.calledWith(passManager.runAsyncCallback, "preExecution");
+      sinon.assert.calledWith(passManager.runAsyncCallback, "action", passRecipe);
+      sinon.assert.calledWith(passManager.runAsyncCallback, "postExecution");
 
-add_task(withMockNormandyApi(async function testRunPostExecutionFailure(mockApi) {
-  const reportAction = sinon.stub(Uptake, "reportAction");
-
-  const failAction = {name: "failAction"};
-  mockApi.actions = [failAction];
+      // fail should only be called for preExecution, since it fails during that
+      sinon.assert.calledWith(failManager.runAsyncCallback, "preExecution");
+      sinon.assert.neverCalledWith(failManager.runAsyncCallback, "action", failRecipe);
+      sinon.assert.neverCalledWith(failManager.runAsyncCallback, "postExecution");
 
-  const failRecipe = {action: "failAction", filter_expression: "true"};
-  mockApi.recipes = [failRecipe];
-
-  await withMockActionSandboxManagers(mockApi.actions, async managers => {
-    const failManager = managers.failAction;
-    failManager.runAsyncCallback.callsFake(async callbackName => {
-      if (callbackName === "postExecution") {
-        throw new Error("postExecution failure");
-      }
+      sinon.assert.calledWith(reportAction, "passAction", Uptake.ACTION_SUCCESS);
+      sinon.assert.calledWith(reportAction, "failAction", Uptake.ACTION_PRE_EXECUTION_ERROR);
+      sinon.assert.calledWith(reportRecipe, "fail", Uptake.RECIPE_ACTION_DISABLED);
     });
 
-    await RecipeRunner.run();
+    // Ensure storage is closed after the run, despite the failures.
+    sinon.assert.calledOnce(closeSpy);
+    closeSpy.restore();
+    reportAction.restore();
+    reportRecipe.restore();
+  }
+);
 
-    // fail should be called for every stage
-    sinon.assert.calledWith(failManager.runAsyncCallback, "preExecution");
-    sinon.assert.calledWith(failManager.runAsyncCallback, "action", failRecipe);
-    sinon.assert.calledWith(failManager.runAsyncCallback, "postExecution");
+decorate_task(
+  withMockNormandyApi,
+  async function testRunPostExecutionFailure(mockApi) {
+    const reportAction = sinon.stub(Uptake, "reportAction");
 
-    // Uptake should report a post-execution error
-    sinon.assert.calledWith(reportAction, "failAction", Uptake.ACTION_POST_EXECUTION_ERROR);
-  });
+    const failAction = {name: "failAction"};
+    mockApi.actions = [failAction];
+
+    const failRecipe = {action: "failAction", filter_expression: "true"};
+    mockApi.recipes = [failRecipe];
 
-  reportAction.restore();
-}));
+    await withMockActionSandboxManagers(mockApi.actions, async managers => {
+      const failManager = managers.failAction;
+      failManager.runAsyncCallback.callsFake(async callbackName => {
+        if (callbackName === "postExecution") {
+          throw new Error("postExecution failure");
+        }
+      });
+
+      await RecipeRunner.run();
 
-add_task(withMockNormandyApi(async function testLoadActionSandboxManagers(mockApi) {
-  mockApi.actions = [
-    {name: "normalAction"},
-    {name: "missingImpl"},
-  ];
-  mockApi.implementations.normalAction = "window.scriptRan = true";
+      // fail should be called for every stage
+      sinon.assert.calledWith(failManager.runAsyncCallback, "preExecution");
+      sinon.assert.calledWith(failManager.runAsyncCallback, "action", failRecipe);
+      sinon.assert.calledWith(failManager.runAsyncCallback, "postExecution");
+
+      // Uptake should report a post-execution error
+      sinon.assert.calledWith(reportAction, "failAction", Uptake.ACTION_POST_EXECUTION_ERROR);
+    });
+
+    reportAction.restore();
+  }
+);
 
-  const managers = await RecipeRunner.loadActionSandboxManagers();
-  ok("normalAction" in managers, "Actions with implementations have managers");
-  ok(!("missingImpl" in managers), "Actions without implementations are skipped");
+decorate_task(
+  withMockNormandyApi,
+  async function testLoadActionSandboxManagers(mockApi) {
+    mockApi.actions = [
+      {name: "normalAction"},
+      {name: "missingImpl"},
+    ];
+    mockApi.implementations.normalAction = "window.scriptRan = true";
 
-  const normalManager = managers.normalAction;
-  ok(
-    await normalManager.evalInSandbox("window.scriptRan"),
-    "Implementations are run in the sandbox",
-  );
-}));
+    const managers = await RecipeRunner.loadActionSandboxManagers();
+    ok("normalAction" in managers, "Actions with implementations have managers");
+    ok(!("missingImpl" in managers), "Actions without implementations are skipped");
+
+    const normalManager = managers.normalAction;
+    ok(
+      await normalManager.evalInSandbox("window.scriptRan"),
+      "Implementations are run in the sandbox",
+    );
+  }
+);
 
 // test init() in dev mode
 decorate_task(
   withPrefEnv({
     set: [
       ["extensions.shield-recipe-client.dev_mode", true],
       ["extensions.shield-recipe-client.first_run", false],
     ],
@@ -397,18 +410,19 @@ decorate_task(
       ["extensions.shield-recipe-client.enabled", true],
       ["extensions.shield-recipe-client.api_url", "https://example.com"], // starts with "https://"
     ],
   }),
   withStub(RecipeRunner, "run"),
   withStub(RecipeRunner, "enable"),
   withStub(RecipeRunner, "disable"),
   withStub(CleanupManager, "addCleanupHandler"),
+  withStub(AddonStudies, "stop"),
 
-  async function testPrefWatching(runStub, enableStub, disableStub, addCleanupHandlerStub) {
+  async function testPrefWatching(runStub, enableStub, disableStub, addCleanupHandlerStub, stopStub) {
     await RecipeRunner.init();
     is(enableStub.callCount, 1, "Enable should be called initially");
     is(disableStub.callCount, 0, "Disable should not be called initially");
 
     await SpecialPowers.pushPrefEnv({ set: [["extensions.shield-recipe-client.enabled", false]] });
     is(enableStub.callCount, 1, "Enable should not be called again");
     is(disableStub.callCount, 1, "RecipeRunner should disable when Shield is disabled");
 
--- a/browser/extensions/shield-recipe-client/test/browser/browser_ShieldPreferences.js
+++ b/browser/extensions/shield-recipe-client/test/browser/browser_ShieldPreferences.js
@@ -1,24 +1,23 @@
 "use strict";
 
 Cu.import("resource://gre/modules/Services.jsm", this);
 Cu.import("resource://shield-recipe-client/lib/AddonStudies.jsm", this);
 
 const OPT_OUT_PREF = "app.shield.optoutstudies.enabled";
 
 decorate_task(
-  withPrefEnv({
-    set: [[OPT_OUT_PREF, true]],
-  }),
+  withMockPreferences,
   AddonStudies.withStudies([
     studyFactory({active: true}),
     studyFactory({active: true}),
   ]),
-  async function testDisableStudiesWhenOptOutDisabled([study1, study2]) {
+  async function testDisableStudiesWhenOptOutDisabled(mockPreferences, [study1, study2]) {
+    mockPreferences.set(OPT_OUT_PREF, true);
     const observers = [
       studyEndObserved(study1.recipeId),
       studyEndObserved(study2.recipeId),
     ];
     Services.prefs.setBoolPref(OPT_OUT_PREF, false);
     await Promise.all(observers);
 
     const newStudy1 = await AddonStudies.get(study1.recipeId);
--- a/browser/extensions/shield-recipe-client/test/browser/browser_bootstrap.js
+++ b/browser/extensions/shield-recipe-client/test/browser/browser_bootstrap.js
@@ -26,22 +26,20 @@ const initPref2 = "test.initShieldPrefs2
 const initPref3 = "test.initShieldPrefs3";
 
 const experimentPref1 = "test.initExperimentPrefs1";
 const experimentPref2 = "test.initExperimentPrefs2";
 const experimentPref3 = "test.initExperimentPrefs3";
 const experimentPref4 = "test.initExperimentPrefs4";
 
 decorate_task(
-  withPrefEnv({
-    clear: [[initPref1], [initPref2], [initPref3]],
-  }),
   withBootstrap,
   async function testInitShieldPrefs(Bootstrap) {
     const defaultBranch = Services.prefs.getDefaultBranch("");
+
     const prefDefaults = {
       [initPref1]: true,
       [initPref2]: 2,
       [initPref3]: "string",
     };
 
     for (const pref of Object.keys(prefDefaults)) {
       is(
@@ -69,16 +67,18 @@ decorate_task(
     );
 
     for (const pref of Object.keys(prefDefaults)) {
       ok(
         !defaultBranch.prefHasUserValue(pref),
         `Pref ${pref} doesn't have a user value after being initialized.`,
       );
     }
+
+    defaultBranch.deleteBranch("test.");
   },
 );
 
 decorate_task(
   withBootstrap,
   async function testInitShieldPrefsError(Bootstrap) {
     Assert.throws(
       () => Bootstrap.initShieldPrefs({"test.prefTypeError": new Date()}),
--- a/browser/extensions/shield-recipe-client/test/browser/head.js
+++ b/browser/extensions/shield-recipe-client/test/browser/head.js
@@ -179,21 +179,37 @@ class MockPreferences {
       };
     }
   }
 
   cleanup() {
     for (const [branchName, values] of Object.entries(this.oldValues)) {
       const preferenceBranch = preferenceBranches[branchName];
       for (const [name, {oldValue, existed}] of Object.entries(values)) {
+        const before = preferenceBranch.get(name);
+
+        if (before === oldValue) {
+          continue;
+        }
+
         if (existed) {
           preferenceBranch.set(name, oldValue);
+        } else if (branchName === "default") {
+          Services.prefs.getDefaultBranch(name).deleteBranch("");
         } else {
           preferenceBranch.reset(name);
         }
+
+        const after = preferenceBranch.get(name);
+        if (before === after && before !== undefined) {
+          throw new Error(
+            `Couldn't reset pref "${name}" to "${oldValue}" on "${branchName}" branch ` +
+            `(value stayed "${before}", did ${existed ? "" : "not "}exist)`
+          );
+        }
       }
     }
   }
 }
 
 this.withPrefEnv = function(inPrefs) {
   return function wrapper(testFunc) {
     return async function inner(...args) {
--- a/browser/tools/mozscreenshots/mozscreenshots/extension/TestRunner.jsm
+++ b/browser/tools/mozscreenshots/mozscreenshots/extension/TestRunner.jsm
@@ -6,16 +6,17 @@
 
 this.EXPORTED_SYMBOLS = ["TestRunner"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
 const APPLY_CONFIG_TIMEOUT_MS = 60 * 1000;
 const HOME_PAGE = "chrome://mozscreenshots/content/lib/mozscreenshots.html";
 
+Cu.import("resource://gre/modules/AppConstants.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://gre/modules/Geometry.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "BrowserTestUtils",
@@ -29,28 +30,51 @@ this.TestRunner = {
   currentComboIndex: 0,
   _lastCombo: null,
   _libDir: null,
   croppingPadding: 10,
   mochitestScope: null,
 
   init(extensionPath) {
     this._extensionPath = extensionPath;
+    this.setupOS();
   },
 
   /**
    * Initialize the mochitest interface. This allows TestRunner to integrate
    * with mochitest functions like is(...) and ok(...). This must be called
    * prior to invoking any of the TestRunner functions. Note that this should
    * be properly setup in head.js, so you probably don't need to call it.
    */
   initTest(mochitestScope) {
     this.mochitestScope = mochitestScope;
   },
 
+  setupOS() {
+    switch (AppConstants.platform) {
+      case "macosx": {
+        this.disableNotificationCenter();
+        break;
+      }
+    }
+  },
+
+  disableNotificationCenter() {
+    let killall = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+    killall.initWithPath("/bin/bash");
+
+    let killallP = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
+    killallP.init(killall);
+    let ncPlist = "/System/Library/LaunchAgents/com.apple.notificationcenterui.plist";
+    let killallArgs = ["-c",
+                       `/bin/launchctl unload -w ${ncPlist} && ` +
+                       "/usr/bin/killall -v NotificationCenter"];
+    killallP.run(true, killallArgs, killallArgs.length);
+  },
+
   /**
    * Load specified sets, execute all combinations of them, and capture screenshots.
    */
   async start(setNames, jobName = null) {
     let subDirs = ["mozscreenshots",
                    (new Date()).toISOString().replace(/:/g, "-") + "_" + Services.appinfo.OS];
     let screenshotPath = FileUtils.getFile("TmpD", subDirs).path;
 
--- a/devtools/client/aboutdebugging/test/head.js
+++ b/devtools/client/aboutdebugging/test/head.js
@@ -396,29 +396,32 @@ function* waitForServiceWorkerActivation
   yield waitUntil(() => {
     return targetStatus.textContent !== "Registering";
   }, 100);
 }
 
 /**
  * Set all preferences needed to enable service worker debugging and testing.
  */
-function enableServiceWorkerDebugging() {
-  return new Promise(done => {
-    let options = { "set": [
-      // Enable service workers.
-      ["dom.serviceWorkers.enabled", true],
-      // Accept workers from mochitest's http.
-      ["dom.serviceWorkers.testing.enabled", true],
-      // Force single content process.
-      ["dom.ipc.processCount", 1],
-    ]};
+function* enableServiceWorkerDebugging() {
+  let options = { "set": [
+    // Enable service workers.
+    ["dom.serviceWorkers.enabled", true],
+    // Accept workers from mochitest's http.
+    ["dom.serviceWorkers.testing.enabled", true],
+    // Force single content process.
+    ["dom.ipc.processCount", 1],
+  ]};
+
+  // Wait for dom.ipc.processCount to be updated before releasing processes.
+  yield new Promise(done => {
     SpecialPowers.pushPrefEnv(options, done);
-    Services.ppmm.releaseCachedProcesses();
   });
+
+  Services.ppmm.releaseCachedProcesses();
 }
 
 /**
  * Returns a promise that resolves when the given add-on event is fired. The
  * resolved value is an array of arguments passed for the event.
  */
 function promiseAddonEvent(event) {
   return new Promise(resolve => {
--- a/devtools/client/framework/test/shared-head.js
+++ b/devtools/client/framework/test/shared-head.js
@@ -688,8 +688,52 @@ function createTestHTTPServer() {
       destroyed.resolve();
     });
     yield destroyed.promise;
   });
 
   server.start(-1);
   return server;
 }
+
+/**
+ * Inject `EventUtils` helpers into ContentTask scope.
+ *
+ * This helper is automatically exposed to mochitest browser tests,
+ * but is missing from content task scope.
+ * You should call this method only once per <browser> tag
+ *
+ * @param {xul:browser} browser
+ *        Reference to the browser in which we load content task
+ */
+async function injectEventUtilsInContentTask(browser) {
+  await ContentTask.spawn(browser, {}, function* () {
+    if ("EventUtils" in this) {
+      return;
+    }
+
+    let EventUtils = this.EventUtils = {};
+
+    EventUtils.window = {};
+    EventUtils.parent = EventUtils.window;
+    /* eslint-disable camelcase */
+    EventUtils._EU_Ci = Components.interfaces;
+    EventUtils._EU_Cc = Components.classes;
+    /* eslint-enable camelcase */
+    // EventUtils' `sendChar` function relies on the navigator to synthetize events.
+    EventUtils.navigator = content.navigator;
+    EventUtils.KeyboardEvent = content.KeyboardEvent;
+
+    EventUtils.synthesizeClick = element => new Promise(resolve => {
+      element.addEventListener("click", function () {
+        resolve();
+      }, {once: true});
+
+      EventUtils.synthesizeMouseAtCenter(element,
+        { type: "mousedown", isSynthesized: false }, content);
+      EventUtils.synthesizeMouseAtCenter(element,
+        { type: "mouseup", isSynthesized: false }, content);
+    });
+
+    Services.scriptloader.loadSubScript(
+      "chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
+  });
+}
--- a/devtools/client/netmonitor/src/components/App.js
+++ b/devtools/client/netmonitor/src/components/App.js
@@ -5,18 +5,22 @@
 "use strict";
 
 const { createFactory } = require("devtools/client/shared/vendor/react");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
 // Components
-const MonitorPanel = createFactory(require("./MonitorPanel"));
-const StatisticsPanel = createFactory(require("./StatisticsPanel"));
+loader.lazyGetter(this, "MonitorPanel", function () {
+  return createFactory(require("./MonitorPanel"));
+});
+loader.lazyGetter(this, "StatisticsPanel", function () {
+  return createFactory(require("./StatisticsPanel"));
+});
 
 const { div } = dom;
 
 /**
  * App component
  * The top level component for representing main panel
  */
 function App({
--- a/devtools/client/netmonitor/src/components/HeadersPanel.js
+++ b/devtools/client/netmonitor/src/components/HeadersPanel.js
@@ -18,21 +18,29 @@ const {
 } = require("../utils/mdn-utils");
 const {
   fetchNetworkUpdatePacket,
   writeHeaderText,
 } = require("../utils/request-utils");
 const { sortObjectKeys } = require("../utils/sort-utils");
 
 // Components
-const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
-const MDNLink = createFactory(require("./MdnLink"));
 const PropertiesView = createFactory(require("./PropertiesView"));
 
-const { Rep } = REPS;
+loader.lazyGetter(this, "MDNLink", function () {
+  return createFactory(require("./MdnLink"));
+});
+
+loader.lazyGetter(this, "Rep", function () {
+  return require("devtools/client/shared/components/reps/reps").REPS.Rep;
+});
+loader.lazyGetter(this, "MODE", function () {
+  return require("devtools/client/shared/components/reps/reps").MODE;
+});
+
 const { button, div, input, textarea, span } = dom;
 
 const EDIT_AND_RESEND = L10N.getStr("netmonitor.summary.editAndResend");
 const RAW_HEADERS = L10N.getStr("netmonitor.summary.rawHeaders");
 const RAW_HEADERS_REQUEST = L10N.getStr("netmonitor.summary.rawHeaders.requestHeaders");
 const RAW_HEADERS_RESPONSE = L10N.getStr("netmonitor.summary.rawHeaders.responseHeaders");
 const HEADERS_EMPTY_TEXT = L10N.getStr("headersEmptyText");
 const HEADERS_FILTER_TEXT = L10N.getStr("headersFilterText");
--- a/devtools/client/netmonitor/src/components/MonitorPanel.js
+++ b/devtools/client/netmonitor/src/components/MonitorPanel.js
@@ -2,29 +2,33 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Services = require("Services");
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
+const { div } = dom;
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
 const Actions = require("../actions/index");
 const { updateFormDataSections } = require("../utils/request-utils");
 const { getSelectedRequest } = require("../selectors/index");
 
 // Components
 const SplitBox = createFactory(require("devtools/client/shared/components/splitter/SplitBox"));
-const NetworkDetailsPanel = createFactory(require("./NetworkDetailsPanel"));
 const RequestList = createFactory(require("./RequestList"));
 const Toolbar = createFactory(require("./Toolbar"));
-const { div } = dom;
+
+loader.lazyGetter(this, "NetworkDetailsPanel", function () {
+  return createFactory(require("./NetworkDetailsPanel"));
+});
+
 const MediaQueryList = window.matchMedia("(min-width: 700px)");
 
 /**
  * Monitor panel component
  * The main panel for displaying various network request information
  */
 class MonitorPanel extends Component {
   static get propTypes() {
--- a/devtools/client/netmonitor/src/components/NetworkDetailsPanel.js
+++ b/devtools/client/netmonitor/src/components/NetworkDetailsPanel.js
@@ -7,18 +7,22 @@
 const { createFactory } = require("devtools/client/shared/vendor/react");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const Actions = require("../actions/index");
 const { getSelectedRequest } = require("../selectors/index");
 
 // Components
-const CustomRequestPanel = createFactory(require("./CustomRequestPanel"));
-const TabboxPanel = createFactory(require("./TabboxPanel"));
+loader.lazyGetter(this, "CustomRequestPanel", function () {
+  return createFactory(require("./CustomRequestPanel"));
+});
+loader.lazyGetter(this, "TabboxPanel", function () {
+  return createFactory(require("./TabboxPanel"));
+});
 
 const { div } = dom;
 
 /**
  * Network details panel component
  */
 function NetworkDetailsPanel({
   connector,
--- a/devtools/client/netmonitor/src/components/PropertiesView.js
+++ b/devtools/client/netmonitor/src/components/PropertiesView.js
@@ -5,28 +5,42 @@
 /* eslint-disable react/prop-types */
 
 "use strict";
 
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
-const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
-const { Rep } = REPS;
-
 const { FILTER_SEARCH_DELAY } = require("../constants");
 
 // Components
-const SearchBox = createFactory(require("devtools/client/shared/components/SearchBox"));
 const TreeViewClass = require("devtools/client/shared/components/tree/TreeView");
 const TreeView = createFactory(TreeViewClass);
-const TreeRow = createFactory(require("devtools/client/shared/components/tree/TreeRow"));
-const SourceEditor = createFactory(require("./SourceEditor"));
-const HTMLPreview = createFactory(require("./HtmlPreview"));
+const SearchBox = createFactory(require("devtools/client/shared/components/SearchBox"));
+
+loader.lazyGetter(this, "SearchBox", function () {
+  return createFactory(require("devtools/client/shared/components/SearchBox"));
+});
+loader.lazyGetter(this, "TreeRow", function () {
+  return createFactory(require("devtools/client/shared/components/tree/TreeRow"));
+});
+loader.lazyGetter(this, "SourceEditor", function () {
+  return createFactory(require("./SourceEditor"));
+});
+loader.lazyGetter(this, "HTMLPreview", function () {
+  return createFactory(require("./HtmlPreview"));
+});
+
+loader.lazyGetter(this, "Rep", function () {
+  return require("devtools/client/shared/components/reps/reps").REPS.Rep;
+});
+loader.lazyGetter(this, "MODE", function () {
+  return require("devtools/client/shared/components/reps/reps").MODE;
+});
 
 const { div, tr, td } = dom;
 const AUTO_EXPAND_MAX_LEVEL = 7;
 const AUTO_EXPAND_MAX_NODES = 50;
 const EDITOR_CONFIG_ID = "EDITOR_CONFIG";
 const HTML_PREVIEW_ID = "HTML_PREVIEW";
 
 /*
--- a/devtools/client/netmonitor/src/components/RequestList.js
+++ b/devtools/client/netmonitor/src/components/RequestList.js
@@ -1,24 +1,28 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
+const { div } = dom;
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 // Components
-const RequestListContent = createFactory(require("./RequestListContent"));
-const RequestListEmptyNotice = createFactory(require("./RequestListEmptyNotice"));
 const StatusBar = createFactory(require("./StatusBar"));
 
-const { div } = dom;
+loader.lazyGetter(this, "RequestListContent", function () {
+  return createFactory(require("./RequestListContent"));
+});
+loader.lazyGetter(this, "RequestListEmptyNotice", function () {
+  return createFactory(require("./RequestListEmptyNotice"));
+});
 
 /**
  * Request panel component
  */
 function RequestList({
   connector,
   isEmpty,
 }) {
rename from devtools/client/netmonitor/src/components/RequestListColumnRemoteIp.js
rename to devtools/client/netmonitor/src/components/RequestListColumnRemoteIP.js
--- a/devtools/client/netmonitor/src/components/RequestListContent.js
+++ b/devtools/client/netmonitor/src/components/RequestListContent.js
@@ -4,29 +4,35 @@
 
 "use strict";
 
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const { HTMLTooltip } = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
-const {
-  setImageTooltip,
-  getImageDimensions,
-} = require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
+
 const Actions = require("../actions/index");
 const { formDataURI } = require("../utils/request-utils");
 const {
   getDisplayedRequests,
   getSelectedRequest,
   getSortedRequests,
   getWaterfallScale,
 } = require("../selectors/index");
 
+loader.lazyGetter(this, "setImageTooltip", function () {
+  return require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper")
+    .setImageTooltip;
+});
+loader.lazyGetter(this, "getImageDimensions", function () {
+  return require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper")
+    .getImageDimensions;
+});
+
 // Components
 const RequestListHeader = createFactory(require("./RequestListHeader"));
 const RequestListItem = createFactory(require("./RequestListItem"));
 const RequestListContextMenu = require("../widgets/RequestListContextMenu");
 
 const { div } = dom;
 
 // Tooltip show / hide delay in ms
--- a/devtools/client/netmonitor/src/components/RequestListItem.js
+++ b/devtools/client/netmonitor/src/components/RequestListItem.js
@@ -1,46 +1,75 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
+const { div } = dom;
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const {
   fetchNetworkUpdatePacket,
   propertiesEqual,
 } = require("../utils/request-utils");
 const { RESPONSE_HEADERS } = require("../constants");
 
 // Components
-const RequestListColumnCause = createFactory(require("./RequestListColumnCause"));
-const RequestListColumnContentSize = createFactory(require("./RequestListColumnContentSize"));
-const RequestListColumnCookies = createFactory(require("./RequestListColumnCookies"));
-const RequestListColumnDomain = createFactory(require("./RequestListColumnDomain"));
-const RequestListColumnDuration = createFactory(require("./RequestListColumnDuration"));
-const RequestListColumnEndTime = createFactory(require("./RequestListColumnEndTime"));
-const RequestListColumnFile = createFactory(require("./RequestListColumnFile"));
-const RequestListColumnLatency = createFactory(require("./RequestListColumnLatency"));
-const RequestListColumnMethod = createFactory(require("./RequestListColumnMethod"));
-const RequestListColumnProtocol = createFactory(require("./RequestListColumnProtocol"));
-const RequestListColumnRemoteIP = createFactory(require("./RequestListColumnRemoteIp"));
-const RequestListColumnResponseHeader = createFactory(require("./RequestListColumnResponseHeader"));
-const RequestListColumnResponseTime = createFactory(require("./RequestListColumnResponseTime"));
-const RequestListColumnScheme = createFactory(require("./RequestListColumnScheme"));
-const RequestListColumnSetCookies = createFactory(require("./RequestListColumnSetCookies"));
-const RequestListColumnStartTime = createFactory(require("./RequestListColumnStartTime"));
-const RequestListColumnStatus = createFactory(require("./RequestListColumnStatus"));
-const RequestListColumnTransferredSize = createFactory(require("./RequestListColumnTransferredSize"));
-const RequestListColumnType = createFactory(require("./RequestListColumnType"));
-const RequestListColumnWaterfall = createFactory(require("./RequestListColumnWaterfall"));
+/* global
+  RequestListColumnCause,
+  RequestListColumnContentSize,
+  RequestListColumnCookies,
+  RequestListColumnDomain,
+  RequestListColumnDuration,
+  RequestListColumnEndTime,
+  RequestListColumnFile,
+  RequestListColumnLatency,
+  RequestListColumnMethod,
+  RequestListColumnProtocol,
+  RequestListColumnRemoteIP,
+  RequestListColumnResponseHeader,
+  RequestListColumnResponseTime,
+  RequestListColumnScheme,
+  RequestListColumnSetCookies,
+  RequestListColumnStartTime,
+  RequestListColumnStatus,
+  RequestListColumnTransferredSize,
+  RequestListColumnType,
+  RequestListColumnWaterfall
+*/
 
-const { div } = dom;
+const COLUMNS = [
+  "Cause",
+  "ContentSize",
+  "Cookies",
+  "Domain",
+  "Duration",
+  "EndTime",
+  "File",
+  "Latency",
+  "Method",
+  "Protocol",
+  "RemoteIP",
+  "ResponseHeader",
+  "ResponseTime",
+  "Scheme",
+  "SetCookies",
+  "StartTime",
+  "Status",
+  "TransferredSize",
+  "Type",
+  "Waterfall"
+];
+for (let name of COLUMNS) {
+  loader.lazyGetter(this, "RequestListColumn" + name, function () {
+    return createFactory(require("./RequestListColumn" + name));
+  });
+}
 
 /**
  * Used by shouldComponentUpdate: compare two items, and compare only properties
  * relevant for rendering the RequestListItem. Other properties (like request and
  * response headers, cookies, bodies) are ignored. These are very useful for the
  * network details, but not here.
  */
 const UPDATED_REQ_ITEM_PROPS = [
--- a/devtools/client/netmonitor/src/components/moz.build
+++ b/devtools/client/netmonitor/src/components/moz.build
@@ -19,17 +19,17 @@ DevToolsModules(
     'RequestListColumnCookies.js',
     'RequestListColumnDomain.js',
     'RequestListColumnDuration.js',
     'RequestListColumnEndTime.js',
     'RequestListColumnFile.js',
     'RequestListColumnLatency.js',
     'RequestListColumnMethod.js',
     'RequestListColumnProtocol.js',
-    'RequestListColumnRemoteIp.js',
+    'RequestListColumnRemoteIP.js',
     'RequestListColumnResponseHeader.js',
     'RequestListColumnResponseTime.js',
     'RequestListColumnScheme.js',
     'RequestListColumnSetCookies.js',
     'RequestListColumnStartTime.js',
     'RequestListColumnStatus.js',
     'RequestListColumnTransferredSize.js',
     'RequestListColumnType.js',
--- a/devtools/client/netmonitor/src/widgets/RequestListContextMenu.js
+++ b/devtools/client/netmonitor/src/widgets/RequestListContextMenu.js
@@ -1,30 +1,31 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Services = require("Services");
-const { Curl } = require("devtools/client/shared/curl");
 const { gDevTools } = require("devtools/client/framework/devtools");
-const { saveAs } = require("devtools/client/shared/file-saver");
-const { copyString } = require("devtools/shared/platform/clipboard");
-const { showMenu } = require("devtools/client/netmonitor/src/utils/menu");
-const { openRequestInTab } = require("devtools/client/netmonitor/src/utils/firefox/open-request-in-tab");
-const { HarExporter } = require("../har/har-exporter");
 const { L10N } = require("../utils/l10n");
 const {
   formDataURI,
   getUrlQuery,
   getUrlBaseName,
   parseQueryString,
 } = require("../utils/request-utils");
 
+loader.lazyRequireGetter(this, "Curl", "devtools/client/shared/curl", true);
+loader.lazyRequireGetter(this, "saveAs", "devtools/client/shared/file-saver", true);
+loader.lazyRequireGetter(this, "copyString", "devtools/shared/platform/clipboard", true);
+loader.lazyRequireGetter(this, "showMenu", "devtools/client/netmonitor/src/utils/menu", true);
+loader.lazyRequireGetter(this, "openRequestInTab", "devtools/client/netmonitor/src/utils/firefox/open-request-in-tab", true);
+loader.lazyRequireGetter(this, "HarExporter", "devtools/client/netmonitor/src/har/har-exporter", true);
+
 class RequestListContextMenu {
   constructor(props) {
     this.props = props;
   }
 
   open(event, selectedRequest, sortedRequests) {
     let {
       id,
--- a/devtools/client/responsive.html/test/browser/browser_touch_simulation.js
+++ b/devtools/client/responsive.html/test/browser/browser_touch_simulation.js
@@ -4,30 +4,29 @@
 "use strict";
 
 // Test global touch simulation button
 
 const TEST_URL = `${URL_ROOT}touch.html`;
 const PREF_DOM_META_VIEWPORT_ENABLED = "dom.meta-viewport.enabled";
 
 addRDMTask(TEST_URL, function* ({ ui }) {
+  yield injectEventUtilsInContentTask(ui.getViewportBrowser());
+
   yield waitBootstrap(ui);
   yield testWithNoTouch(ui);
   yield toggleTouchSimulation(ui);
   yield testWithTouch(ui);
   yield testWithMetaViewportEnabled(ui);
   yield testWithMetaViewportDisabled(ui);
   testTouchButton(ui);
 });
 
 function* testWithNoTouch(ui) {
-  yield injectEventUtils(ui);
   yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
-    let { EventUtils } = content;
-
     let div = content.document.querySelector("div");
     let x = 0, y = 0;
 
     info("testWithNoTouch: Initial test parameter and mouse mouse outside div");
     x = -1; y = -1;
     yield EventUtils.synthesizeMouse(div, x, y,
           { type: "mousemove", isSynthesized: false }, content);
     div.style.transform = "none";
@@ -57,21 +56,17 @@ function* testWithNoTouch(ui) {
     info("testWithNoTouch: Click the div element");
     yield EventUtils.synthesizeClick(div);
     is(div.dataset.isDelay, "false",
       "300ms delay between touch events and mouse events should not work");
   });
 }
 
 function* testWithTouch(ui) {
-  yield injectEventUtils(ui);
-
   yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
-    let { EventUtils } = content;
-
     let div = content.document.querySelector("div");
     let x = 0, y = 0;
 
     info("testWithTouch: Initial test parameter and mouse mouse outside div");
     x = -1; y = -1;
     yield EventUtils.synthesizeMouse(div, x, y,
           { type: "mousemove", isSynthesized: false }, content);
     div.style.transform = "none";
@@ -100,20 +95,18 @@ function* testWithTouch(ui) {
     isnot(div.style.backgroundColor, "blue",
       "mouseout or mouseleave should not work");
   });
 }
 
 function* testWithMetaViewportEnabled(ui) {
   yield SpecialPowers.pushPrefEnv({set: [[PREF_DOM_META_VIEWPORT_ENABLED, true]]});
 
-  yield injectEventUtils(ui);
-
   yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
-    let { synthesizeClick } = content.EventUtils;
+    let { synthesizeClick } = EventUtils;
 
     let meta = content.document.querySelector("meta[name=viewport]");
     let div = content.document.querySelector("div");
     div.dataset.isDelay = "false";
 
     info("testWithMetaViewportEnabled: " +
          "click the div element with <meta name='viewport'>");
     meta.content = "";
@@ -145,20 +138,18 @@ function* testWithMetaViewportEnabled(ui
     is(div.dataset.isDelay, "false",
       "300ms delay between touch events and mouse events should not work");
   });
 }
 
 function* testWithMetaViewportDisabled(ui) {
   yield SpecialPowers.pushPrefEnv({set: [[PREF_DOM_META_VIEWPORT_ENABLED, false]]});
 
-  yield injectEventUtils(ui);
-
   yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
-    let { synthesizeClick } = content.EventUtils;
+    let { synthesizeClick } = EventUtils;
 
     let meta = content.document.querySelector("meta[name=viewport]");
     let div = content.document.querySelector("div");
     div.dataset.isDelay = "false";
 
     info("testWithMetaViewportDisabled: click the div with <meta name='viewport'>");
     meta.content = "";
     yield synthesizeClick(div);
@@ -186,42 +177,8 @@ function testTouchButton(ui) {
 }
 
 function* waitBootstrap(ui) {
   let { store } = ui.toolWindow;
 
   yield waitUntilState(store, state => state.viewports.length == 1);
   yield waitForFrameLoad(ui, TEST_URL);
 }
-
-function* injectEventUtils(ui) {
-  yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
-    if ("EventUtils" in content) {
-      return;
-    }
-
-    let EventUtils = content.EventUtils = {};
-
-    EventUtils.window = {};
-    EventUtils.parent = EventUtils.window;
-    /* eslint-disable camelcase */
-    EventUtils._EU_Ci = Components.interfaces;
-    EventUtils._EU_Cc = Components.classes;
-    /* eslint-enable camelcase */
-    // EventUtils' `sendChar` function relies on the navigator to synthetize events.
-    EventUtils.navigator = content.navigator;
-    EventUtils.KeyboardEvent = content.KeyboardEvent;
-
-    EventUtils.synthesizeClick = element => new Promise(resolve => {
-      element.addEventListener("click", function () {
-        resolve();
-      }, {once: true});
-
-      EventUtils.synthesizeMouseAtCenter(element,
-        { type: "mousedown", isSynthesized: false }, content);
-      EventUtils.synthesizeMouseAtCenter(element,
-        { type: "mouseup", isSynthesized: false }, content);
-    });
-
-    Services.scriptloader.loadSubScript(
-      "chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
-  });
-}
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -2,17 +2,17 @@
 	if(typeof exports === 'object' && typeof module === 'object')
 		module.exports = factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react"));
 	else if(typeof define === 'function' && define.amd)
 		define(["devtools/client/shared/vendor/react-dom-factories", "devtools/client/shared/vendor/lodash", "devtools/client/shared/vendor/react-prop-types", "devtools/client/shared/vendor/react"], factory);
 	else {
 		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react")) : factory(root["devtools/client/shared/vendor/react-dom-factories"], root["devtools/client/shared/vendor/lodash"], root["devtools/client/shared/vendor/react-prop-types"], root["devtools/client/shared/vendor/react"]);
 		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
 	}
-})(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_53__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_6__) {
+})(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_53__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_7__) {
 return /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
 /******/
 /******/ 	// The require function
 /******/ 	function __webpack_require__(moduleId) {
 /******/
 /******/ 		// Check if module is in cache
@@ -528,17 +528,17 @@ module.exports = {
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 __webpack_require__(18);
 
 // Load all existing rep templates
 const Undefined = __webpack_require__(19);
 const Null = __webpack_require__(20);
-const StringRep = __webpack_require__(7);
+const StringRep = __webpack_require__(6);
 const LongStringRep = __webpack_require__(21);
 const Number = __webpack_require__(22);
 const ArrayRep = __webpack_require__(10);
 const Obj = __webpack_require__(23);
 const SymbolRep = __webpack_require__(24);
 const InfinityRep = __webpack_require__(25);
 const NaNRep = __webpack_require__(26);
 const Accessor = __webpack_require__(27);
@@ -739,22 +739,16 @@ function PropRep(props) {
   }, equal), Rep(Object.assign({}, props))];
 }
 
 // Exports from this module
 module.exports = wrapRender(PropRep);
 
 /***/ }),
 /* 6 */
-/***/ (function(module, exports) {
-
-module.exports = __WEBPACK_EXTERNAL_MODULE_6__;
-
-/***/ }),
-/* 7 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -838,18 +832,18 @@ function StringRep(props) {
  * @param {String} text: The actual string to linkify.
  * @param {Integer | null} cropLimit
  * @param {Boolean} omitLinkHref: Do not create an href attribute if true.
  * @param {Function} openLink: Function handling the link opening.
  * @returns {Array<String|ReactElement>}
  */
 function getLinkifiedElements(text, cropLimit, omitLinkHref, openLink) {
   const halfLimit = Math.ceil((cropLimit - ELLIPSIS.length) / 2);
-  const startCropIndex = halfLimit;
-  const endCropIndex = text.length - halfLimit;
+  const startCropIndex = cropLimit ? halfLimit : null;
+  const endCropIndex = cropLimit ? text.length - halfLimit : null;
 
   // As we walk through the tokens of the source string, we make sure to preserve
   // the original whitespace that separated the tokens.
   let currentIndex = 0;
   const items = [];
   for (let token of text.split(tokenSplitRegex)) {
     if (isURL(token)) {
       // Let's grab all the non-url strings before the link.
@@ -897,23 +891,27 @@ function getLinkifiedElements(text, crop
 
 /**
  * Returns a cropped substring given an offset, start and end crop indices in a parent
  * string.
  *
  * @param {String} text: The substring to crop.
  * @param {Integer} offset: The offset corresponding to the index at which the substring
  *                          is in the parent string.
- * @param {Integer} startCropIndex: the index where the start of the crop should happen
- *                                  in the parent string
- * @param {Integer} endCropIndex: the index where the end of the crop should happen
- *                                  in the parent string
+ * @param {Integer|null} startCropIndex: the index where the start of the crop should
+ *                                       happen in the parent string.
+ * @param {Integer|null} endCropIndex: the index where the end of the crop should happen
+ *                                     in the parent string
  * @returns {String|null} The cropped substring, or null if the text is completly cropped.
  */
 function getCroppedString(text, offset = 0, startCropIndex, endCropIndex) {
+  if (!startCropIndex) {
+    return text;
+  }
+
   const start = offset;
   const end = offset + text.length;
 
   const shouldBeVisible = !(start >= startCropIndex && end <= endCropIndex);
   if (!shouldBeVisible) {
     return null;
   }
 
@@ -940,16 +938,22 @@ function supportsObject(object, noGrip =
 // Exports from this module
 
 module.exports = {
   rep: wrapRender(StringRep),
   supportsObject
 };
 
 /***/ }),
+/* 7 */
+/***/ (function(module, exports) {
+
+module.exports = __WEBPACK_EXTERNAL_MODULE_7__;
+
+/***/ }),
 /* 8 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
@@ -1274,17 +1278,17 @@ var _svgInlineReact = __webpack_require_
 var _svgInlineReact2 = _interopRequireDefault(_svgInlineReact);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-const React = __webpack_require__(6);
+const React = __webpack_require__(7);
 const PropTypes = __webpack_require__(2);
 
 
 const svg = {
   "open-inspector": __webpack_require__(34),
   "jump-definition": __webpack_require__(35)
 };
 
@@ -1465,17 +1469,17 @@ module.exports = {
 Object.defineProperty(exports, "__esModule", {
     value: true
 });
 
 var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
 
 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
 
-var _react = __webpack_require__(6);
+var _react = __webpack_require__(7);
 
 var _react2 = _interopRequireDefault(_react);
 
 var _propTypes = __webpack_require__(2);
 
 var _util = __webpack_require__(33);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -1634,30 +1638,40 @@ function GripArray(props) {
   let brackets;
   let needSpace = function (space) {
     return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
   };
 
   if (mode === MODE.TINY) {
     let objectLength = getLength(object);
     let isEmpty = objectLength === 0;
-    if (isEmpty) {
-      items = [];
-    } else {
-      items = [span({
+    let ellipsis;
+    if (!isEmpty) {
+      ellipsis = span({
         className: "more-ellipsis",
         title: "more…"
-      }, "…")];
+      }, "…");
+    }
+
+    let title;
+    if (object.class != "Array") {
+      title = object.class + " ";
     }
     brackets = needSpace(false);
-  } else {
-    let max = maxLengthMap.get(mode);
-    items = arrayIterator(props, object, max);
-    brackets = needSpace(items.length > 0);
-  }
+    return span({
+      "data-link-actor-id": object.actor,
+      className: "objectBox objectBox-array" }, title, span({
+      className: "arrayLeftBracket"
+    }, brackets.left), ellipsis, span({
+      className: "arrayRightBracket"
+    }, brackets.right));
+  }
+  let max = maxLengthMap.get(mode);
+  items = arrayIterator(props, object, max);
+  brackets = needSpace(items.length > 0);
 
   let title = getTitle(props, object);
 
   return span({
     "data-link-actor-id": object.actor,
     className: "objectBox objectBox-array" }, title, span({
     className: "arrayLeftBracket"
   }, brackets.left), ...interleaveCommas(items), span({
@@ -3411,17 +3425,17 @@ const dom = __webpack_require__(1);
 const { span } = dom;
 
 // Reps
 const {
   getGripType,
   isGrip,
   wrapRender
 } = __webpack_require__(0);
-const { rep: StringRep } = __webpack_require__(7);
+const { rep: StringRep } = __webpack_require__(6);
 
 /**
  * Renders DOM attribute
  */
 Attribute.propTypes = {
   object: PropTypes.object.isRequired
 };
 
@@ -3718,33 +3732,35 @@ const {
   wrapRender
 } = __webpack_require__(0);
 const { MODE } = __webpack_require__(3);
 const Svg = __webpack_require__(9);
 
 const dom = __webpack_require__(1);
 const { span } = dom;
 
+const IGNORED_SOURCE_URLS = ["debugger eval code"];
+
 /**
  * This component represents a template for Function objects.
  */
 FunctionRep.propTypes = {
   object: PropTypes.object.isRequired,
   parameterNames: PropTypes.array,
   onViewSourceInDebugger: PropTypes.func
 };
 
 function FunctionRep(props) {
   let {
     object: grip,
     onViewSourceInDebugger
   } = props;
 
   let jumpToDefinitionButton;
-  if (onViewSourceInDebugger && grip.location && grip.location.url) {
+  if (onViewSourceInDebugger && grip.location && grip.location.url && !IGNORED_SOURCE_URLS.includes(grip.location.url)) {
     jumpToDefinitionButton = Svg("jump-definition", {
       element: "a",
       draggable: false,
       title: "Jump to definition",
       onClick: e => {
         // Stop the event propagation so we don't trigger ObjectInspector expand/collapse.
         e.stopPropagation();
         onViewSourceInDebugger(grip.location);
@@ -4227,17 +4243,17 @@ module.exports = {
 // ReactJS
 const PropTypes = __webpack_require__(2);
 
 // Utils
 const {
   isGrip,
   wrapRender
 } = __webpack_require__(0);
-const { rep: StringRep } = __webpack_require__(7);
+const { rep: StringRep } = __webpack_require__(6);
 const { MODE } = __webpack_require__(3);
 const nodeConstants = __webpack_require__(12);
 const Svg = __webpack_require__(9);
 
 const dom = __webpack_require__(1);
 const { span } = dom;
 
 /**
@@ -4631,40 +4647,44 @@ module.exports = {
 const PropTypes = __webpack_require__(2);
 
 // Reps
 const {
   isGrip,
   wrapRender
 } = __webpack_require__(0);
 
+const String = __webpack_require__(6).rep;
+
 const dom = __webpack_require__(1);
 const { span } = dom;
 
 /**
  * Renders a grip object with textual data.
  */
 ObjectWithText.propTypes = {
   object: PropTypes.object.isRequired
 };
 
 function ObjectWithText(props) {
   let grip = props.object;
   return span({
     "data-link-actor-id": grip.actor,
-    className: "objectBox objectBox-" + getType(grip)
-  }, span({ className: "objectPropValue" }, getDescription(grip)));
+    className: "objectTitle objectBox objectBox-" + getType(grip)
+  }, getType(grip), " ", getDescription(grip));
 }
 
 function getType(grip) {
   return grip.class;
 }
 
 function getDescription(grip) {
-  return "\"" + grip.preview.text + "\"";
+  return String({
+    object: grip.preview.text
+  });
 }
 
 // Registration
 function supportsObject(grip, noGrip = false) {
   if (noGrip === true || !isGrip(grip)) {
     return false;
   }
 
@@ -4755,17 +4775,17 @@ var _devtoolsComponents = __webpack_requ
 var _devtoolsComponents2 = _interopRequireDefault(_devtoolsComponents);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-const { Component, createFactory } = __webpack_require__(6);
+const { Component, createFactory } = __webpack_require__(7);
 const PropTypes = __webpack_require__(2);
 const dom = __webpack_require__(1);
 
 const Tree = createFactory(_devtoolsComponents2.default.Tree);
 __webpack_require__(51);
 
 const classnames = __webpack_require__(52);
 
@@ -5208,17 +5228,17 @@ exports.default = {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 
-var _react = __webpack_require__(6);
+var _react = __webpack_require__(7);
 
 var _react2 = _interopRequireDefault(_react);
 
 var _reactDomFactories = __webpack_require__(1);
 
 var _reactDomFactories2 = _interopRequireDefault(_reactDomFactories);
 
 var _propTypes = __webpack_require__(2);
--- a/devtools/server/tests/browser/browser.ini
+++ b/devtools/server/tests/browser/browser.ini
@@ -20,16 +20,17 @@ support-files =
   storage-unsecured-iframe.html
   storage-updates.html
   storage-secured-iframe.html
   stylesheets-nested-iframes.html
   timeline-iframe-child.html
   timeline-iframe-parent.html
   storage-helpers.js
   !/devtools/server/tests/mochitest/hello-actor.js
+  !/devtools/client/framework/test/shared-head.js
 
 [browser_accessibility_node_events.js]
 [browser_accessibility_node.js]
 [browser_accessibility_simple.js]
 [browser_accessibility_walker.js]
 [browser_animation_emitMutations.js]
 [browser_animation_getFrames.js]
 [browser_animation_getProperties.js]
--- a/devtools/server/tests/browser/browser_canvasframe_helper_01.js
+++ b/devtools/server/tests/browser/browser_canvasframe_helper_01.js
@@ -1,91 +1,89 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Simple CanvasFrameAnonymousContentHelper tests.
 
-// This makes sure the 'domnode' protocol actor type is known when importing
-// highlighter.
-require("devtools/server/actors/inspector");
-const {HighlighterEnvironment} = require("devtools/server/actors/highlighters");
-
-const {
-  CanvasFrameAnonymousContentHelper
-} = require("devtools/server/actors/highlighters/utils/markup");
-
 const TEST_URL = "data:text/html;charset=utf-8,CanvasFrameAnonymousContentHelper test";
 
-add_task(function* () {
-  let browser = yield addTab(TEST_URL);
-  // eslint-disable-next-line mozilla/no-cpows-in-tests
-  let doc = browser.contentDocument;
+add_task(async function () {
+  let browser = await addTab(TEST_URL);
+
+  await ContentTask.spawn(browser, null, async function () {
+    const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+    const {HighlighterEnvironment} = require("devtools/server/actors/highlighters");
+    const {
+      CanvasFrameAnonymousContentHelper
+    } = require("devtools/server/actors/highlighters/utils/markup");
+    let doc = content.document;
 
-  let nodeBuilder = () => {
-    let root = doc.createElement("div");
-    let child = doc.createElement("div");
-    child.style = "width:200px;height:200px;background:red;";
-    child.id = "child-element";
-    child.className = "child-element";
-    child.textContent = "test element";
-    root.appendChild(child);
-    return root;
-  };
+    let nodeBuilder = () => {
+      let root = doc.createElement("div");
+      let child = doc.createElement("div");
+      child.style = "width:200px;height:200px;background:red;";
+      child.id = "child-element";
+      child.className = "child-element";
+      child.textContent = "test element";
+      root.appendChild(child);
+      return root;
+    };
 
-  info("Building the helper");
-  let env = new HighlighterEnvironment();
-  env.initFromWindow(doc.defaultView);
-  let helper = new CanvasFrameAnonymousContentHelper(env, nodeBuilder);
+    info("Building the helper");
+    let env = new HighlighterEnvironment();
+    env.initFromWindow(doc.defaultView);
+    let helper = new CanvasFrameAnonymousContentHelper(env, nodeBuilder);
 
-  ok(helper.content instanceof AnonymousContent,
-    "The helper owns the AnonymousContent object");
-  ok(helper.getTextContentForElement,
-    "The helper has the getTextContentForElement method");
-  ok(helper.setTextContentForElement,
-    "The helper has the setTextContentForElement method");
-  ok(helper.setAttributeForElement,
-    "The helper has the setAttributeForElement method");
-  ok(helper.getAttributeForElement,
-    "The helper has the getAttributeForElement method");
-  ok(helper.removeAttributeForElement,
-    "The helper has the removeAttributeForElement method");
-  ok(helper.addEventListenerForElement,
-    "The helper has the addEventListenerForElement method");
-  ok(helper.removeEventListenerForElement,
-    "The helper has the removeEventListenerForElement method");
-  ok(helper.getElement,
-    "The helper has the getElement method");
-  ok(helper.scaleRootElement,
-    "The helper has the scaleRootElement method");
+    ok(helper.content instanceof content.AnonymousContent,
+      "The helper owns the AnonymousContent object");
+    ok(helper.getTextContentForElement,
+      "The helper has the getTextContentForElement method");
+    ok(helper.setTextContentForElement,
+      "The helper has the setTextContentForElement method");
+    ok(helper.setAttributeForElement,
+      "The helper has the setAttributeForElement method");
+    ok(helper.getAttributeForElement,
+      "The helper has the getAttributeForElement method");
+    ok(helper.removeAttributeForElement,
+      "The helper has the removeAttributeForElement method");
+    ok(helper.addEventListenerForElement,
+      "The helper has the addEventListenerForElement method");
+    ok(helper.removeEventListenerForElement,
+      "The helper has the removeEventListenerForElement method");
+    ok(helper.getElement,
+      "The helper has the getElement method");
+    ok(helper.scaleRootElement,
+      "The helper has the scaleRootElement method");
 
-  is(helper.getTextContentForElement("child-element"), "test element",
-    "The text content was retrieve correctly");
-  is(helper.getAttributeForElement("child-element", "id"), "child-element",
-    "The ID attribute was retrieve correctly");
-  is(helper.getAttributeForElement("child-element", "class"), "child-element",
-    "The class attribute was retrieve correctly");
+    is(helper.getTextContentForElement("child-element"), "test element",
+      "The text content was retrieve correctly");
+    is(helper.getAttributeForElement("child-element", "id"), "child-element",
+      "The ID attribute was retrieve correctly");
+    is(helper.getAttributeForElement("child-element", "class"), "child-element",
+      "The class attribute was retrieve correctly");
 
-  let el = helper.getElement("child-element");
-  ok(el, "The DOMNode-like element was created");
+    let el = helper.getElement("child-element");
+    ok(el, "The DOMNode-like element was created");
 
-  is(el.getTextContent(), "test element",
-    "The text content was retrieve correctly");
-  is(el.getAttribute("id"), "child-element",
-    "The ID attribute was retrieve correctly");
-  is(el.getAttribute("class"), "child-element",
-    "The class attribute was retrieve correctly");
+    is(el.getTextContent(), "test element",
+      "The text content was retrieve correctly");
+    is(el.getAttribute("id"), "child-element",
+      "The ID attribute was retrieve correctly");
+    is(el.getAttribute("class"), "child-element",
+      "The class attribute was retrieve correctly");
 
-  info("Destroying the helper");
-  helper.destroy();
-  env.destroy();
+    info("Destroying the helper");
+    helper.destroy();
+    env.destroy();
 
-  ok(!helper.getTextContentForElement("child-element"),
-    "No text content was retrieved after the helper was destroyed");
-  ok(!helper.getAttributeForElement("child-element", "id"),
-    "No ID attribute was retrieved after the helper was destroyed");
-  ok(!helper.getAttributeForElement("child-element", "class"),
-    "No class attribute was retrieved after the helper was destroyed");
+    ok(!helper.getTextContentForElement("child-element"),
+      "No text content was retrieved after the helper was destroyed");
+    ok(!helper.getAttributeForElement("child-element", "id"),
+      "No ID attribute was retrieved after the helper was destroyed");
+    ok(!helper.getAttributeForElement("child-element", "class"),
+      "No class attribute was retrieved after the helper was destroyed");
+  });
 
   gBrowser.removeCurrentTab();
 });
--- a/devtools/server/tests/browser/browser_canvasframe_helper_02.js
+++ b/devtools/server/tests/browser/browser_canvasframe_helper_02.js
@@ -2,48 +2,45 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test that the CanvasFrameAnonymousContentHelper does not insert content in
 // XUL windows.
 
-// This makes sure the 'domnode' protocol actor type is known when importing
-// highlighter.
-require("devtools/server/actors/inspector");
-
-const {HighlighterEnvironment} = require("devtools/server/actors/highlighters");
+add_task(async function () {
+  let browser = await addTab("about:preferences");
 
-const {
-  CanvasFrameAnonymousContentHelper
-} = require("devtools/server/actors/highlighters/utils/markup");
-
-add_task(function* () {
-  let browser = yield addTab("about:preferences");
-  // eslint-disable-next-line mozilla/no-cpows-in-tests
-  let doc = browser.contentDocument;
+  await ContentTask.spawn(browser, null, async function () {
+    const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+    const {HighlighterEnvironment} = require("devtools/server/actors/highlighters");
+    const {
+      CanvasFrameAnonymousContentHelper
+    } = require("devtools/server/actors/highlighters/utils/markup");
+    let doc = content.document;
 
-  let nodeBuilder = () => {
-    let root = doc.createElement("div");
-    let child = doc.createElement("div");
-    child.style = "width:200px;height:200px;background:red;";
-    child.id = "child-element";
-    child.className = "child-element";
-    child.textContent = "test element";
-    root.appendChild(child);
-    return root;
-  };
+    let nodeBuilder = () => {
+      let root = doc.createElement("div");
+      let child = doc.createElement("div");
+      child.style = "width:200px;height:200px;background:red;";
+      child.id = "child-element";
+      child.className = "child-element";
+      child.textContent = "test element";
+      root.appendChild(child);
+      return root;
+    };
 
-  info("Building the helper");
-  let env = new HighlighterEnvironment();
-  env.initFromWindow(doc.defaultView);
-  let helper = new CanvasFrameAnonymousContentHelper(env, nodeBuilder);
+    info("Building the helper");
+    let env = new HighlighterEnvironment();
+    env.initFromWindow(doc.defaultView);
+    let helper = new CanvasFrameAnonymousContentHelper(env, nodeBuilder);
 
-  ok(!helper.content, "The AnonymousContent was not inserted in the window");
-  ok(!helper.getTextContentForElement("child-element"),
-    "No text content is returned");
+    ok(!helper.content, "The AnonymousContent was not inserted in the window");
+    ok(!helper.getTextContentForElement("child-element"),
+      "No text content is returned");
 
-  env.destroy();
-  helper.destroy();
+    env.destroy();
+    helper.destroy();
+  });
 
   gBrowser.removeCurrentTab();
 });
--- a/devtools/server/tests/browser/browser_canvasframe_helper_03.js
+++ b/devtools/server/tests/browser/browser_canvasframe_helper_03.js
@@ -1,103 +1,113 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test the CanvasFrameAnonymousContentHelper event handling mechanism.
 
-// This makes sure the 'domnode' protocol actor type is known when importing
-// highlighter.
-require("devtools/server/actors/inspector");
-
-const {HighlighterEnvironment} = require("devtools/server/actors/highlighters");
-
-const {
-  CanvasFrameAnonymousContentHelper
-} = require("devtools/server/actors/highlighters/utils/markup");
-
 const TEST_URL = "data:text/html;charset=utf-8,CanvasFrameAnonymousContentHelper test";
 
-add_task(function* () {
-  let browser = yield addTab(TEST_URL);
-  // eslint-disable-next-line mozilla/no-cpows-in-tests
-  let doc = browser.contentDocument;
+add_task(async function () {
+  let browser = await addTab(TEST_URL);
+  await ContentTask.spawn(browser, null, async function () {
+    const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+    const {HighlighterEnvironment} = require("devtools/server/actors/highlighters");
+    const {
+      CanvasFrameAnonymousContentHelper
+    } = require("devtools/server/actors/highlighters/utils/markup");
+    let doc = content.document;
 
-  let nodeBuilder = () => {
-    let root = doc.createElement("div");
-    let child = doc.createElement("div");
-    child.style = "pointer-events:auto;width:200px;height:200px;background:red;";
-    child.id = "child-element";
-    child.className = "child-element";
-    root.appendChild(child);
-    return root;
-  };
+    let nodeBuilder = () => {
+      let root = doc.createElement("div");
+      let child = doc.createElement("div");
+      child.style = "pointer-events:auto;width:200px;height:200px;background:red;";
+      child.id = "child-element";
+      child.className = "child-element";
+      root.appendChild(child);
+      return root;
+    };
 
-  info("Building the helper");
-  let env = new HighlighterEnvironment();
-  env.initFromWindow(doc.defaultView);
-  let helper = new CanvasFrameAnonymousContentHelper(env, nodeBuilder);
+    info("Building the helper");
+    let env = new HighlighterEnvironment();
+    env.initFromWindow(doc.defaultView);
+    let helper = new CanvasFrameAnonymousContentHelper(env, nodeBuilder);
 
-  let el = helper.getElement("child-element");
+    let el = helper.getElement("child-element");
 
-  info("Adding an event listener on the inserted element");
-  let mouseDownHandled = 0;
-  function onMouseDown(e, id) {
-    is(id, "child-element", "The mousedown event was triggered on the element");
-    ok(!e.originalTarget, "The originalTarget property isn't available");
-    mouseDownHandled++;
-  }
-  el.addEventListener("mousedown", onMouseDown);
+    info("Adding an event listener on the inserted element");
+    let mouseDownHandled = 0;
+    function onMouseDown(e, id) {
+      is(id, "child-element", "The mousedown event was triggered on the element");
+      ok(!e.originalTarget, "The originalTarget property isn't available");
+      mouseDownHandled++;
+    }
+    el.addEventListener("mousedown", onMouseDown);
 
-  info("Synthesizing an event on the inserted element");
-  let onDocMouseDown = once(doc, "mousedown");
-  synthesizeMouseDown(100, 100, doc.defaultView);
-  yield onDocMouseDown;
+    function once(target, event) {
+      return new Promise(done => {
+        target.addEventListener(event, done, { once: true });
+      });
+    }
+
+    info("Synthesizing an event on the inserted element");
+    let onDocMouseDown = once(doc, "mousedown");
+    synthesizeMouseDown(100, 100, doc.defaultView);
+    await onDocMouseDown;
+
+    is(mouseDownHandled, 1, "The mousedown event was handled once on the element");
 
-  is(mouseDownHandled, 1, "The mousedown event was handled once on the element");
+    info("Synthesizing an event somewhere else");
+    onDocMouseDown = once(doc, "mousedown");
+    synthesizeMouseDown(400, 400, doc.defaultView);
+    await onDocMouseDown;
 
-  info("Synthesizing an event somewhere else");
-  onDocMouseDown = once(doc, "mousedown");
-  synthesizeMouseDown(400, 400, doc.defaultView);
-  yield onDocMouseDown;
+    is(mouseDownHandled, 1, "The mousedown event was not handled on the element");
 
-  is(mouseDownHandled, 1, "The mousedown event was not handled on the element");
+    info("Removing the event listener");
+    el.removeEventListener("mousedown", onMouseDown);
 
-  info("Removing the event listener");
-  el.removeEventListener("mousedown", onMouseDown);
+    info("Synthesizing another event after the listener has been removed");
+    // Using a document event listener to know when the event has been synthesized.
+    onDocMouseDown = once(doc, "mousedown");
+    synthesizeMouseDown(100, 100, doc.defaultView);
+    await onDocMouseDown;
 
-  info("Synthesizing another event after the listener has been removed");
-  // Using a document event listener to know when the event has been synthesized.
-  onDocMouseDown = once(doc, "mousedown");
-  synthesizeMouseDown(100, 100, doc.defaultView);
-  yield onDocMouseDown;
+    is(mouseDownHandled, 1,
+      "The mousedown event hasn't been handled after the listener was removed");
+
+    info("Adding again the event listener");
+    el.addEventListener("mousedown", onMouseDown);
+
+    info("Destroying the helper");
+    env.destroy();
+    helper.destroy();
 
-  is(mouseDownHandled, 1,
-    "The mousedown event hasn't been handled after the listener was removed");
+    info("Synthesizing another event after the helper has been destroyed");
+    // Using a document event listener to know when the event has been synthesized.
+    onDocMouseDown = once(doc, "mousedown");
+    synthesizeMouseDown(100, 100, doc.defaultView);
+    await onDocMouseDown;
 
-  info("Adding again the event listener");
-  el.addEventListener("mousedown", onMouseDown);
+    is(mouseDownHandled, 1,
+      "The mousedown event hasn't been handled after the helper was destroyed");
 
-  info("Destroying the helper");
-  env.destroy();
-  helper.destroy();
-
-  info("Synthesizing another event after the helper has been destroyed");
-  // Using a document event listener to know when the event has been synthesized.
-  onDocMouseDown = once(doc, "mousedown");
-  synthesizeMouseDown(100, 100, doc.defaultView);
-  yield onDocMouseDown;
-
-  is(mouseDownHandled, 1,
-    "The mousedown event hasn't been handled after the helper was destroyed");
+    function synthesizeMouseDown(x, y, win) {
+      // We need to make sure the inserted anonymous content can be targeted by the
+      // event right after having been inserted, and so we need to force a sync
+      // reflow.
+      win.document.documentElement.offsetWidth;
+      // Minimal environment for EventUtils to work.
+      let EventUtils = {
+        window: content,
+        parent: content,
+        _EU_Ci: Components.interfaces,
+        _EU_Cc: Components.classes,
+      };
+      Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
+      EventUtils.synthesizeMouseAtPoint(x, y, {type: "mousedown"}, win);
+    }
+  });
 
   gBrowser.removeCurrentTab();
 });
-
-function synthesizeMouseDown(x, y, win) {
-  // We need to make sure the inserted anonymous content can be targeted by the
-  // event right after having been inserted, and so we need to force a sync
-  // reflow.
-  win.document.documentElement.offsetWidth;
-  EventUtils.synthesizeMouseAtPoint(x, y, {type: "mousedown"}, win);
-}
--- a/devtools/server/tests/browser/browser_canvasframe_helper_04.js
+++ b/devtools/server/tests/browser/browser_canvasframe_helper_04.js
@@ -2,100 +2,104 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test the CanvasFrameAnonymousContentHelper re-inserts the content when the
 // page reloads.
 
-// This makes sure the 'domnode' protocol actor type is known when importing
-// highlighter.
-require("devtools/server/actors/inspector");
-
-const {HighlighterEnvironment} = require("devtools/server/actors/highlighters");
-
-const {
-  CanvasFrameAnonymousContentHelper
-} = require("devtools/server/actors/highlighters/utils/markup");
-
 const TEST_URL_1 =
   "data:text/html;charset=utf-8,CanvasFrameAnonymousContentHelper test 1";
 const TEST_URL_2 =
   "data:text/html;charset=utf-8,CanvasFrameAnonymousContentHelper test 2";
 
-add_task(function* () {
-  let browser = yield addTab(TEST_URL_1);
-  // eslint-disable-next-line mozilla/no-cpows-in-tests
-  let doc = browser.contentDocument;
+add_task(async function () {
+  let browser = await addTab(TEST_URL_1);
+  await injectEventUtilsInContentTask(browser);
+  await ContentTask.spawn(browser, TEST_URL_2, async function (url2) {
+    const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+    const {HighlighterEnvironment} = require("devtools/server/actors/highlighters");
+    const {
+      CanvasFrameAnonymousContentHelper
+    } = require("devtools/server/actors/highlighters/utils/markup");
+    let doc = content.document;
+
+    let nodeBuilder = () => {
+      let root = doc.createElement("div");
+      let child = doc.createElement("div");
+      child.style = "pointer-events:auto;width:200px;height:200px;background:red;";
+      child.id = "child-element";
+      child.className = "child-element";
+      child.textContent = "test content";
+      root.appendChild(child);
+      return root;
+    };
 
-  let nodeBuilder = () => {
-    let root = doc.createElement("div");
-    let child = doc.createElement("div");
-    child.style = "pointer-events:auto;width:200px;height:200px;background:red;";
-    child.id = "child-element";
-    child.className = "child-element";
-    child.textContent = "test content";
-    root.appendChild(child);
-    return root;
-  };
+    info("Building the helper");
+    let env = new HighlighterEnvironment();
+    env.initFromWindow(doc.defaultView);
+    let helper = new CanvasFrameAnonymousContentHelper(env, nodeBuilder);
+
+    info("Get an element from the helper");
+    let el = helper.getElement("child-element");
 
-  info("Building the helper");
-  let env = new HighlighterEnvironment();
-  env.initFromWindow(doc.defaultView);
-  let helper = new CanvasFrameAnonymousContentHelper(env, nodeBuilder);
+    info("Try to access the element");
+    is(el.getAttribute("class"), "child-element",
+      "The attribute is correct before navigation");
+    is(el.getTextContent(), "test content",
+      "The text content is correct before navigation");
 
-  info("Get an element from the helper");
-  let el = helper.getElement("child-element");
-
-  info("Try to access the element");
-  is(el.getAttribute("class"), "child-element",
-    "The attribute is correct before navigation");
-  is(el.getTextContent(), "test content",
-    "The text content is correct before navigation");
+    info("Add an event listener on the element");
+    let mouseDownHandled = 0;
+    let onMouseDown = (e, id) => {
+      is(id, "child-element", "The mousedown event was triggered on the element");
+      mouseDownHandled++;
+    };
+    el.addEventListener("mousedown", onMouseDown);
 
-  info("Add an event listener on the element");
-  let mouseDownHandled = 0;
-  function onMouseDown(e, id) {
-    is(id, "child-element", "The mousedown event was triggered on the element");
-    mouseDownHandled++;
-  }
-  el.addEventListener("mousedown", onMouseDown);
+    let once = function once(target, event) {
+      return new Promise(done => {
+        target.addEventListener(event, done, { once: true });
+      });
+    };
 
-  info("Synthesizing an event on the element");
-  let onDocMouseDown = once(doc, "mousedown");
-  synthesizeMouseDown(100, 100, doc.defaultView);
-  yield onDocMouseDown;
-  is(mouseDownHandled, 1, "The mousedown event was handled once before navigation");
+    let synthesizeMouseDown = function synthesizeMouseDown(x, y, win) {
+      // We need to make sure the inserted anonymous content can be targeted by the
+      // event right after having been inserted, and so we need to force a sync
+      // reflow.
+      win.document.documentElement.offsetWidth;
+      EventUtils.synthesizeMouseAtPoint(x, y, {type: "mousedown"}, win);
+    };
+
+    info("Synthesizing an event on the element");
+    let onDocMouseDown = once(doc, "mousedown");
+    synthesizeMouseDown(100, 100, doc.defaultView);
+    await onDocMouseDown;
+    is(mouseDownHandled, 1, "The mousedown event was handled once before navigation");
 
-  info("Navigating to a new page");
-  let loaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
-  BrowserTestUtils.loadURI(browser, TEST_URL_2);
-  yield loaded;
-  // eslint-disable-next-line mozilla/no-cpows-in-tests
-  doc = gBrowser.selectedBrowser.contentWindow.document;
+    info("Navigating to a new page");
+    let loaded = once(this, "load");
+    content.location = url2;
+    await loaded;
+
+    // Update to the new document we just loaded
+    doc = content.document;
 
-  info("Try to access the element again");
-  is(el.getAttribute("class"), "child-element",
-    "The attribute is correct after navigation");
-  is(el.getTextContent(), "test content",
-    "The text content is correct after navigation");
+    info("Try to access the element again");
+    is(el.getAttribute("class"), "child-element",
+      "The attribute is correct after navigation");
+    is(el.getTextContent(), "test content",
+      "The text content is correct after navigation");
 
-  info("Synthesizing an event on the element again");
-  onDocMouseDown = once(doc, "mousedown");
-  synthesizeMouseDown(100, 100, doc.defaultView);
-  yield onDocMouseDown;
-  is(mouseDownHandled, 1, "The mousedown event was not handled after navigation");
+    info("Synthesizing an event on the element again");
+    onDocMouseDown = once(doc, "mousedown");
+    synthesizeMouseDown(100, 100, doc.defaultView);
+    await onDocMouseDown;
+    is(mouseDownHandled, 1, "The mousedown event was not handled after navigation");
 
-  info("Destroying the helper");
-  env.destroy();
-  helper.destroy();
+    info("Destroying the helper");
+    env.destroy();
+    helper.destroy();
+  });
 
   gBrowser.removeCurrentTab();
 });
-
-function synthesizeMouseDown(x, y, win) {
-  // We need to make sure the inserted anonymous content can be targeted by the
-  // event right after having been inserted, and so we need to force a sync
-  // reflow.
-  win.document.documentElement.offsetWidth;
-  EventUtils.synthesizeMouseAtPoint(x, y, {type: "mousedown"}, win);
-}
--- a/devtools/server/tests/browser/browser_canvasframe_helper_05.js
+++ b/devtools/server/tests/browser/browser_canvasframe_helper_05.js
@@ -2,112 +2,122 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test some edge cases of the CanvasFrameAnonymousContentHelper event handling
 // mechanism.
 
-// This makes sure the 'domnode' protocol actor type is known when importing
-// highlighter.
-require("devtools/server/actors/inspector");
-
-const {HighlighterEnvironment} = require("devtools/server/actors/highlighters");
-
-const {
-  CanvasFrameAnonymousContentHelper
-} = require("devtools/server/actors/highlighters/utils/markup");
-
 const TEST_URL = "data:text/html;charset=utf-8,CanvasFrameAnonymousContentHelper test";
 
-add_task(function* () {
-  let browser = yield addTab(TEST_URL);
-  // eslint-disable-next-line mozilla/no-cpows-in-tests
-  let doc = browser.contentDocument;
+add_task(async function () {
+  let browser = await addTab(TEST_URL);
+  await ContentTask.spawn(browser, null, async function () {
+    const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+    const {HighlighterEnvironment} = require("devtools/server/actors/highlighters");
+    const {
+      CanvasFrameAnonymousContentHelper
+    } = require("devtools/server/actors/highlighters/utils/markup");
+    let doc = content.document;
 
-  let nodeBuilder = () => {
-    let root = doc.createElement("div");
+    let nodeBuilder = () => {
+      let root = doc.createElement("div");
 
-    let parent = doc.createElement("div");
-    parent.style = "pointer-events:auto;width:300px;height:300px;background:yellow;";
-    parent.id = "parent-element";
-    root.appendChild(parent);
+      let parent = doc.createElement("div");
+      parent.style = "pointer-events:auto;width:300px;height:300px;background:yellow;";
+      parent.id = "parent-element";
+      root.appendChild(parent);
 
-    let child = doc.createElement("div");
-    child.style = "pointer-events:auto;width:200px;height:200px;background:red;";
-    child.id = "child-element";
-    parent.appendChild(child);
+      let child = doc.createElement("div");
+      child.style = "pointer-events:auto;width:200px;height:200px;background:red;";
+      child.id = "child-element";
+      parent.appendChild(child);
+
+      return root;
+    };
 
-    return root;
-  };
+    info("Building the helper");
+    let env = new HighlighterEnvironment();
+    env.initFromWindow(doc.defaultView);
+    let helper = new CanvasFrameAnonymousContentHelper(env, nodeBuilder);
 
-  info("Building the helper");
-  let env = new HighlighterEnvironment();
-  env.initFromWindow(doc.defaultView);
-  let helper = new CanvasFrameAnonymousContentHelper(env, nodeBuilder);
+    info("Getting the parent and child elements");
+    let parentEl = helper.getElement("parent-element");
+    let childEl = helper.getElement("child-element");
 
-  info("Getting the parent and child elements");
-  let parentEl = helper.getElement("parent-element");
-  let childEl = helper.getElement("child-element");
+    info("Adding an event listener on both elements");
+    let mouseDownHandled = [];
+    function onMouseDown(e, id) {
+      mouseDownHandled.push(id);
+    }
+    parentEl.addEventListener("mousedown", onMouseDown);
+    childEl.addEventListener("mousedown", onMouseDown);
 
-  info("Adding an event listener on both elements");
-  let mouseDownHandled = [];
-  function onMouseDown(e, id) {
-    mouseDownHandled.push(id);
-  }
-  parentEl.addEventListener("mousedown", onMouseDown);
-  childEl.addEventListener("mousedown", onMouseDown);
+    function once(target, event) {
+      return new Promise(done => {
+        target.addEventListener(event, done, { once: true });
+      });
+    }
+
+    info("Synthesizing an event on the child element");
+    let onDocMouseDown = once(doc, "mousedown");
+    synthesizeMouseDown(100, 100, doc.defaultView);
+    await onDocMouseDown;
 
-  info("Synthesizing an event on the child element");
-  let onDocMouseDown = once(doc, "mousedown");
-  synthesizeMouseDown(100, 100, doc.defaultView);
-  yield onDocMouseDown;
+    is(mouseDownHandled.length, 2, "The mousedown event was handled twice");
+    is(mouseDownHandled[0], "child-element",
+      "The mousedown event was handled on the child element");
+    is(mouseDownHandled[1], "parent-element",
+      "The mousedown event was handled on the parent element");
+
+    info("Synthesizing an event on the parent, outside of the child element");
+    mouseDownHandled = [];
+    onDocMouseDown = once(doc, "mousedown");
+    synthesizeMouseDown(250, 250, doc.defaultView);
+    await onDocMouseDown;
 
-  is(mouseDownHandled.length, 2, "The mousedown event was handled twice");
-  is(mouseDownHandled[0], "child-element",
-    "The mousedown event was handled on the child element");
-  is(mouseDownHandled[1], "parent-element",
-    "The mousedown event was handled on the parent element");
+    is(mouseDownHandled.length, 1, "The mousedown event was handled only once");
+    is(mouseDownHandled[0], "parent-element",
+      "The mousedown event was handled on the parent element");
 
-  info("Synthesizing an event on the parent, outside of the child element");
-  mouseDownHandled = [];
-  onDocMouseDown = once(doc, "mousedown");
-  synthesizeMouseDown(250, 250, doc.defaultView);
-  yield onDocMouseDown;
+    info("Removing the event listener");
+    parentEl.removeEventListener("mousedown", onMouseDown);
+    childEl.removeEventListener("mousedown", onMouseDown);
 
-  is(mouseDownHandled.length, 1, "The mousedown event was handled only once");
-  is(mouseDownHandled[0], "parent-element",
-    "The mousedown event was handled on the parent element");
+    info("Adding an event listener on the parent element only");
+    mouseDownHandled = [];
+    parentEl.addEventListener("mousedown", onMouseDown);
 
-  info("Removing the event listener");
-  parentEl.removeEventListener("mousedown", onMouseDown);
-  childEl.removeEventListener("mousedown", onMouseDown);
+    info("Synthesizing an event on the child element");
+    onDocMouseDown = once(doc, "mousedown");
+    synthesizeMouseDown(100, 100, doc.defaultView);
+    await onDocMouseDown;
 
-  info("Adding an event listener on the parent element only");
-  mouseDownHandled = [];
-  parentEl.addEventListener("mousedown", onMouseDown);
+    is(mouseDownHandled.length, 1, "The mousedown event was handled once");
+    is(mouseDownHandled[0], "parent-element",
+      "The mousedown event did bubble to the parent element");
+
+    info("Removing the parent listener");
+    parentEl.removeEventListener("mousedown", onMouseDown);
+
+    env.destroy();
+    helper.destroy();
 
-  info("Synthesizing an event on the child element");
-  onDocMouseDown = once(doc, "mousedown");
-  synthesizeMouseDown(100, 100, doc.defaultView);
-  yield onDocMouseDown;
-
-  is(mouseDownHandled.length, 1, "The mousedown event was handled once");
-  is(mouseDownHandled[0], "parent-element",
-    "The mousedown event did bubble to the parent element");
-
-  info("Removing the parent listener");
-  parentEl.removeEventListener("mousedown", onMouseDown);
-
-  env.destroy();
-  helper.destroy();
+    function synthesizeMouseDown(x, y, win) {
+      // We need to make sure the inserted anonymous content can be targeted by the
+      // event right after having been inserted, and so we need to force a sync
+      // reflow.
+      win.document.documentElement.offsetWidth;
+      // Minimal environment for EventUtils to work.
+      let EventUtils = {
+        window: content,
+        parent: content,
+        _EU_Ci: Components.interfaces,
+        _EU_Cc: Components.classes,
+      };
+      Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
+      EventUtils.synthesizeMouseAtPoint(x, y, {type: "mousedown"}, win);
+    }
+  });
 
   gBrowser.removeCurrentTab();
 });
-
-function synthesizeMouseDown(x, y, win) {
-  // We need to make sure the inserted anonymous content can be targeted by the
-  // event right after having been inserted, and so we need to force a sync
-  // reflow.
-  win.document.documentElement.offsetWidth;
-  EventUtils.synthesizeMouseAtPoint(x, y, {type: "mousedown"}, win);
-}
--- a/devtools/server/tests/browser/browser_canvasframe_helper_06.js
+++ b/devtools/server/tests/browser/browser_canvasframe_helper_06.js
@@ -2,100 +2,110 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // Test support for event propagation stop in the
 // CanvasFrameAnonymousContentHelper event handling mechanism.
 
-// This makes sure the 'domnode' protocol actor type is known when importing
-// highlighter.
-require("devtools/server/actors/inspector");
-
-const {HighlighterEnvironment} = require("devtools/server/actors/highlighters");
-
-const {
-  CanvasFrameAnonymousContentHelper
-} = require("devtools/server/actors/highlighters/utils/markup");
-
 const TEST_URL = "data:text/html;charset=utf-8,CanvasFrameAnonymousContentHelper test";
 
-add_task(function* () {
-  let browser = yield addTab(TEST_URL);
-  // eslint-disable-next-line mozilla/no-cpows-in-tests
-  let doc = browser.contentDocument;
+add_task(async function () {
+  let browser = await addTab(TEST_URL);
+  await ContentTask.spawn(browser, null, async function () {
+    const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
+    const {HighlighterEnvironment} = require("devtools/server/actors/highlighters");
+    const {
+      CanvasFrameAnonymousContentHelper
+    } = require("devtools/server/actors/highlighters/utils/markup");
+    let doc = content.document;
 
-  let nodeBuilder = () => {
-    let root = doc.createElement("div");
+    let nodeBuilder = () => {
+      let root = doc.createElement("div");
 
-    let parent = doc.createElement("div");
-    parent.style = "pointer-events:auto;width:300px;height:300px;background:yellow;";
-    parent.id = "parent-element";
-    root.appendChild(parent);
+      let parent = doc.createElement("div");
+      parent.style = "pointer-events:auto;width:300px;height:300px;background:yellow;";
+      parent.id = "parent-element";
+      root.appendChild(parent);
+
+      let child = doc.createElement("div");
+      child.style = "pointer-events:auto;width:200px;height:200px;background:red;";
+      child.id = "child-element";
+      parent.appendChild(child);
 
-    let child = doc.createElement("div");
-    child.style = "pointer-events:auto;width:200px;height:200px;background:red;";
-    child.id = "child-element";
-    parent.appendChild(child);
+      return root;
+    };
 
-    return root;
-  };
+    info("Building the helper");
+    let env = new HighlighterEnvironment();
+    env.initFromWindow(doc.defaultView);
+    let helper = new CanvasFrameAnonymousContentHelper(env, nodeBuilder);
+
+    info("Getting the parent and child elements");
+    let parentEl = helper.getElement("parent-element");
+    let childEl = helper.getElement("child-element");
 
-  info("Building the helper");
-  let env = new HighlighterEnvironment();
-  env.initFromWindow(doc.defaultView);
-  let helper = new CanvasFrameAnonymousContentHelper(env, nodeBuilder);
+    info("Adding an event listener on both elements");
+    let mouseDownHandled = [];
 
-  info("Getting the parent and child elements");
-  let parentEl = helper.getElement("parent-element");
-  let childEl = helper.getElement("child-element");
+    function onParentMouseDown(e, id) {
+      mouseDownHandled.push(id);
+    }
+    parentEl.addEventListener("mousedown", onParentMouseDown);
 
-  info("Adding an event listener on both elements");
-  let mouseDownHandled = [];
+    function onChildMouseDown(e, id) {
+      mouseDownHandled.push(id);
+      e.stopPropagation();
+    }
+    childEl.addEventListener("mousedown", onChildMouseDown);
 
-  function onParentMouseDown(e, id) {
-    mouseDownHandled.push(id);
-  }
-  parentEl.addEventListener("mousedown", onParentMouseDown);
+    function once(target, event) {
+      return new Promise(done => {
+        target.addEventListener(event, done, { once: true });
+      });
+    }
 
-  function onChildMouseDown(e, id) {
-    mouseDownHandled.push(id);
-    e.stopPropagation();
-  }
-  childEl.addEventListener("mousedown", onChildMouseDown);
+    info("Synthesizing an event on the child element");
+    let onDocMouseDown = once(doc, "mousedown");
+    synthesizeMouseDown(100, 100, doc.defaultView);
+    await onDocMouseDown;
 
-  info("Synthesizing an event on the child element");
-  let onDocMouseDown = once(doc, "mousedown");
-  synthesizeMouseDown(100, 100, doc.defaultView);
-  yield onDocMouseDown;
+    is(mouseDownHandled.length, 1, "The mousedown event was handled only once");
+    is(mouseDownHandled[0], "child-element",
+      "The mousedown event was handled on the child element");
+
+    info("Synthesizing an event on the parent, outside of the child element");
+    mouseDownHandled = [];
+    onDocMouseDown = once(doc, "mousedown");
+    synthesizeMouseDown(250, 250, doc.defaultView);
+    await onDocMouseDown;
 
-  is(mouseDownHandled.length, 1, "The mousedown event was handled only once");
-  is(mouseDownHandled[0], "child-element",
-    "The mousedown event was handled on the child element");
+    is(mouseDownHandled.length, 1, "The mousedown event was handled only once");
+    is(mouseDownHandled[0], "parent-element",
+      "The mousedown event was handled on the parent element");
 
-  info("Synthesizing an event on the parent, outside of the child element");
-  mouseDownHandled = [];
-  onDocMouseDown = once(doc, "mousedown");
-  synthesizeMouseDown(250, 250, doc.defaultView);
-  yield onDocMouseDown;
+    info("Removing the event listener");
+    parentEl.removeEventListener("mousedown", onParentMouseDown);
+    childEl.removeEventListener("mousedown", onChildMouseDown);
+
+    env.destroy();
+    helper.destroy();
 
-  is(mouseDownHandled.length, 1, "The mousedown event was handled only once");
-  is(mouseDownHandled[0], "parent-element",
-    "The mousedown event was handled on the parent element");
-
-  info("Removing the event listener");
-  parentEl.removeEventListener("mousedown", onParentMouseDown);
-  childEl.removeEventListener("mousedown", onChildMouseDown);
-
-  env.destroy();
-  helper.destroy();
+    function synthesizeMouseDown(x, y, win) {
+      // We need to make sure the inserted anonymous content can be targeted by the
+      // event right after having been inserted, and so we need to force a sync
+      // reflow.
+      win.document.documentElement.offsetWidth;
+      // Minimal environment for EventUtils to work.
+      let EventUtils = {
+        window: content,
+        parent: content,
+        _EU_Ci: Components.interfaces,
+        _EU_Cc: Components.classes,
+      };
+      Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
+      EventUtils.synthesizeMouseAtPoint(x, y, {type: "mousedown"}, win);
+    }
+  });
 
   gBrowser.removeCurrentTab();
 });
-
-function synthesizeMouseDown(x, y, win) {
-  // We need to make sure the inserted anonymous content can be targeted by the
-  // event right after having been inserted, and so we need to force a sync
-  // reflow.
-  win.document.documentElement.offsetWidth;
-  EventUtils.synthesizeMouseAtPoint(x, y, {type: "mousedown"}, win);
-}
--- a/devtools/server/tests/browser/head.js
+++ b/devtools/server/tests/browser/head.js
@@ -1,27 +1,23 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 /* eslint no-unused-vars: [2, {"vars": "local"}] */
+/* import-globals-from ../../../client/framework/test/shared-head.js */
 
-var Cc = Components.classes;
-var Ci = Components.interfaces;
-var Cu = Components.utils;
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
+  this);
 
-const {console} = Cu.import("resource://gre/modules/Console.jsm", {});
-const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
 const {DebuggerClient} = require("devtools/shared/client/debugger-client");
 const {DebuggerServer} = require("devtools/server/main");
-const {defer} = require("promise");
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
-const Services = require("Services");
 
 const PATH = "browser/devtools/server/tests/browser/";
 const MAIN_DOMAIN = "http://test1.example.org/" + PATH;
 const ALT_DOMAIN = "http://sectest1.example.org/" + PATH;
 const ALT_DOMAIN_SECURED = "https://sectest1.example.org:443/" + PATH;
 
 // GUID to be used as a separator in compound keys. This must match the same
 // constant in devtools/server/actors/storage.js,
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -2333,16 +2333,24 @@ nsDOMWindowUtils::GetUsingAdvancedLayers
   *retval = false;
   if (KnowsCompositor* fwd = mgr->AsKnowsCompositor()) {
     *retval = fwd->GetTextureFactoryIdentifier().mUsingAdvancedLayers;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDOMWindowUtils::GetIsWebRenderRequested(bool* retval)
+{
+  *retval = gfxPlatform::WebRenderPrefEnabled() ||
+            gfxPlatform::WebRenderEnvvarEnabled();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMWindowUtils::GetCurrentAudioBackend(nsAString& aBackend)
 {
   CubebUtils::GetCurrentBackend(aBackend);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::GetCurrentMaxAudioChannels(uint32_t* aChannels)
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -1327,16 +1327,23 @@ interface nsIDOMWindowUtils : nsISupport
   readonly attribute boolean layerManagerRemote;
 
   /**
    * True if advanced layers is enabled on this window, false otherwise.
    */
   readonly attribute boolean usingAdvancedLayers;
 
   /**
+   * True if webrender was requested by the user (via pref or env-var), false
+   * otherwise. Note that this doesn't represent whether or not webrender is
+   * *actually* enabled, just whether or not it was requested.
+   */
+  readonly attribute boolean isWebRenderRequested;
+
+  /**
    * Returns the current audio backend as a free-form string.
    */
   readonly attribute AString currentAudioBackend;
 
   /**
    * Returns the max channel counts of the current audio device.
    */
   readonly attribute unsigned long currentMaxAudioChannels;
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1805,17 +1805,18 @@ ContentChild::RecvPBrowserConstructor(PB
                                       const bool& aIsForBrowser)
 {
   MOZ_ASSERT(!IsShuttingDown());
 
   static bool hasRunOnce = false;
   if (!hasRunOnce) {
     hasRunOnce = true;
     MOZ_ASSERT(!gFirstIdleTask);
-    RefPtr<CancelableRunnable> firstIdleTask = NewCancelableRunnableFunction(FirstIdle);
+    RefPtr<CancelableRunnable> firstIdleTask = NewCancelableRunnableFunction("FirstIdleRunnable",
+                                                                             FirstIdle);
     gFirstIdleTask = firstIdleTask;
     NS_IdleDispatchToCurrentThread(firstIdleTask.forget());
   }
 
   return nsIContentChild::RecvPBrowserConstructor(aActor,
                                                   aTabId,
                                                   aSameTabGroupAs,
                                                   aContext,
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1758,17 +1758,18 @@ ContentParent::ActorDestroy(ActorDestroy
   RefPtr<ParentIdleListener> listener;
   for (int32_t i = mIdleListeners.Length() - 1; i >= 0; --i) {
     listener = static_cast<ParentIdleListener*>(mIdleListeners[i].get());
     idleService->RemoveIdleObserver(listener, listener->mTime);
   }
   mIdleListeners.Clear();
 
   MessageLoop::current()->
-    PostTask(NewRunnableFunction(DelayedDeleteSubprocess, mSubprocess));
+    PostTask(NewRunnableFunction("DelayedDeleteSubprocessRunnable",
+                                 DelayedDeleteSubprocess, mSubprocess));
   mSubprocess = nullptr;
 
   // IPDL rules require actors to live on past ActorDestroy, but it
   // may be that the kungFuDeathGrip above is the last reference to
   // |this|.  If so, when we go out of scope here, we're deleted and
   // all hell breaks loose.
   //
   // This runnable ensures that a reference to |this| lives on at
@@ -3115,17 +3116,18 @@ ContentParent::OnGenerateMinidumpComplet
   }
 
   if (mSubprocess) {
     mSubprocess->SetAlreadyDead();
   }
 
   // EnsureProcessTerminated has responsibilty for closing otherProcessHandle.
   XRE_GetIOMessageLoop()->PostTask(
-    NewRunnableFunction(&ProcessWatcher::EnsureProcessTerminated,
+    NewRunnableFunction("EnsureProcessTerminatedRunnable",
+                        &ProcessWatcher::EnsureProcessTerminated,
                         otherProcessHandle, /*force=*/true));
 }
 
 void
 ContentParent::FriendlyName(nsAString& aName, bool aAnonymize)
 {
   aName.Truncate();
   if (mIsForBrowser) {
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -826,31 +826,34 @@ class RTCPeerConnection {
   }
 
   // Handles offerToReceiveAudio/Video
   _ensureTransceiversForOfferToReceive(options) {
     if (options.offerToReceiveVideo) {
       this._ensureOfferToReceive("video");
     }
 
-    if (options.offerToReceiveVideo === false) {
-      this.logWarning("offerToReceiveVideo: false is ignored now. If you " +
-                      "want to disallow a recv track, use " +
-                      "RTCRtpTransceiver.direction");
-    }
-
     if (options.offerToReceiveAudio) {
       this._ensureOfferToReceive("audio");
     }
 
-    if (options.offerToReceiveAudio === false) {
-      this.logWarning("offerToReceiveAudio: false is ignored now. If you " +
-                      "want to disallow a recv track, use " +
-                      "RTCRtpTransceiver.direction");
-    }
+    this._transceivers
+      .filter(transceiver => {
+        return (options.offerToReceiveVideo === false &&
+                transceiver.receiver.track.kind == "video") ||
+               (options.offerToReceiveAudio === false &&
+                transceiver.receiver.track.kind == "audio");
+      })
+      .forEach(transceiver => {
+        if (transceiver.direction == "sendrecv") {
+          transceiver.setDirectionInternal("sendonly");
+        } else if (transceiver.direction == "recvonly") {
+          transceiver.setDirectionInternal("inactive");
+        }
+      });
   }
 
   async _createOffer(options) {
     this._checkClosed();
     let origin = Cu.getWebIDLCallerPrincipal().origin;
     return this._chain(async () => {
       let haveAssertion;
       if (this._localIdp.enabled) {
--- a/dom/media/ipc/VideoDecoderManagerChild.cpp
+++ b/dom/media/ipc/VideoDecoderManagerChild.cpp
@@ -47,17 +47,18 @@ VideoDecoderManagerChild::InitializeThre
     sRecreateTasks = MakeUnique<nsTArray<RefPtr<Runnable>>>();
   }
 }
 
 /* static */ void
 VideoDecoderManagerChild::InitForContent(Endpoint<PVideoDecoderManagerChild>&& aVideoManager)
 {
   InitializeThread();
-  sVideoDecoderChildThread->Dispatch(NewRunnableFunction(&Open, Move(aVideoManager)), NS_DISPATCH_NORMAL);
+  sVideoDecoderChildThread->Dispatch(NewRunnableFunction("InitForContentRunnable",
+                                                         &Open, Move(aVideoManager)), NS_DISPATCH_NORMAL);
 }
 
 /* static */ void
 VideoDecoderManagerChild::Shutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (sVideoDecoderChildThread) {
--- a/dom/media/test/crashtests/crashtests.list
+++ b/dom/media/test/crashtests/crashtests.list
@@ -7,17 +7,17 @@ load 474744-1.html
 HTTP load 481136-1.html # needs to be HTTP to recognize the ogg as an audio file?
 load 492286-1.xhtml
 load 493915-1.html
 load 495794-1.html
 load 497734-1.xhtml
 load 497734-2.html
 load 576612-1.html
 load 752784-1.html
-skip-if(Android) load 789075-1.html # bug 1374405
+skip-if(Android||(webrender&&winWidget)) load 789075-1.html # bug 1374405 for android, bug 1426199 for webrender
 skip-if(Android&&AndroidVersion=='22') HTTP load 795892-1.html # bug 1358718
 load 844563.html
 load 846612.html
 load 852838.html
 load 865537-1.html
 load 868504.html
 load 874869.html
 load 874915.html
@@ -90,17 +90,17 @@ load 1267263.html
 load 1270303.html
 load 1304948.html
 load 1319486.html
 load 1368490.html
 load 1291702.html
 load 1378826.html
 load 1384248.html
 load 1389304.html
-load 1393272.webm
+skip-if(webrender&&winWidget) load 1393272.webm # bug 1426199 for webrender
 load 1411322.html
 load disconnect-wrong-destination.html
 load analyser-channels-1.html
 load audiocontext-double-suspend.html
 load buffer-source-duration-1.html
 load buffer-source-ended-1.html
 load buffer-source-resampling-start-1.html
 load buffer-source-slow-resampling-1.html
--- a/dom/media/tests/mochitest/test_peerConnection_transceivers.html
+++ b/dom/media/tests/mochitest/test_peerConnection_transceivers.html
@@ -351,24 +351,28 @@
 
     pc.close();
     stopTracks(audioStream, videoStream);
   };
 
   let checkAddTransceiverWithOfferToReceive = async kinds => {
     let pc = new RTCPeerConnection();
 
+    let propsToSet = kinds.map(kind => {
+      if (kind == "audio") {
+        return "offerToReceiveAudio";
+      } else if (kind == "video") {
+        return "offerToReceiveVideo";
+      }
+    });
+
     let options = {};
 
-    for (let kind of kinds) {
-      if (kind == "audio") {
-        options.offerToReceiveAudio = true;
-      } else if (kind == "video") {
-        options.offerToReceiveVideo = true;
-      }
+    for (let prop of propsToSet) {
+      options[prop] = true;
     }
 
     let offer = await pc.createOffer(options);
 
     let expected = [];
 
     // NOTE: The ordering here is not laid out in the spec at all, this is
     // firefox specific.
@@ -393,16 +397,45 @@
           mid: null,
           currentDirection: null,
           stopped: false
         });
     }
 
     hasProps(pc.getTransceivers(), expected);
 
+    // Test offerToReceive: false
+    for (let prop of propsToSet) {
+      options[prop] = false;
+    }
+
+    // Check that sendrecv goes to sendonly
+    for (let transceiver of pc.getTransceivers()) {
+      transceiver.direction = "sendrecv";
+    }
+
+    for (let transceiverCheck of expected) {
+      transceiverCheck.direction = "sendonly";
+    }
+
+    offer = await pc.createOffer(options);
+    hasProps(pc.getTransceivers(), expected);
+
+    // Check that recvonly goes to inactive
+    for (let transceiver of pc.getTransceivers()) {
+      transceiver.direction = "recvonly";
+    }
+
+    for (let transceiverCheck of expected) {
+      transceiverCheck.direction = "inactive";
+    }
+
+    offer = await pc.createOffer(options);
+    hasProps(pc.getTransceivers(), expected);
+
     pc.close();
   };
 
   let checkAddTransceiverWithSetRemoteOfferSending = async () => {
     let pc1 = new RTCPeerConnection();
     let pc2 = new RTCPeerConnection();
 
     let stream = await getUserMedia({audio: true});
--- a/dom/plugins/test/crashtests/crashtests.list
+++ b/dom/plugins/test/crashtests/crashtests.list
@@ -4,11 +4,11 @@ HTTP load 110650-1.html
 skip-if(!haveTestPlugin) HTTP script 539897-1.html
 asserts-if(winWidget&&browserIsRemote,0-1) skip-if(!haveTestPlugin) HTTP script 540114-1.html
 HTTP load 570884.html
 # This test relies on the reading of screenX/Y forcing a round trip to
 # the X server, which is a bad assumption for <browser remote>.
 # Plugin arch is going to change anyway with OOP content so skipping
 # this test for now is OK.
 skip-if(!haveTestPlugin||http.platform!="X11") HTTP load 598862.html
-skip-if(Android) HTTP load 626602-1.html # bug 908363
+skip-if(Android||(webrender&&winWidget)) HTTP load 626602-1.html # bug 908363; bug 1322815 for webrender
 HTTP load 752340.html
 HTTP load 843086.xhtml
--- a/dom/svg/test/test_tabindex.html
+++ b/dom/svg/test/test_tabindex.html
@@ -19,16 +19,18 @@
     This is SVG text
   </text>
   <a xlink:href="#" id="l1" tabindex="3">
     <circle cx="10" cy="230" r="10"/>
   </a>
   <a id="l2" tabindex="4">
     <circle cx="10" cy="260" r="10"/>
   </a>
+  <rect id="r6" x="0" y="70" width="100" height="100" fill="yellow" tabindex="6"/>
+  <rect id="r7" x="0" y="70" width="100" height="100" fill="yellow" tabindex="7"/>
 </svg>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 SimpleTest.waitForExplicitFinish();
 
 function main()
 {
   var f = document.getElementById('f');
@@ -70,34 +72,33 @@ function main()
     is(document.activeElement.tabIndex, 2, "The active element tabindex is 2");
 
     synthesizeKey("VK_TAB", {});
     is(document.activeElement.tabIndex, 3, "The active element tabindex is 3");
 
     synthesizeKey("VK_TAB", {});
     // On Mac, SVG link elements should not be focused.
     if (isMac) {
-      is(document.activeElement.tabIndex, 3, "The active element tabindex is 3");
+      is(document.activeElement.tabIndex, 6, "The active element tabindex is 6");
     } else {
       is(document.activeElement.tabIndex, 4, "The active element tabindex is 4");
     }
 
     synthesizeKey("VK_TAB", {});
     // On Mac, SVG link elements should not be focused.
     if (isMac) {
-      // This test has to be run with other tests, otherwise,
-      // document.activeElement.tabIndex will be -1 on Mac.
-      is(document.activeElement.tabIndex, 3, "The active element tabindex is 3");
+      is(document.activeElement.tabIndex, 7, "The active element tabindex is 7");
     } else {
       is(document.activeElement.tabIndex, 5, "The active element tabindex is 5");
     }
   } catch(e) {
     ok(false, "Got unexpected exception" + e);
   }
 
   SimpleTest.finish();
 }
 
-window.addEventListener("load", main);
+SimpleTest.waitForFocus(main);
+
 </script>
 </pre>
 </body>
 </html>
--- a/dom/webidl/CSSNamespaceRule.webidl
+++ b/dom/webidl/CSSNamespaceRule.webidl
@@ -4,13 +4,11 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * The origin of this IDL file is
  * https://drafts.csswg.org/cssom/#cssnamespacerule
  */
 
 // https://drafts.csswg.org/cssom/#cssnamespacerule
 interface CSSNamespaceRule : CSSRule {
-  // Not implemented yet.  <See
-  // https://bugzilla.mozilla.org/show_bug.cgi?id=1326514>.
-  //  readonly attribute DOMString namespaceURI;
-  //  readonly attribute DOMString prefix;
+  readonly attribute DOMString namespaceURI;
+  readonly attribute DOMString prefix;
 };
--- a/dom/workers/ServiceWorkerJob.cpp
+++ b/dom/workers/ServiceWorkerJob.cpp
@@ -222,17 +222,17 @@ ServiceWorkerJob::Finish(ErrorResult& aR
     mFinalCallback = nullptr;
   }
 
   // The callback might not consume the error.
   aRv.SuppressException();
 
   // Async release this object to ensure that our caller methods complete
   // as well.
-  NS_ReleaseOnMainThreadSystemGroup("ServiceWorkerJob",
+  NS_ReleaseOnMainThreadSystemGroup("ServiceWorkerJobProxyRunnable",
     kungFuDeathGrip.forget(), true /* always proxy */);
 }
 
 void
 ServiceWorkerJob::Finish(nsresult aRv)
 {
   ErrorResult converted(aRv);
   Finish(converted);
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -1012,17 +1012,17 @@ ServiceWorkerPrivate::SendPushEvent(cons
 {
   nsresult rv = SpawnWorkerIfNeeded(PushEvent, nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
 
   RefPtr<KeepAliveToken> token = CreateEventKeepAliveToken();
 
   nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> regInfo(
     new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(
-      "ServiceWorkerRegistrationInfo", aRegistration, false));
+      "ServiceWorkerRegistrationInfoProxy", aRegistration, false));
 
   RefPtr<WorkerRunnable> r = new SendPushEventRunnable(mWorkerPrivate,
                                                        token,
                                                        aMessageId,
                                                        aData,
                                                        regInfo);
 
   if (mInfo->State() == ServiceWorkerState::Activating) {
@@ -1755,17 +1755,17 @@ ServiceWorkerPrivate::SendFetchEvent(nsI
   }
 
   nsMainThreadPtrHandle<nsIInterceptedChannel> handle(
     new nsMainThreadPtrHolder<nsIInterceptedChannel>(
       "nsIInterceptedChannel", aChannel, false));
 
   nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> regInfo(
     new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(
-      "ServiceWorkerRegistrationInfo", registration, false));
+      "ServiceWorkerRegistrationInfoProxy", registration, false));
 
   RefPtr<KeepAliveToken> token = CreateEventKeepAliveToken();
 
 
   RefPtr<FetchEventRunnable> r =
     new FetchEventRunnable(mWorkerPrivate, token, handle,
                            mInfo->ScriptSpec(), regInfo,
                            aClientId, aIsReload, newWorkerCreated);
--- a/dom/workers/ServiceWorkerRegistrationInfo.cpp
+++ b/dom/workers/ServiceWorkerRegistrationInfo.cpp
@@ -280,17 +280,17 @@ ServiceWorkerRegistrationInfo::Activate(
   nsCOMPtr<nsIRunnable> failRunnable = NewRunnableMethod<bool>(
     "dom::workers::ServiceWorkerRegistrationInfo::FinishActivate",
     this,
     &ServiceWorkerRegistrationInfo::FinishActivate,
     false /* success */);
 
   nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> handle(
     new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(
-      "ServiceWorkerRegistrationInfo", this));
+      "ServiceWorkerRegistrationInfoProxy", this));
   RefPtr<LifeCycleEventCallback> callback = new ContinueActivateRunnable(handle);
 
   ServiceWorkerPrivate* workerPrivate = mActiveWorker->WorkerPrivate();
   MOZ_ASSERT(workerPrivate);
   nsresult rv = workerPrivate->SendLifeCycleEvent(NS_LITERAL_STRING("activate"),
                                                   callback, failRunnable);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(failRunnable));
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -3871,22 +3871,16 @@ EditorBase::IsDescendantOfEditorRoot(nsI
 
 bool
 EditorBase::IsContainer(nsINode* aNode)
 {
   return aNode ? true : false;
 }
 
 bool
-EditorBase::IsContainer(nsIDOMNode* aNode)
-{
-  return aNode ? true : false;
-}
-
-bool
 EditorBase::IsEditable(nsIDOMNode* aNode)
 {
   nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
   return IsEditable(content);
 }
 
 uint32_t
 EditorBase::CountEditableChildren(nsINode* aNode)
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -1058,17 +1058,16 @@ public:
   bool IsDescendantOfRoot(nsIDOMNode* inNode);
   bool IsDescendantOfRoot(nsINode* inNode);
   bool IsDescendantOfEditorRoot(nsINode* aNode);
 
   /**
    * Returns true if aNode is a container.
    */
   virtual bool IsContainer(nsINode* aNode);
-  virtual bool IsContainer(nsIDOMNode* aNode);
 
   /**
    * returns true if aNode is an editable node.
    */
   bool IsEditable(nsIDOMNode* aNode);
   bool IsEditable(nsINode* aNode)
   {
     NS_ENSURE_TRUE(aNode, false);
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -3466,27 +3466,16 @@ HTMLEditor::IsContainer(nsINode* aNode)
     tagEnum = eHTMLTag_text;
   } else {
     tagEnum = nsHTMLTags::StringTagToId(aNode->NodeName());
   }
 
   return HTMLEditUtils::IsContainer(tagEnum);
 }
 
-bool
-HTMLEditor::IsContainer(nsIDOMNode* aNode)
-{
-  nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
-  if (!node) {
-    return false;
-  }
-  return IsContainer(node);
-}
-
-
 nsresult
 HTMLEditor::SelectEntireDocument(Selection* aSelection)
 {
   if (!aSelection || !mRules) {
     return NS_ERROR_NULL_POINTER;
   }
 
   // Protect the edit rules object from dying
@@ -4046,17 +4035,17 @@ HTMLEditor::IsEmptyNodeImpl(nsINode* aNo
   }
 
   // if it's not a text node (handled above) and it's not a container,
   // then we don't call it empty (it's an <hr>, or <br>, etc.).
   // Also, if it's an anchor then don't treat it as empty - even though
   // anchors are containers, named anchors are "empty" but we don't
   // want to treat them as such.  Also, don't call ListItems or table
   // cells empty if caller desires.  Form Widgets not empty.
-  if (!IsContainer(aNode->AsDOMNode()) ||
+  if (!IsContainer(aNode) ||
       (HTMLEditUtils::IsNamedAnchor(aNode) ||
        HTMLEditUtils::IsFormWidget(aNode) ||
        (aListOrCellNotEmpty &&
         (HTMLEditUtils::IsListItem(aNode) ||
          HTMLEditUtils::IsTableCell(aNode))))) {
     *outIsEmptyNode = false;
     return NS_OK;
   }
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -294,17 +294,16 @@ public:
    */
   virtual bool TagCanContainTag(nsAtom& aParentTag,
                                 nsAtom& aChildTag) const override;
 
   /**
    * Returns true if aNode is a container.
    */
   virtual bool IsContainer(nsINode* aNode) override;
-  virtual bool IsContainer(nsIDOMNode* aNode) override;
 
   /**
    * Make the given selection span the entire document.
    */
   virtual nsresult SelectEntireDocument(Selection* aSelection) override;
 
   /**
    * Join together any adjacent editable text nodes in the range.
--- a/gfx/gl/GLContextProviderGLX.cpp
+++ b/gfx/gl/GLContextProviderGLX.cpp
@@ -12,17 +12,17 @@
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 #include "X11UndefineNone.h"
 
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/layers/CompositorOptions.h"
 #include "mozilla/widget/CompositorWidget.h"
-#include "mozilla/widget/X11CompositorWidget.h"
+#include "mozilla/widget/GtkCompositorWidget.h"
 #include "mozilla/Unused.h"
 
 #include "prenv.h"
 #include "GLContextProvider.h"
 #include "GLLibraryLoader.h"
 #include "nsDebug.h"
 #include "nsIWidget.h"
 #include "GLXLibrary.h"
@@ -798,17 +798,17 @@ CreateForWidget(Display* aXDisplay, Wind
     }
     return GLContextGLX::CreateGLContext(flags, SurfaceCaps::Any(), false, aXDisplay,
                                          aXWindow, config, false, nullptr);
 }
 
 already_AddRefed<GLContext>
 GLContextProviderGLX::CreateForCompositorWidget(CompositorWidget* aCompositorWidget, bool aForceAccelerated)
 {
-    X11CompositorWidget* compWidget = aCompositorWidget->AsX11();
+    GtkCompositorWidget* compWidget = aCompositorWidget->AsX11();
     MOZ_ASSERT(compWidget);
 
     return CreateForWidget(compWidget->XDisplay(),
                            compWidget->XWindow(),
                            compWidget->GetCompositorOptions().UseWebRender(),
                            aForceAccelerated);
 }
 
--- a/gfx/ipc/GPUProcessHost.cpp
+++ b/gfx/ipc/GPUProcessHost.cpp
@@ -240,13 +240,13 @@ GPUProcessHost::DestroyProcess()
   // Cancel all tasks. We don't want anything triggering after our caller
   // expects this to go away.
   {
     MonitorAutoLock lock(mMonitor);
     mTaskFactory.RevokeAll();
   }
 
   MessageLoop::current()->
-    PostTask(NewRunnableFunction(DelayedDeleteSubprocess, this));
+    PostTask(NewRunnableFunction("DestroyProcessRunnable", DelayedDeleteSubprocess, this));
 }
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/layers/PaintThread.cpp
+++ b/gfx/layers/PaintThread.cpp
@@ -191,17 +191,19 @@ PaintThread::Shutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   UniquePtr<PaintThread> pt(sSingleton.forget());
   if (!pt) {
     return;
   }
 
-  sThread->Dispatch(NewRunnableFunction(DestroyPaintThread, Move(pt)));
+  sThread->Dispatch(NewRunnableFunction("DestroyPaintThreadRunnable",
+                                        DestroyPaintThread,
+                                        Move(pt)));
   sThread->Shutdown();
   sThread = nullptr;
 }
 
 void
 PaintThread::ShutdownOnPaintThread()
 {
   MOZ_ASSERT(IsOnPaintThread());
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -2379,17 +2379,18 @@ APZCTreeManager::BuildOverscrollHandoffC
 
   return result;
 }
 
 void
 APZCTreeManager::SetLongTapEnabled(bool aLongTapEnabled)
 {
   APZThreadUtils::RunOnControllerThread(
-    NewRunnableFunction(GestureEventListener::SetLongTapEnabled, aLongTapEnabled));
+    NewRunnableFunction("SetLongTapEnabledRunnable",
+                        GestureEventListener::SetLongTapEnabled, aLongTapEnabled));
 }
 
 RefPtr<HitTestingTreeNode>
 APZCTreeManager::FindScrollThumbNode(const AsyncDragMetrics& aDragMetrics)
 {
   MutexAutoLock lock(mTreeLock);
 
   return DepthFirstSearch<ReverseIterator>(mRootNode.get(),
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -333,23 +333,25 @@ DeallocateTextureClient(TextureDeallocPa
   }
 
   // First make sure that the work is happening on the IPDL thread.
   if (ipdlMsgLoop && MessageLoop::current() != ipdlMsgLoop) {
     if (params.syncDeallocation) {
       bool done = false;
       ReentrantMonitor barrier("DeallocateTextureClient");
       ReentrantMonitorAutoEnter autoMon(barrier);
-      ipdlMsgLoop->PostTask(NewRunnableFunction(DeallocateTextureClientSyncProxy,
+      ipdlMsgLoop->PostTask(NewRunnableFunction("DeallocateTextureClientSyncProxyRunnable",
+                                                DeallocateTextureClientSyncProxy,
                                                 params, &barrier, &done));
       while (!done) {
         barrier.Wait();
       }
     } else {
-      ipdlMsgLoop->PostTask(NewRunnableFunction(DeallocateTextureClient,
+      ipdlMsgLoop->PostTask(NewRunnableFunction("DeallocateTextureClientRunnable",
+                                                DeallocateTextureClient,
                                                 params));
     }
     // The work has been forwarded to the IPDL thread, we are done.
     return;
   }
 
   // Below this line, we are either in the IPDL thread or ther is no IPDL
   // thread anymore.
@@ -835,17 +837,18 @@ void CancelTextureClientRecycle(uint64_t
   MessageLoop* msgLoop = nullptr;
   msgLoop = aAllocator->GetMessageLoop();
   if (!msgLoop) {
     return;
   }
   if (MessageLoop::current() == msgLoop) {
     aAllocator->CancelWaitForRecycle(aTextureId);
   } else {
-    msgLoop->PostTask(NewRunnableFunction(CancelTextureClientRecycle,
+    msgLoop->PostTask(NewRunnableFunction("CancelTextureClientRecycleRunnable",
+                                          CancelTextureClientRecycle,
                                           aTextureId, aAllocator));
   }
 }
 
 void
 TextureClient::CancelWaitForRecycle()
 {
   if (GetFlags() & TextureFlags::RECYCLE) {
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -490,17 +490,18 @@ CompositorBridgeChild::RecvCaptureAllPlu
 {
 #if defined(XP_WIN)
   MOZ_ASSERT(NS_IsMainThread());
   nsIWidget::CaptureRegisteredPlugins(aParentWidget);
 
   // Bounce the call to SendAllPluginsCaptured off the ImageBridgeChild loop,
   // to make sure that the image updates on that thread have been processed.
   ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(
-    NewRunnableFunction(&ScheduleSendAllPluginsCaptured, this,
+    NewRunnableFunction("ScheduleSendAllPluginsCapturedRunnable",
+                        &ScheduleSendAllPluginsCaptured, this,
                         MessageLoop::current()));
   return IPC_OK();
 #else
   MOZ_ASSERT_UNREACHABLE(
     "CompositorBridgeChild::RecvCaptureAllPlugins calls unexpected.");
   return IPC_FAIL_NO_REASON(this);
 #endif
 }
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -377,20 +377,22 @@ CompositorBridgeParent::Initialize()
   MOZ_ASSERT(CompositorThread(),
              "The compositor thread must be Initialized before instanciating a CompositorBridgeParent.");
 
   mCompositorBridgeID = 0;
   // FIXME: This holds on the the fact that right now the only thing that
   // can destroy this instance is initialized on the compositor thread after
   // this task has been processed.
   MOZ_ASSERT(CompositorLoop());
-  CompositorLoop()->PostTask(NewRunnableFunction(&AddCompositor,
+  CompositorLoop()->PostTask(NewRunnableFunction("AddCompositorRunnable",
+                                                 &AddCompositor,
                                                  this, &mCompositorBridgeID));
 
-  CompositorLoop()->PostTask(NewRunnableFunction(SetThreadPriority));
+  CompositorLoop()->PostTask(NewRunnableFunction("SetThreadPriorityRunnable",
+                                                 SetThreadPriority));
 
 
   { // scope lock
     MonitorAutoLock lock(*sIndirectLayerTreesLock);
     sIndirectLayerTrees[mRootLayerTreeID].mParent = this;
   }
 
   LayerScope::SetPixelScale(mScale.scale);
@@ -1783,17 +1785,18 @@ CompositorBridgeParent::DeallocateLayerT
   MOZ_ASSERT(NS_IsMainThread());
   // Here main thread notifies compositor to remove an element from
   // sIndirectLayerTrees. This removed element might be queried soon.
   // Checking the elements of sIndirectLayerTrees exist or not before using.
   if (!CompositorLoop()) {
     gfxCriticalError() << "Attempting to post to a invalid Compositor Loop";
     return;
   }
-  CompositorLoop()->PostTask(NewRunnableFunction(&EraseLayerState, aId));
+  CompositorLoop()->PostTask(NewRunnableFunction("EraseLayerStateRunnable",
+                                                 &EraseLayerState, aId));
 }
 
 static void
 UpdateControllerForLayersId(uint64_t aLayersId,
                             GeckoContentController* aController)
 {
   // Adopt ref given to us by SetControllerForLayerTree()
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
@@ -1820,17 +1823,18 @@ ScopedLayerTreeRegistration::~ScopedLaye
 }
 
 /*static*/ void
 CompositorBridgeParent::SetControllerForLayerTree(uint64_t aLayersId,
                                                   GeckoContentController* aController)
 {
   // This ref is adopted by UpdateControllerForLayersId().
   aController->AddRef();
-  CompositorLoop()->PostTask(NewRunnableFunction(&UpdateControllerForLayersId,
+  CompositorLoop()->PostTask(NewRunnableFunction("UpdateControllerForLayersIdRunnable",
+                                                 &UpdateControllerForLayersId,
                                                  aLayersId,
                                                  aController));
 }
 
 /*static*/ already_AddRefed<APZCTreeManager>
 CompositorBridgeParent::GetAPZCTreeManager(uint64_t aLayersId)
 {
   EnsureLayerTreeMapReady();
@@ -1860,17 +1864,18 @@ InsertVsyncProfilerMarker(TimeStamp aVsy
 
 /*static */ void
 CompositorBridgeParent::PostInsertVsyncProfilerMarker(TimeStamp aVsyncTimestamp)
 {
 #if defined(MOZ_GECKO_PROFILER)
   // Called in the vsync thread
   if (profiler_is_active() && CompositorThreadHolder::IsActive()) {
     CompositorLoop()->PostTask(
-      NewRunnableFunction(InsertVsyncProfilerMarker, aVsyncTimestamp));
+      NewRunnableFunction("InsertVsyncProfilerMarkerRunnable", InsertVsyncProfilerMarker,
+                          aVsyncTimestamp));
   }
 #endif
 }
 
 widget::PCompositorWidgetParent*
 CompositorBridgeParent::AllocPCompositorWidgetParent(const CompositorWidgetInitData& aInitData)
 {
 #if defined(MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING)
--- a/gfx/tests/mochitest/test_acceleration.html
+++ b/gfx/tests/mochitest/test_acceleration.html
@@ -30,24 +30,28 @@ function runTest() {
 
   var sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
   var xr = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
 
   var windows = SpecialPowers.Services.ww.getWindowEnumerator();
   var windowutils;
   var acceleratedWindows = 0;
   var advancedLayersWindows = 0;
+  var webrenderWindows = 0;
   var layerManagerLog = [];
   while (windows.hasMoreElements()) {
     windowutils = windows.getNext().QueryInterface(Ci.nsIInterfaceRequestor)
                                    .getInterface(Ci.nsIDOMWindowUtils);
     try {
       if (windowutils.layerManagerType != "Basic") {
         acceleratedWindows++;
       }
+      if (windowutils.layerManagerType == "WebRender") {
+        webrenderWindows++;
+      }
       if (windowutils.usingAdvancedLayers) {
         advancedLayersWindows++;
       }
       layerManagerLog.push(windowutils.layerManagerType + ":" +
                            windowutils.usingAdvancedLayers);
     } catch (e) {
       // The window may not have a layer manager, in which case we get an error.
       // Don't count it as an accelerated window.
@@ -103,32 +107,47 @@ function runTest() {
         } else {
           ok(gfxInfo.DWriteEnabled, "DirectWrite enabled on Windows 7 or newer");
         }
       } else {
         ok(gfxInfo.D2DEnabled, "Direct2D enabled on Windows 8 or newer");
         ok(gfxInfo.DWriteEnabled, "DirectWrite enabled on Windows 7 or newer");
       }
 
+      var shouldGetWR = false;
+      try {
+        shouldGetWR = SpecialPowers.DOMWindowUtils.isWebRenderRequested;
+      } catch (e) {}
+
       var advancedLayersEnabled = false;
       var advancedLayersEnabledOnWin7 = false;
       try {
         advancedLayersEnabled = SpecialPowers.getBoolPref("layers.mlgpu.enabled");
         advancedLayersEnabledOnWin7 = SpecialPowers.getBoolPref("layers.mlgpu.enable-on-windows7");
       } catch (e) {}
       var shouldGetAL = advancedLayersEnabled;
       if (version < 6.2) {
         shouldGetAL &= advancedLayersEnabledOnWin7;
       }
+      if (shouldGetWR) {
+        shouldGetAL = false;
+      }
+
       if (shouldGetAL) {
         isnot(advancedLayersWindows, 0, "Advanced Layers enabled on Windows; "
                                         + layerManagerLog.join(","));
       } else {
         is(advancedLayersWindows, 0, "Advanced Layers disabled on Windows");
       }
+
+      if (shouldGetWR) {
+        isnot(webrenderWindows, 0, "WebRender enabled on Windows");
+      } else {
+        is(webrenderWindows, 0, "WebRender disabled on Windows");
+      }
       break;
 
     case "Linux":
       todo(false, "Acceleration supported on Linux, but only on taskcluster instances (bug 1296086)");
       break;
 
     default:
       if (xr.OS == "Android") {
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2455,20 +2455,33 @@ gfxPlatform::InitCompositorAccelerationP
                          NS_LITERAL_CSTRING("FEATURE_FAILURE_COMP_SAFEMODE"));
   }
   if (IsHeadless()) {
     feature.ForceDisable(FeatureStatus::Blocked, "Acceleration blocked by headless mode",
                          NS_LITERAL_CSTRING("FEATURE_FAILURE_COMP_HEADLESSMODE"));
   }
 }
 
+/*static*/ bool
+gfxPlatform::WebRenderPrefEnabled()
+{
+  return Preferences::GetBool("gfx.webrender.enabled", false);
+}
+
+/*static*/ bool
+gfxPlatform::WebRenderEnvvarEnabled()
+{
+  const char* env = PR_GetEnv("MOZ_WEBRENDER");
+  return (env && *env == '1');
+}
+
 void
 gfxPlatform::InitWebRenderConfig()
 {
-  bool prefEnabled = Preferences::GetBool("gfx.webrender.enabled", false);
+  bool prefEnabled = WebRenderPrefEnabled();
 
   ScopedGfxFeatureReporter reporter("WR", prefEnabled);
   if (!XRE_IsParentProcess()) {
     // The parent process runs through all the real decision-making code
     // later in this function. For other processes we still want to report
     // the state of the feature for crash reports.
     if (gfxVars::UseWebRender()) {
       reporter.SetSuccessful();
@@ -2480,21 +2493,18 @@ gfxPlatform::InitWebRenderConfig()
 
   featureWebRender.DisableByDefault(
       FeatureStatus::OptIn,
       "WebRender is an opt-in feature",
       NS_LITERAL_CSTRING("FEATURE_FAILURE_DEFAULT_OFF"));
 
   if (prefEnabled) {
     featureWebRender.UserEnable("Enabled by pref");
-  } else {
-    const char* env = PR_GetEnv("MOZ_WEBRENDER");
-    if (env && *env == '1') {
-      featureWebRender.UserEnable("Enabled by envvar");
-    }
+  } else if (WebRenderEnvvarEnabled()) {
+    featureWebRender.UserEnable("Enabled by envvar");
   }
 
   // HW_COMPOSITING being disabled implies interfacing with the GPU might break
   if (!gfxConfig::IsEnabled(Feature::HW_COMPOSITING)) {
     featureWebRender.ForceDisable(
       FeatureStatus::Unavailable,
       "Hardware compositing is disabled",
       NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBRENDER_NEED_HWCOMP"));
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -705,16 +705,21 @@ public:
      * GPUProcessManager, in the UI process.
      */
     virtual void ImportGPUDeviceData(const mozilla::gfx::GPUDeviceData& aData);
 
     virtual FT_Library GetFTLibrary() {
       return nullptr;
     }
 
+    // you probably want to use gfxVars::UseWebRender() instead of this
+    static bool WebRenderPrefEnabled();
+    // you probably want to use gfxVars::UseWebRender() instead of this
+    static bool WebRenderEnvvarEnabled();
+
 protected:
     gfxPlatform();
     virtual ~gfxPlatform();
 
     virtual void InitAcceleration();
 
     /**
      * Called immediately before deleting the gfxPlatform object.
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -674,16 +674,20 @@ public:
     void GetFamilyNameAndURIForLogging(nsACString& aFamilyName,
                                        nsACString& aURI);
 
     gfxFontEntry* Clone() const override {
         MOZ_ASSERT_UNREACHABLE("cannot Clone user fonts");
         return nullptr;
     }
 
+#ifdef DEBUG
+    gfxUserFontSet* GetUserFontSet() const { return mFontSet; }
+#endif
+
 protected:
     const uint8_t* SanitizeOpenTypeData(const uint8_t* aData,
                                         uint32_t aLength,
                                         uint32_t& aSaneLength,
                                         gfxUserFontType aFontType);
 
     // attempt to load the next resource in the src list.
     void LoadNextSrc();
--- a/gfx/vr/ipc/VRManagerChild.cpp
+++ b/gfx/vr/ipc/VRManagerChild.cpp
@@ -151,17 +151,18 @@ void
 VRManagerChild::Destroy()
 {
   // Keep ourselves alive until everything has been shut down
   RefPtr<VRManagerChild> selfRef = this;
 
   // The DeferredDestroyVRManager task takes ownership of
   // the VRManagerChild and will release it when it runs.
   MessageLoop::current()->PostTask(
-             NewRunnableFunction(DeferredDestroy, selfRef));
+             NewRunnableFunction("VRManagerChildDestroyRunnable",
+                                 DeferredDestroy, selfRef));
 }
 
 PVRLayerChild*
 VRManagerChild::AllocPVRLayerChild(const uint32_t& aDisplayID,
                                    const uint32_t& aGroup)
 {
   return VRLayerChild::CreateIPDLActor();
 }
--- a/gfx/vr/ipc/VRManagerParent.cpp
+++ b/gfx/vr/ipc/VRManagerParent.cpp
@@ -116,17 +116,18 @@ VRManagerParent::RegisterVRManagerInVRLi
 
 /*static*/ VRManagerParent*
 VRManagerParent::CreateSameProcess()
 {
   MessageLoop* loop = VRListenerThreadHolder::Loop();
   RefPtr<VRManagerParent> vmp = new VRManagerParent(base::GetCurrentProcId(), false);
   vmp->mVRListenerThreadHolder = VRListenerThreadHolder::GetSingleton();
   vmp->mSelfRef = vmp;
-  loop->PostTask(NewRunnableFunction(RegisterVRManagerInVRListenerThread, vmp.get()));
+  loop->PostTask(NewRunnableFunction("RegisterVRManagerInVRListenerThreadRunnable",
+                                     RegisterVRManagerInVRListenerThread, vmp.get()));
   return vmp.get();
 }
 
 bool
 VRManagerParent::CreateForGPUProcess(Endpoint<PVRManagerParent>&& aEndpoint)
 {
   MessageLoop* loop = VRListenerThreadHolder::Loop();
 
--- a/gfx/webrender_bindings/RenderThread.cpp
+++ b/gfx/webrender_bindings/RenderThread.cpp
@@ -229,16 +229,17 @@ RenderThread::UpdateAndRender(wr::Window
     // Render did not happen, do not call NotifyDidRender.
     return;
   }
 
   TimeStamp end = TimeStamp::Now();
 
   auto epochs = renderer->FlushRenderedEpochs();
   layers::CompositorThreadHolder::Loop()->PostTask(NewRunnableFunction(
+    "NotifyDidRenderRunnable",
     &NotifyDidRender,
     renderer->GetCompositorBridge(),
     epochs,
     start, end
   ));
 }
 
 void
--- a/gfx/webrender_bindings/RendererOGL.cpp
+++ b/gfx/webrender_bindings/RendererOGL.cpp
@@ -222,16 +222,17 @@ DoNotifyWebRenderError(layers::Composito
 {
   aBridge->NotifyWebRenderError(aError);
 }
 
 void
 RendererOGL::NotifyWebRenderError(WebRenderError aError)
 {
   layers::CompositorThreadHolder::Loop()->PostTask(NewRunnableFunction(
+    "DoNotifyWebRenderErrorRunnable",
     &DoNotifyWebRenderError,
     mBridge,
     aError
   ));
 }
 
 } // namespace wr
 } // namespace mozilla
--- a/gfx/webrender_bindings/WebRenderTypes.h
+++ b/gfx/webrender_bindings/WebRenderTypes.h
@@ -485,26 +485,26 @@ static inline wr::SideOffsets2D_f32 ToSi
   SideOffsets2D_f32 offset;
   offset.top = top;
   offset.right = right;
   offset.bottom = bottom;
   offset.left = left;
   return offset;
 }
 
-static inline wr::RepeatMode ToRepeatMode(uint8_t repeatMode)
+static inline wr::RepeatMode ToRepeatMode(mozilla::StyleBorderImageRepeat repeatMode)
 {
   switch (repeatMode) {
-  case NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH:
+  case mozilla::StyleBorderImageRepeat::Stretch:
     return wr::RepeatMode::Stretch;
-  case NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT:
+  case mozilla::StyleBorderImageRepeat::Repeat:
     return wr::RepeatMode::Repeat;
-  case NS_STYLE_BORDER_IMAGE_REPEAT_ROUND:
+  case mozilla::StyleBorderImageRepeat::Round:
     return wr::RepeatMode::Round;
-  case NS_STYLE_BORDER_IMAGE_REPEAT_SPACE:
+  case mozilla::StyleBorderImageRepeat::Space:
     return wr::RepeatMode::Space;
   default:
     MOZ_ASSERT(false);
   }
 
   return wr::RepeatMode::Stretch;
 }
 
--- a/image/ClippedImage.cpp
+++ b/image/ClippedImage.cpp
@@ -454,17 +454,17 @@ ClippedImage::DrawSingleTile(gfxContext*
                              SamplingFilter aSamplingFilter,
                              const Maybe<SVGImageContext>& aSVGContext,
                              uint32_t aFlags,
                              float aOpacity)
 {
   MOZ_ASSERT(!MustCreateSurface(aContext, aSize, aRegion, aFlags),
              "Shouldn't need to create a surface");
 
-  gfxRect clip(mClip.x, mClip.y, mClip.Width(), mClip.Height());
+  gfxRect clip(mClip.X(), mClip.Y(), mClip.Width(), mClip.Height());
   nsIntSize size(aSize), innerSize(aSize);
   bool needScale = false;
   if (mSVGViewportSize && !mSVGViewportSize->IsEmpty()) {
     innerSize = *mSVGViewportSize;
     needScale = true;
   } else if (NS_SUCCEEDED(InnerImage()->GetWidth(&innerSize.width)) &&
              NS_SUCCEEDED(InnerImage()->GetHeight(&innerSize.height))) {
     needScale = true;
@@ -481,21 +481,21 @@ ClippedImage::DrawSingleTile(gfxContext*
     clip.Scale(scaleX, scaleY);
     size = innerSize;
     size.Scale(scaleX, scaleY);
   }
 
   // We restrict our drawing to only the clipping region, and translate so that
   // the clipping region is placed at the position the caller expects.
   ImageRegion region(aRegion);
-  region.MoveBy(clip.x, clip.y);
+  region.MoveBy(clip.X(), clip.Y());
   region = region.Intersect(clip);
 
   gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
-  aContext->Multiply(gfxMatrix::Translation(-clip.x, -clip.y));
+  aContext->Multiply(gfxMatrix::Translation(-clip.X(), -clip.Y()));
 
   auto unclipViewport = [&](const SVGImageContext& aOldContext) {
     // Map the viewport to the inner image. Note that we don't take the aSize
     // parameter of imgIContainer::Draw into account, just the clipping region.
     // The size in pixels at which the output will ultimately be drawn is
     // irrelevant here since the purpose of the SVG viewport size is to
     // determine what *region* of the SVG document will be drawn.
     SVGImageContext context(aOldContext);
@@ -596,14 +596,14 @@ NS_IMETHODIMP_(nsIntRect)
 ClippedImage::GetImageSpaceInvalidationRect(const nsIntRect& aRect)
 {
   if (!ShouldClip()) {
     return InnerImage()->GetImageSpaceInvalidationRect(aRect);
   }
 
   nsIntRect rect(InnerImage()->GetImageSpaceInvalidationRect(aRect));
   rect = rect.Intersect(mClip);
-  rect.MoveBy(-mClip.x, -mClip.y);
+  rect.MoveBy(-mClip.X(), -mClip.Y());
   return rect;
 }
 
 } // namespace image
 } // namespace mozilla
--- a/image/Downscaler.cpp
+++ b/image/Downscaler.cpp
@@ -73,17 +73,17 @@ Downscaler::BeginFrame(const nsIntSize& 
   // Only downscale from reasonable sizes to avoid using too much memory/cpu
   // downscaling and decoding. 1 << 20 == 1,048,576 seems a reasonable limit.
   if (aOriginalSize.width > (1 << 20) || aOriginalSize.height > (1 << 20)) {
     NS_WARNING("Trying to downscale image frame that is too large");
     return NS_ERROR_INVALID_ARG;
   }
 
   mFrameRect = aFrameRect.valueOr(nsIntRect(nsIntPoint(), aOriginalSize));
-  MOZ_ASSERT(mFrameRect.x >= 0 && mFrameRect.y >= 0 &&
+  MOZ_ASSERT(mFrameRect.X() >= 0 && mFrameRect.Y() >= 0 &&
              mFrameRect.Width() >= 0 && mFrameRect.Height() >= 0,
              "Frame rect must have non-negative components");
   MOZ_ASSERT(nsIntRect(0, 0, aOriginalSize.width, aOriginalSize.height)
                .Contains(mFrameRect),
              "Frame rect must fit inside image");
   MOZ_ASSERT_IF(!nsIntRect(0, 0, aOriginalSize.width, aOriginalSize.height)
                   .IsEqualEdges(mFrameRect),
                 aHasAlpha);
@@ -163,17 +163,17 @@ Downscaler::ResetForNextProgressivePass(
   mCurrentInLine = 0;
   mLinesInBuffer = 0;
 
   if (mFrameRect.IsEmpty()) {
     // Our frame rect is zero size; commit rows until the end of the image.
     SkipToRow(mOriginalSize.height - 1);
   } else {
     // If we have a vertical offset, commit rows to shift us past it.
-    SkipToRow(mFrameRect.y);
+    SkipToRow(mFrameRect.Y());
   }
 }
 
 void
 Downscaler::ClearRestOfRow(uint32_t aStartingAtCol)
 {
   MOZ_ASSERT(int64_t(aStartingAtCol) <= int64_t(mOriginalSize.width));
   uint32_t bytesToClear = (mOriginalSize.width - aStartingAtCol)
@@ -214,17 +214,17 @@ Downscaler::CommitRow()
                                         &filterOffset, &filterLength);
     }
   }
 
   mCurrentInLine += 1;
 
   // If we're at the end of the part of the original image that has data, commit
   // rows to shift us to the end.
-  if (mCurrentInLine == (mFrameRect.y + mFrameRect.Height())) {
+  if (mCurrentInLine == (mFrameRect.Y() + mFrameRect.Height())) {
     SkipToRow(mOriginalSize.height - 1);
   }
 }
 
 bool
 Downscaler::HasInvalidation() const
 {
   return mCurrentOutLine > mPrevInvalidatedLine;
--- a/image/Downscaler.h
+++ b/image/Downscaler.h
@@ -84,17 +84,17 @@ public:
                       bool aHasAlpha,
                       bool aFlipVertically = false);
 
   bool IsFrameComplete() const { return mCurrentInLine >= mOriginalSize.height; }
 
   /// Retrieves the buffer into which the Decoder should write each row.
   uint8_t* RowBuffer()
   {
-    return mRowBuffer.get() + mFrameRect.x * sizeof(uint32_t);
+    return mRowBuffer.get() + mFrameRect.X() * sizeof(uint32_t);
   }
 
   /// Clears the current row buffer.
   void ClearRow() { ClearRestOfRow(0); }
 
   /// Clears the current row buffer starting at @aStartingAtCol.
   void ClearRestOfRow(uint32_t aStartingAtCol);
 
--- a/image/FrameAnimator.cpp
+++ b/image/FrameAnimator.cpp
@@ -597,36 +597,32 @@ FrameAnimator::DoBlend(DrawableSurface& 
       !mCompositingPrevFrame) {
     prevFrameData.mDisposalMethod = DisposalMethod::CLEAR;
   }
 
   IntRect prevRect = prevFrameData.mBlendRect
                    ? prevFrameData.mRect.Intersect(*prevFrameData.mBlendRect)
                    : prevFrameData.mRect;
 
-  bool isFullPrevFrame = prevRect.x == 0 && prevRect.y == 0 &&
-                         prevRect.Width() == mSize.width &&
-                         prevRect.Height() == mSize.height;
+  bool isFullPrevFrame = prevRect.IsEqualRect(0, 0, mSize.width, mSize.height);
 
   // Optimization: DisposeClearAll if the previous frame is the same size as
   //               container and it's clearing itself
   if (isFullPrevFrame &&
       (prevFrameData.mDisposalMethod == DisposalMethod::CLEAR)) {
     prevFrameData.mDisposalMethod = DisposalMethod::CLEAR_ALL;
   }
 
   AnimationData nextFrameData = nextFrame->GetAnimationData();
 
   IntRect nextRect = nextFrameData.mBlendRect
                    ? nextFrameData.mRect.Intersect(*nextFrameData.mBlendRect)
                    : nextFrameData.mRect;
 
-  bool isFullNextFrame = nextRect.x == 0 && nextRect.y == 0 &&
-                         nextRect.Width() == mSize.width &&
-                         nextRect.Height() == mSize.height;
+  bool isFullNextFrame = nextRect.IsEqualRect(0, 0, mSize.width, mSize.height);
 
   if (!nextFrame->GetIsPaletted()) {
     // Optimization: Skip compositing if the previous frame wants to clear the
     //               whole image
     if (prevFrameData.mDisposalMethod == DisposalMethod::CLEAR_ALL) {
       aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
       return true;
     }
@@ -714,17 +710,17 @@ FrameAnimator::DoBlend(DrawableSurface& 
       nextFrameData.mDisposalMethod != DisposalMethod::RESTORE_PREVIOUS) {
     if (isFullNextFrame) {
       // Optimization: No need to dispose prev.frame when
       // next frame is full frame and not transparent.
       doDisposal = false;
       // No need to blank the composite frame
       needToBlankComposite = false;
     } else {
-      if ((prevRect.x >= nextRect.x) && (prevRect.y >= nextRect.y) &&
+      if ((prevRect.X() >= nextRect.X()) && (prevRect.Y() >= nextRect.Y()) &&
           (prevRect.XMost() <= nextRect.XMost()) &&
           (prevRect.YMost() <= nextRect.YMost())) {
         // Optimization: No need to dispose prev.frame when
         // next frame fully overlaps previous frame.
         doDisposal = false;
       }
     }
   }
@@ -888,18 +884,18 @@ FrameAnimator::ClearFrame(uint8_t* aFram
   }
 
   IntRect toClear = aFrameRect.Intersect(aRectToClear);
   if (toClear.IsEmpty()) {
     return;
   }
 
   uint32_t bytesPerRow = aFrameRect.Width() * 4;
-  for (int row = toClear.y; row < toClear.YMost(); ++row) {
-    memset(aFrameData + toClear.x * 4 + row * bytesPerRow, 0,
+  for (int row = toClear.Y(); row < toClear.YMost(); ++row) {
+    memset(aFrameData + toClear.X() * 4 + row * bytesPerRow, 0,
            toClear.Width() * 4);
   }
 }
 
 //******************************************************************************
 // Whether we succeed or fail will not cause a crash, and there's not much
 // we can do about a failure, so there we don't return a nsresult
 bool
@@ -925,47 +921,48 @@ FrameAnimator::DrawFrameTo(const uint8_t
                            uint32_t aSrcPaletteLength, bool aSrcHasAlpha,
                            uint8_t* aDstPixels, const IntRect& aDstRect,
                            BlendMethod aBlendMethod, const Maybe<IntRect>& aBlendRect)
 {
   NS_ENSURE_ARG_POINTER(aSrcData);
   NS_ENSURE_ARG_POINTER(aDstPixels);
 
   // According to both AGIF and APNG specs, offsets are unsigned
-  if (aSrcRect.x < 0 || aSrcRect.y < 0) {
+  if (aSrcRect.X() < 0 || aSrcRect.Y() < 0) {
     NS_WARNING("FrameAnimator::DrawFrameTo: negative offsets not allowed");
     return NS_ERROR_FAILURE;
   }
+
   // Outside the destination frame, skip it
-  if ((aSrcRect.x > aDstRect.Width()) || (aSrcRect.y > aDstRect.Height())) {
+  if ((aSrcRect.X() > aDstRect.Width()) || (aSrcRect.Y() > aDstRect.Height())) {
     return NS_OK;
   }
 
   if (aSrcPaletteLength) {
     // Larger than the destination frame, clip it
-    int32_t width = std::min(aSrcRect.Width(), aDstRect.Width() - aSrcRect.x);
-    int32_t height = std::min(aSrcRect.Height(), aDstRect.Height() - aSrcRect.y);
+    int32_t width = std::min(aSrcRect.Width(), aDstRect.Width() - aSrcRect.X());
+    int32_t height = std::min(aSrcRect.Height(), aDstRect.Height() - aSrcRect.Y());
 
     // The clipped image must now fully fit within destination image frame
-    NS_ASSERTION((aSrcRect.x >= 0) && (aSrcRect.y >= 0) &&
-                 (aSrcRect.x + width <= aDstRect.Width()) &&
-                 (aSrcRect.y + height <= aDstRect.Height()),
+    NS_ASSERTION((aSrcRect.X() >= 0) && (aSrcRect.Y() >= 0) &&
+                 (aSrcRect.X() + width <= aDstRect.Width()) &&
+                 (aSrcRect.Y() + height <= aDstRect.Height()),
                 "FrameAnimator::DrawFrameTo: Invalid aSrcRect");
 
     // clipped image size may be smaller than source, but not larger
     NS_ASSERTION((width <= aSrcRect.Width()) && (height <= aSrcRect.Height()),
       "FrameAnimator::DrawFrameTo: source must be smaller than dest");
 
     // Get pointers to image data
     const uint8_t* srcPixels = aSrcData + aSrcPaletteLength;
     uint32_t* dstPixels = reinterpret_cast<uint32_t*>(aDstPixels);
     const uint32_t* colormap = reinterpret_cast<const uint32_t*>(aSrcData);
 
     // Skip to the right offset
-    dstPixels += aSrcRect.x + (aSrcRect.y * aDstRect.Width());
+    dstPixels += aSrcRect.X() + (aSrcRect.Y() * aDstRect.Width());
     if (!aSrcHasAlpha) {
       for (int32_t r = height; r > 0; --r) {
         for (int32_t c = 0; c < width; c++) {
           dstPixels[c] = colormap[srcPixels[c]];
         }
         // Go to the next row in the source resp. destination image
         srcPixels += aSrcRect.Width();
         dstPixels += aDstRect.Width();
@@ -1025,35 +1022,35 @@ FrameAnimator::DrawFrameTo(const uint8_t
       // We don't need to do anything clever. (Or, in the case where no blend
       // rect was specified, we can't.)
       pixman_image_composite32(op,
                                src,
                                nullptr,
                                dst,
                                0, 0,
                                0, 0,
-                               aSrcRect.x, aSrcRect.y,
+                               aSrcRect.X(), aSrcRect.Y(),
                                aSrcRect.Width(), aSrcRect.Height());
     } else {
       // We need to do the OVER followed by SOURCE trick above.
       pixman_image_composite32(PIXMAN_OP_OVER,
                                src,
                                nullptr,
                                dst,
                                0, 0,
                                0, 0,
-                               aSrcRect.x, aSrcRect.y,
+                               aSrcRect.X(), aSrcRect.Y(),
                                aSrcRect.Width(), aSrcRect.Height());
       pixman_image_composite32(PIXMAN_OP_SRC,
                                src,
                                nullptr,
                                dst,
-                               aBlendRect->x, aBlendRect->y,
+                               aBlendRect->X(), aBlendRect->Y(),
                                0, 0,
-                               aBlendRect->x, aBlendRect->y,
+                               aBlendRect->X(), aBlendRect->Y(),
                                aBlendRect->Width(), aBlendRect->Height());
     }
 
     pixman_image_unref(src);
     pixman_image_unref(dst);
   }
 
   return NS_OK;
--- a/image/OrientedImage.cpp
+++ b/image/OrientedImage.cpp
@@ -390,17 +390,17 @@ OrientedImage::GetImageSpaceInvalidation
   rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&innerSize.height);
   if (NS_FAILED(rv)) {
     // Fall back to identity if the width and height aren't available.
     return rect;
   }
 
   // Transform the invalidation rect into the correct orientation.
   gfxMatrix matrix(OrientationMatrix(innerSize));
-  gfxRect invalidRect(matrix.TransformBounds(gfxRect(rect.x, rect.y,
+  gfxRect invalidRect(matrix.TransformBounds(gfxRect(rect.X(), rect.Y(),
                                                      rect.Width(), rect.Height())));
 
-  return IntRect::RoundOut(invalidRect.x, invalidRect.y,
+  return IntRect::RoundOut(invalidRect.X(), invalidRect.Y(),
                            invalidRect.Width(), invalidRect.Height());
 }
 
 } // namespace image
 } // namespace mozilla
--- a/image/ProgressTracker.cpp
+++ b/image/ProgressTracker.cpp
@@ -396,20 +396,23 @@ ProgressTracker::SyncNotify(IProgressObs
   if (image && image->GetURI()) {
     image->GetURI()->GetSpec(spec);
   }
   LOG_SCOPE_WITH_PARAM(gImgLog,
                        "ProgressTracker::SyncNotify", "uri", spec.get());
 
   nsIntRect rect;
   if (image) {
-    if (NS_FAILED(image->GetWidth(&rect.width)) ||
-        NS_FAILED(image->GetHeight(&rect.height))) {
+    int32_t width, height;
+    if (NS_FAILED(image->GetWidth(&width)) ||
+        NS_FAILED(image->GetHeight(&height))) {
       // Either the image has no intrinsic size, or it has an error.
       rect = GetMaxSizedIntRect();
+    } else {
+      rect.SizeTo(width, height);
     }
   }
 
   SyncNotifyInternal(aObserver, !!image, mProgress, rect);
 }
 
 void
 ProgressTracker::EmulateRequestFinished(IProgressObserver* aObserver)
--- a/image/SurfaceFilters.h
+++ b/image/SurfaceFilters.h
@@ -418,22 +418,22 @@ protected:
   uint8_t* DoResetToFirstRow() override
   {
     uint8_t* rowPtr = mNext.ResetToFirstRow();
     if (rowPtr == nullptr) {
       mRow = mFrameRect.YMost();
       return nullptr;
     }
 
-    mRow = mUnclampedFrameRect.y;
+    mRow = mUnclampedFrameRect.Y();
 
     // Advance the next pipeline stage to the beginning of the frame rect,
     // outputting blank rows.
-    if (mFrameRect.y > 0) {
-      for (int32_t rowToOutput = 0; rowToOutput < mFrameRect.y ; ++rowToOutput) {
+    if (mFrameRect.Y() > 0) {
+      for (int32_t rowToOutput = 0; rowToOutput < mFrameRect.Y() ; ++rowToOutput) {
         mNext.WriteEmptyRow();
       }
     }
 
     // We're at the beginning of the frame rect now, so return if we're either
     // ready for input or we're already done.
     rowPtr = mBuffer ? mBuffer.get() : mNext.CurrentRowPointer();
     if (!mFrameRect.IsEmpty() || rowPtr == nullptr) {
@@ -454,38 +454,38 @@ protected:
 
   uint8_t* DoAdvanceRow() override
   {
     uint8_t* rowPtr = nullptr;
 
     const int32_t currentRow = mRow;
     mRow++;
 
-    if (currentRow < mFrameRect.y) {
+    if (currentRow < mFrameRect.Y()) {
       // This row is outside of the frame rect, so just drop it on the floor.
       rowPtr = mBuffer ? mBuffer.get() : mNext.CurrentRowPointer();
       return AdjustRowPointer(rowPtr);
     } else if (currentRow >= mFrameRect.YMost()) {
       NS_WARNING("RemoveFrameRectFilter: Advancing past end of frame rect");
       return nullptr;
     }
 
     // If we had to buffer, copy the data. Otherwise, just advance the row.
     if (mBuffer) {
       // We write from the beginning of the buffer unless |mUnclampedFrameRect.x|
       // is negative; if that's the case, we have to skip the portion of the
       // unclamped frame rect that's outside the row.
       uint32_t* source = reinterpret_cast<uint32_t*>(mBuffer.get()) -
-                         std::min(mUnclampedFrameRect.x, 0);
+                         std::min(mUnclampedFrameRect.X(), 0);
 
       // We write |mFrameRect.width| columns starting at |mFrameRect.x|; we've
       // already clamped these values to the size of the output, so we don't
       // have to worry about bounds checking here (though WriteBuffer() will do
       // it for us in any case).
-      WriteState state = mNext.WriteBuffer(source, mFrameRect.x, mFrameRect.Width());
+      WriteState state = mNext.WriteBuffer(source, mFrameRect.X(), mFrameRect.Width());
 
       rowPtr = state == WriteState::NEED_MORE_DATA ? mBuffer.get()
                                                    : nullptr;
     } else {
       rowPtr = mNext.AdvanceRow();
     }
 
     // If there's still more data coming or we're already done, just adjust the
@@ -511,17 +511,17 @@ private:
     }
 
     if (mFrameRect.IsEmpty() ||
         mRow >= mFrameRect.YMost() ||
         aNextRowPointer == nullptr) {
       return nullptr;  // Nothing left to write.
     }
 
-    return aNextRowPointer + mFrameRect.x * sizeof(uint32_t);
+    return aNextRowPointer + mFrameRect.X() * sizeof(uint32_t);
   }
 
   Next mNext;                        /// The next SurfaceFilter in the chain.
 
   gfx::IntRect mFrameRect;           /// The surface subrect which contains data,
                                      /// clamped to the image size.
   gfx::IntRect mUnclampedFrameRect;  /// The frame rect before clamping.
   UniquePtr<uint8_t[]> mBuffer;      /// The intermediate buffer, if one is
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -792,20 +792,20 @@ nsGIFDecoder2::ReadImageDescriptor(const
 }
 
 LexerTransition<nsGIFDecoder2::State>
 nsGIFDecoder2::FinishImageDescriptor(const char* aData)
 {
   IntRect frameRect;
 
   // Get image offsets with respect to the screen origin.
-  frameRect.x = LittleEndian::readUint16(aData + 0);
-  frameRect.y = LittleEndian::readUint16(aData + 2);
-  frameRect.SetWidth(LittleEndian::readUint16(aData + 4));
-  frameRect.SetHeight(LittleEndian::readUint16(aData + 6));
+  frameRect.SetRect(LittleEndian::readUint16(aData + 0),
+                    LittleEndian::readUint16(aData + 2),
+                    LittleEndian::readUint16(aData + 4),
+                    LittleEndian::readUint16(aData + 6));
 
   if (!mGIFStruct.images_decoded) {
     // Work around GIF files where
     //   * at least one of the logical screen dimensions is smaller than the
     //     same dimension in the first image, or
     //   * GIF87a files where the first image's dimensions do not match the
     //     logical screen dimensions.
     if (mGIFStruct.screen_height < frameRect.Height() ||
--- a/image/imgFrame.cpp
+++ b/image/imgFrame.cpp
@@ -502,33 +502,33 @@ imgFrame::SurfaceForDrawing(bool        
   MOZ_ASSERT(NS_IsMainThread());
   mMonitor.AssertCurrentThreadOwns();
 
   if (!aDoPartialDecode) {
     return SurfaceWithFormat(new gfxSurfaceDrawable(aSurface, mImageSize),
                              mFormat);
   }
 
-  gfxRect available = gfxRect(mDecoded.x, mDecoded.y, mDecoded.Width(),
+  gfxRect available = gfxRect(mDecoded.X(), mDecoded.Y(), mDecoded.Width(),
                               mDecoded.Height());
 
   if (aDoTile) {
     // Create a temporary surface.
     // Give this surface an alpha channel because there are
     // transparent pixels in the padding or undecoded area
     RefPtr<DrawTarget> target =
       gfxPlatform::GetPlatform()->
         CreateOffscreenContentDrawTarget(mImageSize, SurfaceFormat::B8G8R8A8);
     if (!target) {
       return SurfaceWithFormat();
     }
 
     SurfacePattern pattern(aSurface,
                            aRegion.GetExtendMode(),
-                           Matrix::Translation(mDecoded.x, mDecoded.y));
+                           Matrix::Translation(mDecoded.X(), mDecoded.Y()));
     target->FillRect(ToRect(aRegion.Intersect(available).Rect()), pattern);
 
     RefPtr<SourceSurface> newsurf = target->Snapshot();
     return SurfaceWithFormat(new gfxSurfaceDrawable(newsurf, mImageSize),
                              target->GetFormat());
   }
 
   // Not tiling, and we have a surface, so we can account for
--- a/image/test/gtest/Common.cpp
+++ b/image/test/gtest/Common.cpp
@@ -165,17 +165,17 @@ RowsAreSolidColor(SourceSurface* aSurfac
 bool
 PalettedRowsAreSolidColor(Decoder* aDecoder,
                           int32_t aStartRow,
                           int32_t aRowCount,
                           uint8_t aColor)
 {
   RawAccessFrameRef currentFrame = aDecoder->GetCurrentFrameRef();
   IntRect frameRect = currentFrame->GetRect();
-  IntRect solidColorRect(frameRect.x, aStartRow, frameRect.Width(), aRowCount);
+  IntRect solidColorRect(frameRect.X(), aStartRow, frameRect.Width(), aRowCount);
   return PalettedRectIsSolidColor(aDecoder, solidColorRect, aColor);
 }
 
 bool
 RectIsSolidColor(SourceSurface* aSurface,
                  const IntRect& aRect,
                  BGRAColor aColor,
                  uint8_t aFuzz /* = 0 */)
@@ -191,18 +191,18 @@ RectIsSolidColor(SourceSurface* aSurface
                                        DataSourceSurface::MapType::READ);
   ASSERT_TRUE_OR_RETURN(mapping.IsMapped(), false);
   ASSERT_EQ_OR_RETURN(mapping.GetStride(), surfaceSize.width * 4, false);
 
   uint8_t* data = mapping.GetData();
   ASSERT_TRUE_OR_RETURN(data != nullptr, false);
 
   int32_t rowLength = mapping.GetStride();
-  for (int32_t row = rect.y; row < rect.YMost(); ++row) {
-    for (int32_t col = rect.x; col < rect.XMost(); ++col) {
+  for (int32_t row = rect.Y(); row < rect.YMost(); ++row) {
+    for (int32_t col = rect.X(); col < rect.XMost(); ++col) {
       int32_t i = row * rowLength + col * 4;
       if (aFuzz != 0) {
         ASSERT_LE_OR_RETURN(abs(aColor.mBlue - data[i + 0]), aFuzz, false);
         ASSERT_LE_OR_RETURN(abs(aColor.mGreen - data[i + 1]), aFuzz, false);
         ASSERT_LE_OR_RETURN(abs(aColor.mRed - data[i + 2]), aFuzz, false);
         ASSERT_LE_OR_RETURN(abs(aColor.mAlpha - data[i + 3]), aFuzz, false);
       } else {
         ASSERT_EQ_OR_RETURN(aColor.mBlue,  data[i + 0], false);
@@ -238,18 +238,18 @@ PalettedRectIsSolidColor(Decoder* aDecod
   // frame rect's offset doesn't actually mean anything in terms of the
   // in-memory representation of the surface. The image data starts at the upper
   // left corner of the frame rect, in other words.
   rect -= frameRect.TopLeft();
 
   // Walk through the image data and make sure that the entire rect has the
   // palette index |aColor|.
   int32_t rowLength = frameRect.Width();
-  for (int32_t row = rect.y; row < rect.YMost(); ++row) {
-    for (int32_t col = rect.x; col < rect.XMost(); ++col) {
+  for (int32_t row = rect.Y(); row < rect.YMost(); ++row) {
+    for (int32_t col = rect.X(); col < rect.XMost(); ++col) {
       int32_t i = row * rowLength + col;
       ASSERT_EQ_OR_RETURN(aColor, imageData[i], false);
     }
   }
 
   return true;
 }
 
@@ -337,28 +337,28 @@ CheckGeneratedImage(Decoder* aDecoder,
   // |             E             |
   // +---------------------------+
 
   // Check that the output rect itself is green. (Region 'C'.)
   EXPECT_TRUE(RectIsSolidColor(surface, aRect, BGRAColor::Green(), aFuzz));
 
   // Check that the area above the output rect is transparent. (Region 'A'.)
   EXPECT_TRUE(RectIsSolidColor(surface,
-                               IntRect(0, 0, surfaceSize.width, aRect.y),
+                               IntRect(0, 0, surfaceSize.width, aRect.Y()),
                                BGRAColor::Transparent(), aFuzz));
 
   // Check that the area to the left of the output rect is transparent. (Region 'B'.)
   EXPECT_TRUE(RectIsSolidColor(surface,
-                               IntRect(0, aRect.y, aRect.x, aRect.YMost()),
+                               IntRect(0, aRect.Y(), aRect.X(), aRect.YMost()),
                                BGRAColor::Transparent(), aFuzz));
 
   // Check that the area to the right of the output rect is transparent. (Region 'D'.)
   const int32_t widthOnRight = surfaceSize.width - aRect.XMost();
   EXPECT_TRUE(RectIsSolidColor(surface,
-                               IntRect(aRect.XMost(), aRect.y, widthOnRight, aRect.YMost()),
+                               IntRect(aRect.XMost(), aRect.Y(), widthOnRight, aRect.YMost()),
                                BGRAColor::Transparent(), aFuzz));
 
   // Check that the area below the output rect is transparent. (Region 'E'.)
   const int32_t heightBelow = surfaceSize.height - aRect.YMost();
   EXPECT_TRUE(RectIsSolidColor(surface,
                                IntRect(0, aRect.YMost(), surfaceSize.width, heightBelow),
                                BGRAColor::Transparent(), aFuzz));
 }
@@ -381,28 +381,28 @@ CheckGeneratedPalettedImage(Decoder* aDe
   // |             E             |
   // +---------------------------+
 
   // Check that the output rect itself is all 255's. (Region 'C'.)
   EXPECT_TRUE(PalettedRectIsSolidColor(aDecoder, aRect, 255));
 
   // Check that the area above the output rect is all 0's. (Region 'A'.)
   EXPECT_TRUE(PalettedRectIsSolidColor(aDecoder,
-                                       IntRect(0, 0, imageSize.width, aRect.y),
+                                       IntRect(0, 0, imageSize.width, aRect.Y()),
                                        0));
 
   // Check that the area to the left of the output rect is all 0's. (Region 'B'.)
   EXPECT_TRUE(PalettedRectIsSolidColor(aDecoder,
-                                       IntRect(0, aRect.y, aRect.x, aRect.YMost()),
+                                       IntRect(0, aRect.Y(), aRect.X(), aRect.YMost()),
                                        0));
 
   // Check that the area to the right of the output rect is all 0's. (Region 'D'.)
   const int32_t widthOnRight = imageSize.width - aRect.XMost();
   EXPECT_TRUE(PalettedRectIsSolidColor(aDecoder,
-                                       IntRect(aRect.XMost(), aRect.y, widthOnRight, aRect.YMost()),
+                                       IntRect(aRect.XMost(), aRect.Y(), widthOnRight, aRect.YMost()),
                                        0));
 
   // Check that the area below the output rect is transparent. (Region 'E'.)
   const int32_t heightBelow = imageSize.height - aRect.YMost();
   EXPECT_TRUE(PalettedRectIsSolidColor(aDecoder,
                                        IntRect(0, aRect.YMost(), imageSize.width, heightBelow),
                                        0));
 }
--- a/ipc/chromium/src/base/task.h
+++ b/ipc/chromium/src/base/task.h
@@ -330,18 +330,18 @@ NewRunnableMethod(T* object, Method meth
 
 } // namespace dont_add_new_uses_of_this
 
 // RunnableFunction and NewRunnableFunction implementation ---------------------
 
 template <class Function, class Params>
 class RunnableFunction : public mozilla::CancelableRunnable {
  public:
-   RunnableFunction(Function function, Params&& params)
-     : mozilla::CancelableRunnable("RunnableFunction")
+   RunnableFunction(const char* name, Function function, Params&& params)
+     : mozilla::CancelableRunnable(name)
      , function_(function)
      , params_(mozilla::Forward<Params>(params))
    {
   }
 
   ~RunnableFunction() {
   }
 
@@ -357,27 +357,27 @@ class RunnableFunction : public mozilla:
   }
 
   Function function_;
   Params params_;
 };
 
 template <class Function, typename... Args>
 inline already_AddRefed<mozilla::CancelableRunnable>
-NewCancelableRunnableFunction(Function function, Args&&... args) {
+NewCancelableRunnableFunction(const char* name, Function function, Args&&... args) {
   typedef mozilla::Tuple<typename mozilla::Decay<Args>::Type...> ArgsTuple;
   RefPtr<mozilla::CancelableRunnable> t =
-    new RunnableFunction<Function, ArgsTuple>(function,
+    new RunnableFunction<Function, ArgsTuple>(name, function,
                                               mozilla::MakeTuple(mozilla::Forward<Args>(args)...));
   return t.forget();
 }
 
 template <class Function, typename... Args>
 inline already_AddRefed<mozilla::Runnable>
-NewRunnableFunction(Function function, Args&&... args) {
+NewRunnableFunction(const char* name, Function function, Args&&... args) {
   typedef mozilla::Tuple<typename mozilla::Decay<Args>::Type...> ArgsTuple;
   RefPtr<mozilla::Runnable> t =
-    new RunnableFunction<Function, ArgsTuple>(function,
+    new RunnableFunction<Function, ArgsTuple>(name, function,
                                               mozilla::MakeTuple(mozilla::Forward<Args>(args)...));
   return t.forget();
 }
 
 #endif  // BASE_TASK_H_
--- a/js/src/frontend/BinSource.cpp
+++ b/js/src/frontend/BinSource.cpp
@@ -330,17 +330,17 @@ BinASTParser::parseBlockStatementAux(con
             break;
           default:
             return raiseInvalidField("BlockStatement", field);
         }
     }
 
     // In case of absent optional fields, inject default values.
     if (!body)
-        body = factory_.newStatementList(tokenizer_->pos());
+        TRY_VAR(body, factory_.newStatementList(tokenizer_->pos()));
 
     MOZ_TRY_VAR(body, appendDirectivesToBody(body, directives));
 
     ParseNode* result;
     if (kind == BinKind::Program) {
         result = body;
     } else {
         TRY_DECL(bindings, NewLexicalScopeData(cx_, scope, alloc_, parseContext_));
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -739,25 +739,16 @@ RecomputePosition(nsIFrame* aFrame)
   }
 
   aFrame->SchedulePaint();
 
   // For relative positioning, we can simply update the frame rect
   if (display->IsRelativelyPositionedStyle()) {
     // Move the frame
     if (display->mPosition == NS_STYLE_POSITION_STICKY) {
-      if (display->IsInnerTableStyle()) {
-        // We don't currently support sticky positioning of inner table
-        // elements (bug 975644). Bail.
-        //
-        // When this is fixed, remove the null-check for the computed
-        // offsets in nsTableRowFrame::ReflowChildren.
-        return true;
-      }
-
       // Update sticky positioning for an entire element at once, starting with
       // the first continuation or ib-split sibling.
       // It's rare that the frame we already have isn't already the first
       // continuation or ib-split sibling, but it can happen when styles differ
       // across continuations such as ::first-line or ::first-letter, and in
       // those cases we will generally (but maybe not always) do the work twice.
       nsIFrame* firstContinuation =
         nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -378,17 +378,17 @@ load 609821-1.xhtml
 load 613817-1.svg
 load 615146-1.html
 load 615781-1.xhtml
 load 616495-single-side-composite-color-border.html
 load 629035-1.html
 load 629908-1.html
 load 635329.html
 load 636229-1.html
-== 640272.html 640272-ref.html
+skip-if(webrender&&winWidget) == 640272.html 640272-ref.html # bug 1426199 for webrender
 load 645193.html
 load 645572-1.html
 load 650475.xhtml
 load 650489.xhtml
 load 651342-1.html
 load 653133-1.html
 load 663295.html
 load 663662-1.html
--- a/layout/generic/StickyScrollContainer.cpp
+++ b/layout/generic/StickyScrollContainer.cpp
@@ -40,17 +40,17 @@ StickyScrollContainer::GetStickyScrollCo
     nsLayoutUtils::GetNearestScrollableFrame(aFrame->GetParent(),
       nsLayoutUtils::SCROLLABLE_SAME_DOC |
       nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
   if (!scrollFrame) {
     // We might not find any, for instance in the case of
     // <html style="position: fixed">
     return nullptr;
   }
-  auto frame = static_cast<nsIFrame*>(do_QueryFrame(scrollFrame));
+  nsIFrame* frame = do_QueryFrame(scrollFrame);
   StickyScrollContainer* s =
     frame->GetProperty(StickyScrollContainerProperty());
   if (!s) {
     s = new StickyScrollContainer(scrollFrame);
     frame->SetProperty(StickyScrollContainerProperty(), s);
   }
   return s;
 }
@@ -172,16 +172,25 @@ StickyScrollContainer::ComputeStickyLimi
   nsIFrame* cbFrame = aFrame->GetContainingBlock();
   NS_ASSERTION(cbFrame == scrolledFrame ||
     nsLayoutUtils::IsProperAncestorFrame(scrolledFrame, cbFrame),
     "Scroll frame should be an ancestor of the containing block");
 
   nsRect rect =
     nsLayoutUtils::GetAllInFlowRectsUnion(aFrame, aFrame->GetParent());
 
+  // FIXME(bug 1421660): Table row groups aren't supposed to be containing
+  // blocks, but we treat them as such (maybe it's the right thing to do!).
+  // Anyway, not having this basically disables position: sticky on table cells,
+  // which would be really unfortunate, and doesn't match what other browsers
+  // do.
+  if (cbFrame != scrolledFrame && cbFrame->IsTableRowGroupFrame()) {
+    cbFrame = cbFrame->GetContainingBlock();
+  }
+
   // Containing block limits for the position of aFrame relative to its parent.
   // The margin box of the sticky element stays within the content box of the
   // contaning-block element.
   if (cbFrame != scrolledFrame) {
     *aContain = nsLayoutUtils::
       GetAllInFlowRectsUnion(cbFrame, aFrame->GetParent(),
                              nsLayoutUtils::RECTS_USE_CONTENT_BOX);
     nsRect marginRect = nsLayoutUtils::
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -685,25 +685,22 @@ nsFrame::Init(nsIContent*       aContent
       (IsFrameOfType(eSupportsCSSTransforms) &&
        nsLayoutUtils::HasAnimationOfProperty(this, eCSSProperty_transform))) {
     // The frame gets reconstructed if we toggle the -moz-transform
     // property, so we can set this bit here and then ignore it.
     AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
   }
   if (disp->mPosition == NS_STYLE_POSITION_STICKY &&
       !aPrevInFlow &&
-      !(mState & NS_FRAME_IS_NONDISPLAY) &&
-      !disp->IsInnerTableStyle()) {
+      !(mState & NS_FRAME_IS_NONDISPLAY)) {
     // Note that we only add first continuations, but we really only
     // want to add first continuation-or-ib-split-siblings.  But since we
     // don't yet know if we're a later part of a block-in-inline split,
     // we'll just add later members of a block-in-inline split here, and
     // then StickyScrollContainer will remove them later.
-    // We don't currently support relative positioning of inner table
-    // elements (bug 35168), so exclude them from sticky positioning too.
     StickyScrollContainer* ssc =
       StickyScrollContainer::GetStickyScrollContainerForFrame(this);
     if (ssc) {
       ssc->AddFrame(this);
     }
   }
 
   if (nsLayoutUtils::FontSizeInflationEnabled(PresContext()) || !GetParent()
@@ -721,18 +718,17 @@ nsFrame::Init(nsIContent*       aContent
         AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
       }
     }
     NS_ASSERTION(GetParent() ||
                  (GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER),
                  "root frame should always be a container");
   }
 
-  if (PresShell()->AssumeAllFramesVisible() &&
-      TrackingVisibility()) {
+  if (PresShell()->AssumeAllFramesVisible() && TrackingVisibility()) {
     IncApproximateVisibleCount();
   }
 
   DidSetStyleContext(nullptr);
 
   if (::IsXULBoxWrapped(this))
     ::InitBoxMetrics(this, false);
 }
@@ -7551,16 +7547,19 @@ static nsIFrame*
 GetNearestBlockContainer(nsIFrame* frame)
 {
   // The block wrappers we use to wrap blocks inside inlines aren't
   // described in the CSS spec.  We need to make them not be containing
   // blocks.
   // Since the parent of such a block is either a normal block or
   // another such pseudo, this shouldn't cause anything bad to happen.
   // Also the anonymous blocks inside table cells are not containing blocks.
+  //
+  // If we ever start skipping table row groups from being containing blocks,
+  // you need to remove the StickyScrollContainer hack referencing bug 1421660.
   while (frame->IsFrameOfType(nsIFrame::eLineParticipant) ||
          frame->IsBlockWrapper() ||
          // Table rows are not containing blocks either
          frame->IsTableRowFrame()) {
     frame = frame->GetParent();
     NS_ASSERTION(frame, "How come we got to the root frame without seeing a containing block?");
   }
   return frame;
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -744,20 +744,20 @@ nsCSSRendering::CreateWebRenderCommandsF
   bool imageTypeSupported = false;
   // FIXME(1409773): fix this: image->GetType() == eStyleImageType_Image
   // FIXME(1409774): fix this: image->GetType() == eStyleImageType_Gradient;
 
   if (!imageTypeSupported) {
     return false;
   }
 
-  if (styleBorder->mBorderImageRepeatH == NS_STYLE_BORDER_IMAGE_REPEAT_ROUND ||
-      styleBorder->mBorderImageRepeatH == NS_STYLE_BORDER_IMAGE_REPEAT_SPACE ||
-      styleBorder->mBorderImageRepeatV == NS_STYLE_BORDER_IMAGE_REPEAT_ROUND ||
-      styleBorder->mBorderImageRepeatV == NS_STYLE_BORDER_IMAGE_REPEAT_SPACE) {
+  if (styleBorder->mBorderImageRepeatH == StyleBorderImageRepeat::Round ||
+      styleBorder->mBorderImageRepeatH == StyleBorderImageRepeat::Space ||
+      styleBorder->mBorderImageRepeatV == StyleBorderImageRepeat::Round ||
+      styleBorder->mBorderImageRepeatV == StyleBorderImageRepeat::Space) {
     return false;
   }
 
 
   uint32_t flags = 0;
   if (aDisplayListBuilder->ShouldSyncDecodeImages()) {
     flags |= nsImageRenderer::FLAG_SYNC_DECODE_IMAGES;
   }
--- a/layout/painting/nsCSSRenderingBorders.cpp
+++ b/layout/painting/nsCSSRenderingBorders.cpp
@@ -3749,17 +3749,17 @@ nsCSSBorderImageRenderer::DrawBorderImag
     std::max(mImageSize.height - mSlice.top - mSlice.bottom, 0),
     mSlice.bottom,
   };
 
   ImgDrawResult result = ImgDrawResult::SUCCESS;
 
   for (int i = LEFT; i <= RIGHT; i++) {
     for (int j = TOP; j <= BOTTOM; j++) {
-      uint8_t fillStyleH, fillStyleV;
+      StyleBorderImageRepeat fillStyleH, fillStyleV;
       nsSize unitSize;
 
       if (i == MIDDLE && j == MIDDLE) {
         // Discard the middle portion unless set to fill.
         if (NS_STYLE_BORDER_IMAGE_SLICE_NOFILL == mFill) {
           continue;
         }
 
@@ -3803,36 +3803,36 @@ nsCSSBorderImageRenderer::DrawBorderImag
         if (0 < borderHeight[j] && 0 < sliceHeight[j])
           factor = gfxFloat(borderHeight[j]) / sliceHeight[j];
         else
           factor = 1;
 
         unitSize.width = sliceWidth[i] * factor;
         unitSize.height = borderHeight[j];
         fillStyleH = mRepeatModeHorizontal;
-        fillStyleV = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH;
+        fillStyleV = StyleBorderImageRepeat::Stretch;
 
       } else if (j == MIDDLE) { // left, right
         gfxFloat factor;
         if (0 < borderWidth[i] && 0 < sliceWidth[i])
           factor = gfxFloat(borderWidth[i]) / sliceWidth[i];
         else
           factor = 1;
 
         unitSize.width = borderWidth[i];
         unitSize.height = sliceHeight[j] * factor;
-        fillStyleH = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH;
+        fillStyleH = StyleBorderImageRepeat::Stretch;
         fillStyleV = mRepeatModeVertical;
 
       } else {
         // Corners are always stretched to fit the corner.
         unitSize.width = borderWidth[i];
         unitSize.height = borderHeight[j];
-        fillStyleH = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH;
-        fillStyleV = NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH;
+        fillStyleH = StyleBorderImageRepeat::Stretch;
+        fillStyleV = StyleBorderImageRepeat::Stretch;
       }
 
       nsRect destArea(borderX[i], borderY[j], borderWidth[i], borderHeight[j]);
       nsRect subArea(sliceX[i], sliceY[j], sliceWidth[i], sliceHeight[j]);
       if (subArea.IsEmpty())
         continue;
 
       nsIntRect intSubArea = subArea.ToOutsidePixels(nsPresContext::AppUnitsPerCSSPixel());
--- a/layout/painting/nsCSSRenderingBorders.h
+++ b/layout/painting/nsCSSRenderingBorders.h
@@ -331,18 +331,18 @@ private:
 
   nsImageRenderer mImageRenderer;
   nsSize mImageSize;
   nsMargin mSlice;
   nsMargin mWidths;
   nsMargin mImageOutset;
   nsRect mArea;
   nsRect mClip;
-  uint8_t mRepeatModeHorizontal;
-  uint8_t mRepeatModeVertical;
+  mozilla::StyleBorderImageRepeat mRepeatModeHorizontal;
+  mozilla::StyleBorderImageRepeat mRepeatModeVertical;
   uint8_t mFill;
 
   friend class nsDisplayBorder;
   friend struct nsCSSRendering;
 };
 
 namespace mozilla {
 #ifdef DEBUG_NEW_BORDERS
--- a/layout/painting/nsImageRenderer.cpp
+++ b/layout/painting/nsImageRenderer.cpp
@@ -740,78 +740,78 @@ nsImageRenderer::BuildWebRenderDisplayIt
                                     aOpacity);
 }
 
 /**
  * Compute the size and position of the master copy of the image. I.e., a single
  * tile used to fill the dest rect.
  * aFill The destination rect to be filled
  * aHFill and aVFill are the repeat patterns for the component -
- * NS_STYLE_BORDER_IMAGE_REPEAT_* - i.e., how a tiling unit is used to fill aFill
+ * StyleBorderImageRepeat - i.e., how a tiling unit is used to fill aFill
  * aUnitSize The size of the source rect in dest coords.
  */
 static nsRect
 ComputeTile(nsRect&              aFill,
-            uint8_t              aHFill,
-            uint8_t              aVFill,
+            StyleBorderImageRepeat aHFill,
+            StyleBorderImageRepeat aVFill,
             const nsSize&        aUnitSize,
             nsSize&              aRepeatSize)
 {
   nsRect tile;
   switch (aHFill) {
-  case NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH:
+  case StyleBorderImageRepeat::Stretch:
     tile.x = aFill.x;
     tile.width = aFill.width;
     aRepeatSize.width = tile.width;
     break;
-  case NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT:
+  case StyleBorderImageRepeat::Repeat:
     tile.x = aFill.x + aFill.width/2 - aUnitSize.width/2;
     tile.width = aUnitSize.width;
     aRepeatSize.width = tile.width;
     break;
-  case NS_STYLE_BORDER_IMAGE_REPEAT_ROUND:
+  case StyleBorderImageRepeat::Round:
     tile.x = aFill.x;
     tile.width = nsCSSRendering::ComputeRoundedSize(aUnitSize.width,
                                                     aFill.width);
     aRepeatSize.width = tile.width;
     break;
-  case NS_STYLE_BORDER_IMAGE_REPEAT_SPACE:
+  case StyleBorderImageRepeat::Space:
     {
       nscoord space;
       aRepeatSize.width =
         nsCSSRendering::ComputeBorderSpacedRepeatSize(aUnitSize.width,
                                                       aFill.width, space);
       tile.x = aFill.x + space;
       tile.width = aUnitSize.width;
       aFill.x = tile.x;
       aFill.width = aFill.width - space * 2;
     }
     break;
   default:
     NS_NOTREACHED("unrecognized border-image fill style");
   }
 
   switch (aVFill) {
-  case NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH:
+  case StyleBorderImageRepeat::Stretch:
     tile.y = aFill.y;
     tile.height = aFill.height;
     aRepeatSize.height = tile.height;
     break;
-  case NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT:
+  case StyleBorderImageRepeat::Repeat:
     tile.y = aFill.y + aFill.height/2 - aUnitSize.height/2;
     tile.height = aUnitSize.height;
     aRepeatSize.height = tile.height;
     break;
-  case NS_STYLE_BORDER_IMAGE_REPEAT_ROUND:
+  case StyleBorderImageRepeat::Round:
     tile.y = aFill.y;
     tile.height = nsCSSRendering::ComputeRoundedSize(aUnitSize.height,
                                                      aFill.height);
     aRepeatSize.height = tile.height;
     break;
-  case NS_STYLE_BORDER_IMAGE_REPEAT_SPACE:
+  case StyleBorderImageRepeat::Space:
     {
       nscoord space;
       aRepeatSize.height =
         nsCSSRendering::ComputeBorderSpacedRepeatSize(aUnitSize.height,
                                                       aFill.height, space);
       tile.y = aFill.y + space;
       tile.height = aUnitSize.height;
       aFill.y = tile.y;
@@ -827,36 +827,36 @@ ComputeTile(nsRect&              aFill,
 
 /**
  * Returns true if the given set of arguments will require the tiles which fill
  * the dest rect to be scaled from the source tile. See comment on ComputeTile
  * for argument descriptions.
  */
 static bool
 RequiresScaling(const nsRect&        aFill,
-                uint8_t              aHFill,
-                uint8_t              aVFill,
+                StyleBorderImageRepeat aHFill,
+                StyleBorderImageRepeat aVFill,
                 const nsSize&        aUnitSize)
 {
   // If we have no tiling in either direction, we can skip the intermediate
   // scaling step.
-  return (aHFill != NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH ||
-          aVFill != NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH) &&
+  return (aHFill != StyleBorderImageRepeat::Stretch ||
+          aVFill != StyleBorderImageRepeat::Stretch) &&
          (aUnitSize.width != aFill.width ||
           aUnitSize.height != aFill.height);
 }
 
 ImgDrawResult
 nsImageRenderer::DrawBorderImageComponent(nsPresContext*       aPresContext,
                                           gfxContext&          aRenderingContext,
                                           const nsRect&        aDirtyRect,
                                           const nsRect&        aFill,
                                           const CSSIntRect&    aSrc,
-                                          uint8_t              aHFill,
-                                          uint8_t              aVFill,
+                                          StyleBorderImageRepeat aHFill,
+                                          StyleBorderImageRepeat aVFill,
                                           const nsSize&        aUnitSize,
                                           uint8_t              aIndex,
                                           const Maybe<nsSize>& aSVGViewportSize,
                                           const bool           aHasIntrinsicRatio)
 {
   if (!IsReady()) {
     NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me");
     return ImgDrawResult::BAD_ARGS;
--- a/layout/painting/nsImageRenderer.h
+++ b/layout/painting/nsImageRenderer.h
@@ -242,18 +242,18 @@ public:
    * intrinsic ratio.
    */
   ImgDrawResult
   DrawBorderImageComponent(nsPresContext*       aPresContext,
                            gfxContext&          aRenderingContext,
                            const nsRect&        aDirtyRect,
                            const nsRect&        aFill,
                            const mozilla::CSSIntRect& aSrc,
-                           uint8_t              aHFill,
-                           uint8_t              aVFill,
+                           mozilla::StyleBorderImageRepeat aHFill,
+                           mozilla::StyleBorderImageRepeat aVFill,
                            const nsSize&        aUnitSize,
                            uint8_t              aIndex,
                            const mozilla::Maybe<nsSize>& aSVGViewportSize,
                            const bool           aHasIntrinsicRatio);
 
   bool IsRasterImage();
   bool IsAnimatedImage();
 
deleted file mode 100644
--- a/layout/reftests/position-sticky/inner-table-1-ref.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-<html>
-  <head>
-    <link rel="author" title="Corey Ford" href="mailto:corey@coreyford.name">
-  </head>
-  <body>
-    <table>
-      <thead>
-        <tr>
-          <td>a</td>
-        </tr>
-      </thead>
-      <tr>
-        <td>b</td>
-      </tr>
-      <tr>
-        <td>c</td>
-      </tr>
-      <tr>
-        <td>d</td>
-      </tr>
-    </table>
-  </body>
-</html>
deleted file mode 100644
--- a/layout/reftests/position-sticky/inner-table-1.html
+++ /dev/null
@@ -1,35 +0,0 @@
-<!DOCTYPE html>
-<!-- Any copyright is dedicated to the Public Domain.
-   - http://creativecommons.org/publicdomain/zero/1.0/ -->
-<html>
-  <head>
-    <title>CSS Test: Sticky Positioning - inner table elements</title>
-    <link rel="author" title="Corey Ford" href="mailto:corey@coreyford.name">
-    <link rel="match" href="inner-table-1-ref.html">
-    <meta name="assert" content="Sticky positioning on inner table elements should have no effect (until bug 35168 is fixed)">
-    <style>
-      .sticky {
-        position: sticky;
-        top: 1000px;
-      }
-    </style>
-  </head>
-  <body>
-    <table>
-      <thead class="sticky">
-        <tr>
-          <td>a</td>
-        </tr>
-      </thead>
-      <tr class="sticky">
-        <td>b</td>
-      </tr>
-      <tr>
-        <td class="sticky">c</td>
-      </tr>
-      <tr>
-        <td>d</td>
-      </tr>
-    </table>
-  </body>
-</html>
--- a/layout/reftests/position-sticky/reftest.list
+++ b/layout/reftests/position-sticky/reftest.list
@@ -43,10 +43,9 @@ fuzzy-if(OSX,99,210) == inline-3.html in
 skip-if(!asyncPan) == inline-4.html inline-4-ref.html
 fails == column-contain-1a.html column-contain-1-ref.html
 == column-contain-1b.html column-contain-1-ref.html
 == column-contain-2.html column-contain-2-ref.html
 == block-in-inline-1.html block-in-inline-1-ref.html
 fuzzy-if(skiaContent,1,22) fuzzy-if(winWidget&&!layersGPUAccelerated,116,1320) fuzzy-if(Android,8,1533) == block-in-inline-2.html block-in-inline-2-ref.html
 fuzzy-if(Android,8,630) fuzzy-if(OSX,1,11) fuzzy-if(skiaContent,1,220) fuzzy-if(winWidget&&!layersGPUAccelerated,116,1320) == block-in-inline-3.html block-in-inline-3-ref.html
 == block-in-inline-continuations.html block-in-inline-continuations-ref.html
-fuzzy-if(winWidget&&!layersGPUAccelerated,140,140) == inner-table-1.html inner-table-1-ref.html
 == iframe-1.html iframe-1-ref.html
--- a/layout/style/CSSNamespaceRule.h
+++ b/layout/style/CSSNamespaceRule.h
@@ -31,16 +31,22 @@ public:
 
   virtual nsAtom* GetPrefix() const = 0;
   virtual void GetURLSpec(nsString& aURLSpec) const = 0;
 
   // WebIDL interfaces
   uint16_t Type() const final {
     return nsIDOMCSSRule::NAMESPACE_RULE;
   }
+  void GetNamespaceURI(nsString& aNamespaceURI) {
+    GetURLSpec(aNamespaceURI);
+  }
+  void GetPrefix(DOMString& aPrefix) {
+    aPrefix.SetOwnedAtom(GetPrefix(), DOMString::eTreatNullAsEmpty);
+  }
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const = 0;
 
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) final {
     return CSSNamespaceRuleBinding::Wrap(aCx, this, aGivenProto);
   }
 };
--- a/layout/style/FontFace.cpp
+++ b/layout/style/FontFace.cpp
@@ -673,16 +673,21 @@ FontFace::SetUserFontEntry(gfxUserFontEn
   if (mUserFontEntry) {
     mUserFontEntry->mFontFaces.RemoveElement(this);
   }
 
   mUserFontEntry = static_cast<Entry*>(aEntry);
   if (mUserFontEntry) {
     mUserFontEntry->mFontFaces.AppendElement(this);
 
+    MOZ_ASSERT(mUserFontEntry->GetUserFontSet() ==
+                 mFontFaceSet->GetUserFontSet(),
+               "user font entry must be associated with the same user font set "
+               "as the FontFace");
+
     // Our newly assigned user font entry might be in the process of or
     // finished loading, so set our status accordingly.  But only do so
     // if we're not going "backwards" in status, which could otherwise
     // happen in this case:
     //
     //   new FontFace("ABC", "url(x)").load();
     //
     // where the SetUserFontEntry call (from the after-initialization
--- a/layout/style/FontFace.h
+++ b/layout/style/FontFace.h
@@ -90,16 +90,18 @@ public:
   /**
    * Returns whether this object is in the specified FontFaceSet.
    */
   bool IsInFontFaceSet(FontFaceSet* aFontFaceSet) const;
 
   void AddFontFaceSet(FontFaceSet* aFontFaceSet);
   void RemoveFontFaceSet(FontFaceSet* aFontFaceSet);
 
+  FontFaceSet* GetPrimaryFontFaceSet() const { return mFontFaceSet; }
+
   /**
    * Gets the family name of the FontFace as a raw string (such as 'Times', as
    * opposed to GetFamily, which returns a CSS-escaped string, such as
    * '"Times"').  Returns whether a valid family name was available.
    */
   bool GetFamilyName(nsString& aResult);
 
   /**
--- a/layout/style/FontFaceSet.cpp
+++ b/layout/style/FontFaceSet.cpp
@@ -996,35 +996,37 @@ FontFaceSet::InsertRuleFontFace(FontFace
   // Add the entry to the end of the list.  If an existing userfont entry was
   // returned by FindOrCreateUserFontEntryFromFontFace that was already stored
   // on the family, gfxUserFontFamily::AddFontEntry(), which AddUserFontEntry
   // calls, will automatically remove the earlier occurrence of the same
   // userfont entry.
   mUserFontSet->AddUserFontEntry(fontfamily, entry);
 }
 
-already_AddRefed<gfxUserFontEntry>
+/* static */ already_AddRefed<gfxUserFontEntry>
 FontFaceSet::FindOrCreateUserFontEntryFromFontFace(FontFace* aFontFace)
 {
   nsAutoString fontfamily;
   if (!aFontFace->GetFamilyName(fontfamily)) {
     // If there is no family name, this rule cannot contribute a
     // usable font, so there is no point in processing it further.
     return nullptr;
   }
 
   return FindOrCreateUserFontEntryFromFontFace(fontfamily, aFontFace,
                                                SheetType::Doc);
 }
 
-already_AddRefed<gfxUserFontEntry>
+/* static */ already_AddRefed<gfxUserFontEntry>
 FontFaceSet::FindOrCreateUserFontEntryFromFontFace(const nsAString& aFamilyName,
                                                    FontFace* aFontFace,
                                                    SheetType aSheetType)
 {
+  FontFaceSet* set = aFontFace->GetPrimaryFontFaceSet();
+
   nsCSSValue val;
   nsCSSUnit unit;
 
   uint32_t weight = NS_STYLE_FONT_WEIGHT_NORMAL;
   int32_t stretch = NS_STYLE_FONT_STRETCH_NORMAL;
   uint8_t italicStyle = NS_STYLE_FONT_STYLE_NORMAL;
   uint32_t languageOverride = NO_FONT_LANGUAGE_OVERRIDE;
   uint8_t fontDisplay = NS_FONT_DISPLAY_AUTO;
@@ -1143,17 +1145,17 @@ FontFaceSet::FindOrCreateUserFontEntryFr
           face->mReferrerPolicy = mozilla::net::RP_Unset;
           break;
         case eCSSUnit_URL: {
           face->mSourceType = gfxFontFaceSrc::eSourceType_URL;
           nsIURI* uri = val.GetURLValue();
           face->mURI = uri ? new gfxFontSrcURI(uri) : nullptr;
           URLValue* url = val.GetURLStructValue();
           face->mReferrer = url->mExtraData->GetReferrer();
-          face->mReferrerPolicy = mDocument->GetReferrerPolicy();
+          face->mReferrerPolicy = set->mDocument->GetReferrerPolicy();
           face->mOriginPrincipal =
             new gfxFontSrcPrincipal(url->mExtraData->GetPrincipal());
           NS_ASSERTION(face->mOriginPrincipal, "null origin principal in @font-face rule");
 
           // agent and user stylesheets are treated slightly differently,
           // the same-site origin check and access control headers are
           // enforced against the sheet principal rather than the document
           // principal to allow user stylesheets to include @font-face rules
@@ -1210,21 +1212,21 @@ FontFaceSet::FindOrCreateUserFontEntryFr
     }
   }
 
   if (srcArray.IsEmpty()) {
     return nullptr;
   }
 
   RefPtr<gfxUserFontEntry> entry =
-    mUserFontSet->FindOrCreateUserFontEntry(aFamilyName, srcArray, weight,
-                                            stretch, italicStyle,
-                                            featureSettings,
-                                            languageOverride,
-                                            unicodeRanges, fontDisplay);
+    set->mUserFontSet->FindOrCreateUserFontEntry(aFamilyName, srcArray, weight,
+                                                 stretch, italicStyle,
+                                                 featureSettings,
+                                                 languageOverride,
+                                                 unicodeRanges, fontDisplay);
   return entry.forget();
 }
 
 nsCSSFontFaceRule*
 FontFaceSet::FindRuleForEntry(gfxFontEntry* aFontEntry)
 {
   NS_ASSERTION(!aFontEntry->mIsUserFontContainer, "only platform font entries");
   for (uint32_t i = 0; i < mRuleFaces.Length(); ++i) {
--- a/layout/style/FontFaceSet.h
+++ b/layout/style/FontFaceSet.h
@@ -130,17 +130,17 @@ public:
   nsCSSFontFaceRule* FindRuleForEntry(gfxFontEntry* aFontEntry);
 
   void IncrementGeneration(bool aIsRebuild = false);
 
   /**
    * Finds an existing entry in the user font cache or creates a new user
    * font entry for the given FontFace object.
    */
-  already_AddRefed<gfxUserFontEntry>
+  static already_AddRefed<gfxUserFontEntry>
     FindOrCreateUserFontEntryFromFontFace(FontFace* aFontFace);
 
   /**
    * Notification method called by a FontFace to indicate that its loading
    * status has changed.
    */
   void OnFontFaceStatusChanged(FontFace* aFontFace);
 
@@ -262,17 +262,17 @@ private:
     SheetType mSheetType;  // only relevant for mRuleFaces entries
 
     // When true, indicates that when finished loading, the FontFace should be
     // included in the subsequent loadingdone/loadingerror event fired at the
     // FontFaceSet.
     bool mLoadEventShouldFire;
   };
 
-  already_AddRefed<gfxUserFontEntry> FindOrCreateUserFontEntryFromFontFace(
+  static already_AddRefed<gfxUserFontEntry> FindOrCreateUserFontEntryFromFontFace(
                                                    const nsAString& aFamilyName,
                                                    FontFace* aFontFace,
                                                    SheetType aSheetType);
 
   // search for @font-face rule that matches a userfont font entry
   nsCSSFontFaceRule* FindRuleForUserFontEntry(gfxUserFontEntry* aUserFontEntry);
 
   nsresult StartLoad(gfxUserFontEntry* aUserFontEntry,
--- a/layout/style/ServoBindings.toml
+++ b/layout/style/ServoBindings.toml
@@ -163,16 +163,17 @@ rusty-enums = [
     "StyleTextJustify",
     "StyleHyphens",
     "StyleGeometryBox",
     "StyleFillRule",
     "StyleShapeSourceType",
     "StyleBasicShapeType",
     "nsStyleImageLayers_Size_DimensionType",
     "StyleStackSizing",
+    "StyleBorderImageRepeat",
     "StyleBoxPack",
     "StyleBoxOrient",
     "StyleBoxAlign",
     "StyleUserFocus",
     "StyleUserSelect",
     "StyleImageLayerRepeat",
     "StyleBoxDecorationBreak",
     "StyleRuleInclusion",
--- a/layout/style/nsCSSDataBlock.cpp
+++ b/layout/style/nsCSSDataBlock.cpp
@@ -449,18 +449,17 @@ nsCSSCompressedDataBlock::HasDefaultBord
   return outset.AllSidesEqualTo(nsCSSValue(0.0f, eCSSUnit_Number));
 }
 
 bool
 nsCSSCompressedDataBlock::HasDefaultBorderImageRepeat() const
 {
   const nsCSSValuePair &repeat =
     ValueFor(eCSSProperty_border_image_repeat)->GetPairValue();
-  return repeat.BothValuesEqualTo(
-    nsCSSValue(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH, eCSSUnit_Enumerated));
+  return repeat.BothValuesEqualTo(nsCSSValue(StyleBorderImageRepeat::Stretch));
 }
 
 /*****************************************************************************/
 
 nsCSSExpandedDataBlock::nsCSSExpandedDataBlock()
 {
   AssertInitialState();
 }
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -13153,18 +13153,17 @@ CSSParserImpl::SetBorderImageInitialValu
   nsCSSValue outset;
   nsCSSRect& outsetBox = outset.SetRectValue();
   outsetBox.SetAllSidesTo(nsCSSValue(0.0f, eCSSUnit_Number));
   AppendValue(eCSSProperty_border_image_outset, outset);
 
   // border-image-repeat: repeat
   nsCSSValue repeat;
   nsCSSValuePair repeatPair;
-  repeatPair.SetBothValuesTo(nsCSSValue(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH,
-                                        eCSSUnit_Enumerated));
+  repeatPair.SetBothValuesTo(nsCSSValue(StyleBorderImageRepeat::Stretch));
   repeat.SetPairValue(&repeatPair);
   AppendValue(eCSSProperty_border_image_repeat, repeat);
 }
 
 bool
 CSSParserImpl::ParseBorderImageSlice(bool aAcceptsInherit,
                                      bool* aConsumedTokens)
 {
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -999,20 +999,20 @@ const KTableEntry nsCSSProps::kBlendMode
 
 const KTableEntry nsCSSProps::kBorderCollapseKTable[] = {
   { eCSSKeyword_collapse,  NS_STYLE_BORDER_COLLAPSE },
   { eCSSKeyword_separate,  NS_STYLE_BORDER_SEPARATE },
   { eCSSKeyword_UNKNOWN,   -1 }
 };
 
 const KTableEntry nsCSSProps::kBorderImageRepeatKTable[] = {
-  { eCSSKeyword_stretch, NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH },
-  { eCSSKeyword_repeat, NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT },
-  { eCSSKeyword_round, NS_STYLE_BORDER_IMAGE_REPEAT_ROUND },
-  { eCSSKeyword_space, NS_STYLE_BORDER_IMAGE_REPEAT_SPACE },
+  { eCSSKeyword_stretch, StyleBorderImageRepeat::Stretch },
+  { eCSSKeyword_repeat, StyleBorderImageRepeat::Repeat },
+  { eCSSKeyword_round, StyleBorderImageRepeat::Round },
+  { eCSSKeyword_space, StyleBorderImageRepeat::Space },
   { eCSSKeyword_UNKNOWN, -1 }
 };
 
 const KTableEntry nsCSSProps::kBorderImageSliceKTable[] = {
   { eCSSKeyword_fill, NS_STYLE_BORDER_IMAGE_SLICE_FILL },
   { eCSSKeyword_UNKNOWN, -1 }
 };
 
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -617,16 +617,26 @@ public:
   explicit nsCSSValue(mozilla::SharedFontList* aValue);
   nsCSSValue(const nsCSSValue& aCopy);
   nsCSSValue(nsCSSValue&& aOther)
     : mUnit(aOther.mUnit)
     , mValue(aOther.mValue)
   {
     aOther.mUnit = eCSSUnit_Null;
   }
+  template<typename T,
+           typename = typename std::enable_if<std::is_enum<T>::value>::type>
+  explicit nsCSSValue(T aValue)
+    : mUnit(eCSSUnit_Enumerated)
+  {
+    static_assert(mozilla::EnumTypeFitsWithin<T, int32_t>::value,
+                  "aValue must be an enum that fits within mValue.mInt");
+    mValue.mInt = static_cast<int32_t>(aValue);
+  }
+
   ~nsCSSValue() { Reset(); }
 
   nsCSSValue&  operator=(const nsCSSValue& aCopy);
   nsCSSValue&  operator=(nsCSSValue&& aCopy);
   bool        operator==(const nsCSSValue& aOther) const;
 
   bool operator!=(const nsCSSValue& aOther) const
   {
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -1420,16 +1420,17 @@ struct SetEnumValueHelper
   { \
     auto value = aValue.GetIntValue(); \
     MOZ_ASSERT(value >= static_cast<decltype(value)>(type_::min_) && \
                value <= static_cast<decltype(value)>(type_::max_), \
                "inappropriate value"); \
     aField = static_cast<type_>(value); \
   }
 
+  DEFINE_ENUM_CLASS_SETTER(StyleBorderImageRepeat, Stretch, Space)
   DEFINE_ENUM_CLASS_SETTER(StyleBoxAlign, Stretch, End)
   DEFINE_ENUM_CLASS_SETTER(StyleBoxDecorationBreak, Slice, Clone)
   DEFINE_ENUM_CLASS_SETTER(StyleBoxDirection, Normal, Reverse)
   DEFINE_ENUM_CLASS_SETTER(StyleBoxOrient, Horizontal, Vertical)
   DEFINE_ENUM_CLASS_SETTER(StyleBoxPack, Start, Justify)
   DEFINE_ENUM_CLASS_SETTER(StyleBoxSizing, Content, Border)
   DEFINE_ENUM_CLASS_SETTER(StyleClear, None, Both)
   DEFINE_ENUM_CLASS_SETTER(StyleContent, OpenQuote, AltContent)
@@ -7597,24 +7598,24 @@ nsRuleNode::ComputeBorderData(void* aSta
   SetBorderImagePair(*aRuleData->ValueForBorderImageRepeat(),
                      borderImageRepeat);
 
   SetValue(borderImageRepeat.mXValue,
            border->mBorderImageRepeatH,
            conditions,
            SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL,
            parentBorder->mBorderImageRepeatH,
-           NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH);
+           StyleBorderImageRepeat::Stretch);
 
   SetValue(borderImageRepeat.mYValue,
            border->mBorderImageRepeatV,
            conditions,
            SETVAL_ENUMERATED | SETVAL_UNSET_INITIAL,
            parentBorder->mBorderImageRepeatV,
-           NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH);
+           StyleBorderImageRepeat::Stretch);
 
   COMPUTE_END_RESET(Border, border)
 }
 
 const void*
 nsRuleNode::ComputePaddingData(void* aStartStruct,
                                const nsRuleData* aRuleData,
                                GeckoStyleContext* aContext,
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -332,21 +332,23 @@ enum class StyleImageLayerRepeat : uint8
 #define NS_STYLE_BORDER_STYLE_DASHED            4
 #define NS_STYLE_BORDER_STYLE_SOLID             5
 #define NS_STYLE_BORDER_STYLE_DOUBLE            6
 #define NS_STYLE_BORDER_STYLE_INSET             7
 #define NS_STYLE_BORDER_STYLE_OUTSET            8
 #define NS_STYLE_BORDER_STYLE_HIDDEN            9
 #define NS_STYLE_BORDER_STYLE_AUTO              10 // for outline-style only
 
-// See nsStyleBorder mBorderImage
-#define NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH    0
-#define NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT     1
-#define NS_STYLE_BORDER_IMAGE_REPEAT_ROUND      2
-#define NS_STYLE_BORDER_IMAGE_REPEAT_SPACE      3
+// border-image-repeat
+enum class StyleBorderImageRepeat : uint8_t {
+  Stretch,
+  Repeat,
+  Round,
+  Space
+};
 
 #define NS_STYLE_BORDER_IMAGE_SLICE_NOFILL      0
 #define NS_STYLE_BORDER_IMAGE_SLICE_FILL        1
 
 // See nsStyleContent
 enum class StyleContent : uint8_t {
   OpenQuote,
   CloseQuote,
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -332,18 +332,18 @@ nsStylePadding::CalcDifference(const nsS
   // force reflow of all descendants, but the hint would need to force
   // reflow of the frame's children (see how
   // ReflowInput::InitResizeFlags initializes the inline-resize flag).
   return NS_STYLE_HINT_REFLOW & ~nsChangeHint_ClearDescendantIntrinsics;
 }
 
 nsStyleBorder::nsStyleBorder(const nsPresContext* aContext)
   : mBorderImageFill(NS_STYLE_BORDER_IMAGE_SLICE_NOFILL)
-  , mBorderImageRepeatH(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH)
-  , mBorderImageRepeatV(NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH)
+  , mBorderImageRepeatH(StyleBorderImageRepeat::Stretch)
+  , mBorderImageRepeatV(StyleBorderImageRepeat::Stretch)
   , mFloatEdge(StyleFloatEdge::ContentBox)
   , mBoxDecorationBreak(StyleBoxDecorationBreak::Slice)
   , mComputedBorder(0, 0, 0, 0)
 {
   MOZ_COUNT_CTOR(nsStyleBorder);
 
   NS_FOR_CSS_HALF_CORNERS (corner) {
     mBorderRadius.Set(corner, nsStyleCoord(0, nsStyleCoord::CoordConstructor));
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1248,18 +1248,18 @@ public:
   mozilla::UniquePtr<nsBorderColors> mBorderColors;
   nsStyleCorners mBorderRadius;       // [reset] coord, percent
   nsStyleImage   mBorderImageSource;  // [reset]
   nsStyleSides   mBorderImageSlice;   // [reset] factor, percent
   nsStyleSides   mBorderImageWidth;   // [reset] length, factor, percent, auto
   nsStyleSides   mBorderImageOutset;  // [reset] length, factor
 
   uint8_t        mBorderImageFill;    // [reset]
-  uint8_t        mBorderImageRepeatH; // [reset] see nsStyleConsts.h
-  uint8_t        mBorderImageRepeatV; // [reset]
+  mozilla::StyleBorderImageRepeat mBorderImageRepeatH; // [reset]
+  mozilla::StyleBorderImageRepeat mBorderImageRepeatV; // [reset]
   mozilla::StyleFloatEdge mFloatEdge; // [reset]
   mozilla::StyleBoxDecorationBreak mBoxDecorationBreak; // [reset]
 
 protected:
   uint8_t       mBorderStyle[4];  // [reset] See nsStyleConsts.h
 
 public:
   // [reset] the colors to use for a simple border.
--- a/layout/tables/nsTableRowFrame.cpp
+++ b/layout/tables/nsTableRowFrame.cpp
@@ -936,22 +936,23 @@ nsTableRowFrame::ReflowChildren(nsPresCo
         // We reflowed. Apply relative positioning in the normal way.
         kidReflowInput->ApplyRelativePositioning(&kidPosition, containerSize);
       } else if (kidFrame->IsRelativelyPositioned()) {
         // We didn't reflow.  Do the positioning part of what
         // MovePositionBy does internally.  (This codepath should really
         // be merged into the else below if we can.)
         nsMargin* computedOffsetProp =
           kidFrame->GetProperty(nsIFrame::ComputedOffsetProperty());
-        // Bug 975644: a position:sticky kid can end up with a null
-        // property value here.
-        LogicalMargin computedOffsets(wm, computedOffsetProp ?
-                                            *computedOffsetProp : nsMargin());
-        ReflowInput::ApplyRelativePositioning(kidFrame, wm, computedOffsets,
-                                                    &kidPosition, containerSize);
+
+        // On our fist reflow sticky children may not have the property yet (we
+        // need to reflow the children first to size the scroll frame).
+        LogicalMargin computedOffsets(
+          wm, computedOffsetProp ? *computedOffsetProp : nsMargin());
+        ReflowInput::ApplyRelativePositioning(
+            kidFrame, wm, computedOffsets, &kidPosition, containerSize);
       }
 
       // In vertical-rl mode, we are likely to have containerSize.width = 0
       // because ComputedWidth() was NS_UNCONSTRAINEDSIZE.
       // For cases where that's wrong, we will fix up the position later.
       FinishReflowChild(kidFrame, aPresContext, desiredSize, nullptr,
                         wm, kidPosition, containerSize, 0);
 
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -3825,18 +3825,24 @@ PeerConnectionImpl::DTMFState::Notify(ns
   }
 
   RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
   if (!pco) {
     NS_WARNING("Failed to dispatch the RTCDTMFToneChange event!");
     return NS_OK; // Return is ignored anyhow
   }
 
+  RefPtr<dom::MediaStreamTrack> sendTrack = mTransceiver->GetSendTrack();
+  if (!sendTrack) {
+    NS_WARNING("Failed to dispatch the RTCDTMFToneChange event!");
+    return NS_OK; // Return is ignored anyhow
+  }
+
   JSErrorResult jrv;
-  pco->OnDTMFToneChange(*mTransceiver->GetSendTrack(), eventTone, jrv);
+  pco->OnDTMFToneChange(*sendTrack, eventTone, jrv);
 
   if (jrv.Failed()) {
     NS_WARNING("Failed to dispatch the RTCDTMFToneChange event!");
   }
 
   return NS_OK;
 }
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -3869,18 +3869,42 @@ pref("print.extend_native_print_dialog",
 pref("plugin.scan.plid.all", true);
 
 // Whether sending WM_MOUSEWHEEL and WM_MOUSEHWHEEL to plugins on Windows.
 pref("plugin.mousewheel.enabled", true);
 
 // Switch the keyboard layout per window
 pref("intl.keyboard.per_window_layout", false);
 
+// Whether Gecko sets input scope of the URL bar to IS_DEFAULT when black
+// listed IMEs are active.  If you use tablet mode mainly and you want to
+// use touch keyboard for URL when you set focus to the URL bar, you can
+// set this to false.  Then, you'll see, e.g., ".com" key on the keyboard.
+// However, if you set this to false, such IMEs set its open state to "closed"
+// when you set focus to the URL bar.  I.e., input mode is automatically
+// changed to English input mode.
+// Black listed IMEs:
+//   - Microsoft IME for Japanese
+//   - Google Japanese Input
+//   - Microsoft Bopomofo
+//   - Microsoft ChangJie
+//   - Microsoft Phonetic
+//   - Microsoft Quick
+//   - Microsoft New ChangJie
+//   - Microsoft New Phonetic
+//   - Microsoft New Quick
+//   - Microsoft Pinyin
+//   - Microsoft Pinyin New Experience Input Style
+//   - Microsoft Wubi
+//   - Microsoft IME for Korean (except on Win7)
+//   - Microsoft Old Hangul
+pref("intl.ime.hack.set_input_scope_of_url_bar_to_default", true);
+
 #ifdef NS_ENABLE_TSF
-// Enable/Disable TSF support on Vista or later.
+// Enable/Disable TSF support.
 pref("intl.tsf.enable", true);
 
 // Support IMEs implemented with IMM in TSF mode.
 pref("intl.tsf.support_imm", true);
 
 // This is referred only when both "intl.tsf.enable" and "intl.tsf.support_imm"
 // are true.  When this is true, default IMC is associated with focused window
 // only when active keyboard layout is a legacy IMM-IME.
--- a/security/manager/pki/resources/content/certManager.js
+++ b/security/manager/pki/resources/content/certManager.js
@@ -1,16 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 /* import-globals-from pippki.js */
 "use strict";
 
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-
 const nsIFilePicker = Components.interfaces.nsIFilePicker;
 const nsFilePicker = "@mozilla.org/filepicker;1";
 const nsIX509CertDB = Components.interfaces.nsIX509CertDB;
 const nsX509CertDB = "@mozilla.org/security/x509certdb;1";
 const nsIX509Cert = Components.interfaces.nsIX509Cert;
 const nsICertTree = Components.interfaces.nsICertTree;
 const nsCertTree = "@mozilla.org/security/nsCertTree;1";
 
--- a/security/manager/pki/resources/content/certViewer.js
+++ b/security/manager/pki/resources/content/certViewer.js
@@ -1,43 +1,36 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from pippki.js */
 "use strict";
 
 /**
  * @file Implements functionality for certViewer.xul and its tabs certDump.xul
  *       and viewCertDetails.xul: a dialog that allows various attributes of a
  *       certificate to be viewed.
  * @argument {nsISupports} window.arguments[0]
  *           The cert to view, queryable to nsIX509Cert.
  */
 
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
-
 const nsIX509Cert = Ci.nsIX509Cert;
 const nsX509CertDB = "@mozilla.org/security/x509certdb;1";
 const nsIX509CertDB = Ci.nsIX509CertDB;
 const nsPK11TokenDB = "@mozilla.org/security/pk11tokendb;1";
 const nsIPK11TokenDB = Ci.nsIPK11TokenDB;
 const nsIASN1Object = Ci.nsIASN1Object;
 const nsIASN1Sequence = Ci.nsIASN1Sequence;
 const nsIASN1PrintableItem = Ci.nsIASN1PrintableItem;
 const nsIASN1Tree = Ci.nsIASN1Tree;
 const nsASN1Tree = "@mozilla.org/security/nsASN1Tree;1";
 
 var bundle;
 
-function doPrompt(msg) {
-  let prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
-    getService(Components.interfaces.nsIPromptService);
-  prompts.alert(window, null, msg);
-}
-
 /**
  * Fills out the "Certificate Hierarchy" tree of the cert viewer "Details" tab.
  *
  * @param {tree} node
  *        Parent tree node to append to.
  * @param {Array} chain
  *        An array of nsIX509Cert where cert n is issued by cert n + 1.
  */
@@ -359,19 +352,17 @@ function DisplayGeneralDataFromCert(cert
   addAttributeFromCert("issuerorgunit", cert.issuerOrganizationUnit);
 }
 
 function updateCertDump() {
   var asn1Tree = document.getElementById("prettyDumpTree")
           .view.QueryInterface(nsIASN1Tree);
 
   var tree = document.getElementById("treesetDump");
-  if (tree.currentIndex < 0) {
-    doPrompt("No items are selected."); // This should never happen.
-  } else {
+  if (tree.currentIndex >= 0) {
     var item = tree.contentView.getItemAtIndex(tree.currentIndex);
     var dbKey = item.firstChild.firstChild.getAttribute("display");
     //  Get the cert from the cert database
     var certdb = Components.classes[nsX509CertDB].getService(nsIX509CertDB);
     var cert = certdb.findCertByDBKey(dbKey);
     asn1Tree.loadASN1Structure(cert.ASN1Structure);
   }
   displaySelected();
--- a/security/manager/pki/resources/content/certViewer.xul
+++ b/security/manager/pki/resources/content/certViewer.xul
@@ -15,19 +15,19 @@
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
   buttons="accept"
   buttonlabelaccept="&certmgr.close.label;"
   buttonaccesskeyaccept="&certmgr.close.accesskey;"
   onload="setWindowName();">
 
 <stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
 
+<script type="application/javascript" src="chrome://pippki/content/pippki.js"/>
 <script type="application/javascript"
         src="chrome://pippki/content/certViewer.js"/>
-<script type="application/javascript" src="chrome://pippki/content/pippki.js"/>
 
   <tabbox flex="1">
     <tabs>
       <tab id="general_tab" label="&certmgr.detail.general_tab.title;"
            accesskey="&certmgr.detail.general_tab.accesskey;"/>
       <tab id="prettyprint_tab" label="&certmgr.detail.prettyprint_tab.title;"
            accesskey="&certmgr.detail.prettyprint_tab.accesskey;"/>
     </tabs>
--- a/security/manager/pki/resources/content/changepassword.js
+++ b/security/manager/pki/resources/content/changepassword.js
@@ -1,29 +1,28 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 
 const nsIPK11TokenDB = Components.interfaces.nsIPK11TokenDB;
 const nsPKCS11ModuleDB = "@mozilla.org/security/pkcs11moduledb;1";
 const nsIPKCS11ModuleDB = Components.interfaces.nsIPKCS11ModuleDB;
 const nsIPKCS11Slot = Components.interfaces.nsIPKCS11Slot;
 const nsIPK11Token = Components.interfaces.nsIPK11Token;
 
 var params;
 var tokenName = "";
 var pw1;
 
 function doPrompt(msg) {
-  let prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
-    getService(Components.interfaces.nsIPromptService);
-  prompts.alert(window, null, msg);
+  Services.prompt.alert(window, null, msg);
 }
 
 function onLoad() {
   document.documentElement.getButton("accept").disabled = true;
 
   pw1 = document.getElementById("pw1");
   params = window.arguments[0].QueryInterface(Ci.nsIDialogParamBlock);
   tokenName = params.GetString(1);
--- a/security/manager/pki/resources/content/clientauthask.js
+++ b/security/manager/pki/resources/content/clientauthask.js
@@ -34,20 +34,16 @@
  * @property {Boolean} rememberSelection
  *           Set to true if the user wanted their cert selection to be
  *           remembered, false otherwise.
  * @property {Number} selectedIndex
  *           The index the chosen cert is at for the given cert list. Undefined
  *           value if |certChosen| is not true.
  */
 
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-
-const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
-
 /**
  * The pippki <stringbundle> element.
  * @type <stringbundle>
  */
 var bundle;
 /**
  * The array of certs the user can choose from.
  * @type nsIArray<nsIX509Cert>
--- a/security/manager/pki/resources/content/deletecert.js
+++ b/security/manager/pki/resources/content/deletecert.js
@@ -18,18 +18,16 @@
 /**
  * @typedef DeleteCertReturnValues
  * @type Object
  * @property {Boolean} deleteConfirmed
  *           Set to true if the user confirmed deletion of the given certs,
  *           false otherwise.
  */
 
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-
 /**
  * Returns the most appropriate string to represent the given nsICertTreeItem.
  * @param {nsICertTreeItem} certTreeItem
  *        The item to represent.
  * @returns {String}
  *          A representative string.
  */
 function certTreeItemToString(certTreeItem) {
--- a/security/manager/pki/resources/content/deletecert.xul
+++ b/security/manager/pki/resources/content/deletecert.xul
@@ -12,17 +12,17 @@
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
   onload="onLoad();"
   buttons="accept,cancel"
   ondialogaccept="return onDialogAccept();"
   ondialogcancel="return onDialogCancel();">
 
   <stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
 
+  <script type="application/javascript" src="pippki.js" />
   <script type="application/javascript" src="chrome://pippki/content/deletecert.js"/>
-  <script type="application/javascript" src="pippki.js" />
 
   <description id="confirm" style="width: 400px;"/>
   <richlistbox id="certlist" class="box-padded" flex="1"
                style="min-height: 8em; height: 8em; min-width: 35em;"/>
   <description id="impact" style="width: 400px;"/>
 
 </dialog>
--- a/security/manager/pki/resources/content/device_manager.js
+++ b/security/manager/pki/resources/content/device_manager.js
@@ -1,13 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+
 const nsIPKCS11Slot = Components.interfaces.nsIPKCS11Slot;
 const nsIPKCS11Module = Components.interfaces.nsIPKCS11Module;
 const nsPKCS11ModuleDB = "@mozilla.org/security/pkcs11moduledb;1";
 const nsIPKCS11ModuleDB = Components.interfaces.nsIPKCS11ModuleDB;
 const nsIPK11Token = Components.interfaces.nsIPK11Token;
 const nsPK11TokenDB = "@mozilla.org/security/pk11tokendb;1";
 const nsIPK11TokenDB = Components.interfaces.nsIPK11TokenDB;
 const nsIDialogParamBlock = Components.interfaces.nsIDialogParamBlock;
@@ -24,25 +27,21 @@ function LoadModules() {
   RefreshDeviceList();
 }
 
 function getNSSString(name) {
   return document.getElementById("pipnss_bundle").getString(name);
 }
 
 function doPrompt(msg) {
-  let prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
-    getService(Components.interfaces.nsIPromptService);
-  prompts.alert(window, null, msg);
+  Services.prompt.alert(window, null, msg);
 }
 
 function doConfirm(msg) {
-  let prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
-    getService(Components.interfaces.nsIPromptService);
-  return prompts.confirm(window, null, msg);
+  return Services.prompt.confirm(window, null, msg);
 }
 
 function RefreshDeviceList() {
   let modules = secmoddb.listModules();
   while (modules.hasMoreElements()) {
     let module = modules.getNext().QueryInterface(nsIPKCS11Module);
     let slotnames = [];
     let slots = module.listSlots();
--- a/security/manager/pki/resources/content/downloadcert.js
+++ b/security/manager/pki/resources/content/downloadcert.js
@@ -24,18 +24,16 @@
  * @property {Boolean} trustForSSL
  *           Set to true if the cert should be trusted for SSL, false otherwise.
  *           Undefined value if |importConfirmed| is not true.
  * @property {Boolean} trustForEmail
  *           Set to true if the cert should be trusted for e-mail, false
  *           otherwise. Undefined value if |importConfirmed| is not true.
  */
 
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-
 /**
  * The cert to potentially import.
  * @type nsIX509Cert
  */
 var gCert;
 
 /**
  * onload() handler.
--- a/security/manager/pki/resources/content/downloadcert.xul
+++ b/security/manager/pki/resources/content/downloadcert.xul
@@ -13,18 +13,18 @@
         style="width: 46em;"
         buttons="accept,cancel"
         ondialogaccept="return onDialogAccept();"
         ondialogcancel="return onDialogCancel();"
         onload="onLoad();">
 
 <stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
 
+<script type="application/javascript" src="chrome://pippki/content/pippki.js"/>
 <script type="application/javascript" src="chrome://pippki/content/downloadcert.js"/>
-<script type="application/javascript" src="chrome://pippki/content/pippki.js"/>
 
 
   <!--  Let 'em know what they're doing -->
   <vbox>
     <description>&downloadCert.message1;</description>
   </vbox>
 
   <separator/>
--- a/security/manager/pki/resources/content/editcacert.js
+++ b/security/manager/pki/resources/content/editcacert.js
@@ -1,16 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 /* import-globals-from pippki.js */
 "use strict";
 
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-
 var gCertDB = Cc["@mozilla.org/security/x509certdb;1"]
                 .getService(Ci.nsIX509CertDB);
 /**
  * Cert to edit the trust of.
  * @type nsIX509Cert
  */
 var gCert;
 
--- a/security/manager/pki/resources/content/exceptionDialog.js
+++ b/security/manager/pki/resources/content/exceptionDialog.js
@@ -44,19 +44,17 @@ badCertListener.prototype = {
   }
 };
 
 function initExceptionDialog() {
   gNeedReset = false;
   gDialog = document.documentElement;
   gBundleBrand = document.getElementById("brand_bundle");
   gPKIBundle = document.getElementById("pippki_bundle");
-  gSecHistogram = Components.classes["@mozilla.org/base/telemetry;1"].
-                    getService(Components.interfaces.nsITelemetry).
-                    getHistogramById("SECURITY_UI");
+  gSecHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
   gNsISecTel = Components.interfaces.nsISecurityUITelemetry;
 
   var brandName = gBundleBrand.getString("brandShortName");
   setText("warningText", gPKIBundle.getFormattedString("addExceptionBrandedWarning2", [brandName]));
   gDialog.getButton("extra1").disabled = true;
 
   var args = window.arguments;
   if (args && args[0]) {
@@ -138,20 +136,18 @@ function checkCert() {
  * @returns {nsIURI}
  *          URI constructed from the information supplied on success, null
  *          otherwise.
  */
 function getURI() {
   // Use fixup service instead of just ioservice's newURI since it's quite
   // likely that the host will be supplied without a protocol prefix, resulting
   // in malformed uri exceptions being thrown.
-  let fus = Components.classes["@mozilla.org/docshell/urifixup;1"]
-                      .getService(Components.interfaces.nsIURIFixup);
   let locationTextBox = document.getElementById("locationTextBox");
-  let uri = fus.createFixupURI(locationTextBox.value, 0);
+  let uri = Services.uriFixup.createFixupURI(locationTextBox.value, 0);
 
   if (!uri) {
     return null;
   }
 
   if (uri.scheme == "http") {
     uri.scheme = "https";
   }
@@ -259,19 +255,17 @@ function updateCertStatus() {
       document.getElementById("permanent").disabled = true;
     }
 
     // We're done checking the certificate, so allow the user to check it again.
     document.getElementById("checkCertButton").disabled = false;
     document.getElementById("viewCertButton").disabled = false;
 
     // Notify observers about the availability of the certificate
-    Components.classes["@mozilla.org/observer-service;1"]
-              .getService(Components.interfaces.nsIObserverService)
-              .notifyObservers(null, "cert-exception-ui-ready");
+    Services.obs.notifyObservers(null, "cert-exception-ui-ready");
   } else if (gChecking) {
     shortDesc = "addExceptionCheckingShort";
     longDesc  = "addExceptionCheckingLong2";
     // We're checking the certificate, so we disable the Get Certificate
     // button to make sure that the user can't interrupt the process and
     // trigger another certificate fetch.
     document.getElementById("checkCertButton").disabled = true;
     document.getElementById("viewCertButton").disabled = true;
--- a/security/manager/pki/resources/content/load_device.js
+++ b/security/manager/pki/resources/content/load_device.js
@@ -4,20 +4,16 @@
 /* import-globals-from pippki.js */
 "use strict";
 
 /**
  * @file Implements the functionality of load_device.xul: a dialog that allows
  *       a PKCS #11 module to be loaded into Firefox.
  */
 
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-
-const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
-
 function onBrowseBtnPress() {
   let bundle = document.getElementById("pippki_bundle");
   let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
   fp.init(window, bundle.getString("loadPK11ModuleFilePickerTitle"),
           Ci.nsIFilePicker.modeOpen);
   fp.appendFilters(Ci.nsIFilePicker.filterAll);
   fp.open(rv => {
     if (rv == Ci.nsIFilePicker.returnOK) {
--- a/security/manager/pki/resources/content/pippki.js
+++ b/security/manager/pki/resources/content/pippki.js
@@ -5,16 +5,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 /*
  * These are helper functions to be included
  * pippki UI js files.
  */
 
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+
 function setText(id, value) {
   let element = document.getElementById(id);
   if (!element) {
     return;
   }
   if (element.hasChildNodes()) {
     element.firstChild.remove();
   }
@@ -59,16 +62,19 @@ function getPEMString(cert) {
   // (as specified in RFC 1421).
   var wrapped = derb64.replace(/(\S{64}(?!$))/g, "$1\r\n");
   return "-----BEGIN CERTIFICATE-----\r\n"
          + wrapped
          + "\r\n-----END CERTIFICATE-----\r\n";
 }
 
 function alertPromptService(title, message) {
+  // XXX Bug 1425832 - Using Services.prompt here causes tests to report memory
+  // leaks.
+  // eslint-disable-next-line mozilla/use-services
   var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].
            getService(Components.interfaces.nsIPromptService);
   ps.alert(window, title, message);
 }
 
 const DEFAULT_CERT_EXTENSION = "crt";
 
 /**
--- a/security/manager/pki/resources/content/resetpassword.js
+++ b/security/manager/pki/resources/content/resetpassword.js
@@ -6,19 +6,17 @@
 
 function resetPassword() {
   var pk11db = Components.classes["@mozilla.org/security/pk11tokendb;1"]
                                  .getService(Components.interfaces.nsIPK11TokenDB);
   var token = pk11db.getInternalKeyToken();
   token.reset();
 
   try {
-    var loginManager = Components.classes["@mozilla.org/login-manager;1"].
-                       getService(Components.interfaces.nsILoginManager);
-    loginManager.removeAllLogins();
+    Services.logins.removeAllLogins();
   } catch (e) {
   }
 
   var bundle = document.getElementById("pippki_bundle");
   var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService();
   promptService = promptService.QueryInterface(Components.interfaces.nsIPromptService);
   if (promptService && bundle) {
     promptService.alert(window,
--- a/security/manager/ssl/tests/mochitest/browser/browser_loadPKCS11Module_ui.js
+++ b/security/manager/ssl/tests/mochitest/browser/browser_loadPKCS11Module_ui.js
@@ -1,16 +1,17 @@
 // Any copyright is dedicated to the Public Domain.
 // http://creativecommons.org/publicdomain/zero/1.0/
 "use strict";
 
 // Tests the dialog used for loading PKCS #11 modules.
 
 const { MockRegistrar } =
   Cu.import("resource://testing-common/MockRegistrar.jsm", {});
+const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 
 const gMockPKCS11ModuleDB = {
   addModuleCallCount: 0,
   expectedLibPath: "",
   expectedModuleName: "",
   throwOnAddModule: false,
 
   addModule(moduleName, libraryFullPath, cryptoMechanismFlags, cipherFlags) {
@@ -90,19 +91,17 @@ var gMockPKCS11CID =
                          gMockPKCS11ModuleDB);
 var gMockPromptServiceCID =
   MockRegistrar.register("@mozilla.org/embedcomp/prompt-service;1",
                          gMockPromptService);
 
 var gMockFilePicker = SpecialPowers.MockFilePicker;
 gMockFilePicker.init(window);
 
-var gTempFile = Cc["@mozilla.org/file/directory_service;1"]
-                  .getService(Ci.nsIProperties)
-                  .get("TmpD", Ci.nsIFile);
+var gTempFile = Services.dirsvc.get("TmpD", Ci.nsIFile);
 gTempFile.append("browser_loadPKCS11Module_ui-fakeModule");
 
 registerCleanupFunction(() => {
   gMockFilePicker.cleanup();
   MockRegistrar.unregister(gMockPKCS11CID);
   MockRegistrar.unregister(gMockPromptServiceCID);
 });
 
--- a/security/manager/ssl/tests/mochitest/mixedcontent/test_bug383369.html
+++ b/security/manager/ssl/tests/mochitest/mixedcontent/test_bug383369.html
@@ -15,36 +15,33 @@
   // We want to start this test from an insecure context
   loadAsInsecure = true;
   // We don't want to go through the navigation back/forward test
   bypassNavigationTest = true;
 
   function runTest() {
     let script = SpecialPowers.loadChromeScript(function() {
       const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+      const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
       // Force download to be w/o user assistance for our testing mime type
       const mimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
       let handlerInfo =
         mimeSvc.getFromTypeAndExtension("application/x-auto-download", "auto");
       handlerInfo.preferredAction = Ci.nsIHandlerInfo.saveToDisk;
       handlerInfo.alwaysAskBeforeHandling = false;
       handlerInfo.preferredApplicationHandler = null;
 
       const handlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"]
                            .getService(Ci.nsIHandlerService);
       handlerSvc.store(handlerInfo);
 
-      let dirProvider = Cc["@mozilla.org/file/directory_service;1"]
-                          .getService(Ci.nsIProperties);
-      let profileDir = dirProvider.get("ProfDS", Ci.nsIFile);
+      let profileDir = Services.dirsvc.get("ProfDS", Ci.nsIFile);
       profileDir.append("downloads");
 
-      let prefs = Cc["@mozilla.org/preferences-service;1"]
-                    .getService(Ci.nsIPrefService);
-      let prefBranch = prefs.getBranch("browser.download.");
+      let prefBranch = Services.prefs.getBranch("browser.download.");
 
       prefBranch.setCharPref("dir", profileDir.path);
       prefBranch.setBoolPref("useDownloadDir", true);
       prefBranch.setIntPref("folderList", 2);
 
       const { Downloads } =
         Cu.import("resource://gre/modules/Downloads.jsm", {});
       Downloads.getList(Downloads.PUBLIC).then(list => {
@@ -64,28 +61,27 @@
       window.location = url;
     });
   }
 
   function afterNavigationTest() {}
 
   testCleanUp = function cleanup() {
     SpecialPowers.loadChromeScript(function() {
-      const { classes: Cc, interfaces: Ci } = Components;
+      const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+      const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
       const mimeSvc = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
       let handlerInfo =
         mimeSvc.getFromTypeAndExtension("application/x-auto-download", "auto");
 
       const handlerSvc = Cc["@mozilla.org/uriloader/handler-service;1"]
                            .getService(Ci.nsIHandlerService);
       handlerSvc.remove(handlerInfo);
 
-      let prefs = Cc["@mozilla.org/preferences-service;1"]
-                    .getService(Ci.nsIPrefService);
-      let prefBranch = prefs.getBranch("browser.download.");
+      let prefBranch = Services.prefs.getBranch("browser.download.");
 
       const prefKeys = ["dir", "useDownloadDir", "folderList"];
       for (let prefKey of prefKeys) {
         if (prefBranch.prefHasUserValue(prefKey)) {
           prefBranch.clearUserPref(prefKey);
         }
       }
     });
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -371,19 +371,17 @@ function add_tls_server_setup(serverBinN
 function add_connection_test(aHost, aExpectedResult,
                              aBeforeConnect, aWithSecurityInfo,
                              aAfterStreamOpen,
                              /* optional */ aOriginAttributes) {
   const REMOTE_PORT = 8443;
 
   function Connection(host) {
     this.host = host;
-    let threadManager = Cc["@mozilla.org/thread-manager;1"]
-                          .getService(Ci.nsIThreadManager);
-    this.thread = threadManager.currentThread;
+    this.thread = Services.tm.currentThread;
     this.defer = Promise.defer();
     let sts = Cc["@mozilla.org/network/socket-transport-service;1"]
                 .getService(Ci.nsISocketTransportService);
     this.transport = sts.createTransport(["ssl"], 1, host, REMOTE_PORT, null);
     // See bug 1129771 - attempting to connect to [::1] when the server is
     // listening on 127.0.0.1 causes frequent failures on OS X 10.10.
     this.transport.connectionFlags |= Ci.nsISocketTransport.DISABLE_IPV6;
     this.transport.setEventSink(this, this.thread);
@@ -466,25 +464,22 @@ function add_connection_test(aHost, aExp
                               .QueryInterface(Ci.nsITransportSecurityInfo));
       }
       run_next_test();
     });
   });
 }
 
 function _getBinaryUtil(binaryUtilName) {
-  let directoryService = Cc["@mozilla.org/file/directory_service;1"]
-                           .getService(Ci.nsIProperties);
-
-  let utilBin = directoryService.get("CurProcD", Ci.nsIFile);
+  let utilBin = Services.dirsvc.get("CurProcD", Ci.nsIFile);
   utilBin.append(binaryUtilName + mozinfo.bin_suffix);
   // If we're testing locally, the above works. If not, the server executable
   // is in another location.
   if (!utilBin.exists()) {
-    utilBin = directoryService.get("CurWorkD", Ci.nsIFile);
+    utilBin = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
     while (utilBin.path.indexOf("xpcshell") != -1) {
       utilBin = utilBin.parent;
     }
     utilBin.append("bin");
     utilBin.append(binaryUtilName + mozinfo.bin_suffix);
   }
   // But maybe we're on Android or B2G, where binaries are in /data/local/xpcb.
   if (!utilBin.exists()) {
@@ -499,21 +494,19 @@ function _getBinaryUtil(binaryUtilName) 
 function _setupTLSServerTest(serverBinName, certsPath) {
   let certdb = Cc["@mozilla.org/security/x509certdb;1"]
                   .getService(Ci.nsIX509CertDB);
   // The trusted CA that is typically used for "good" certificates.
   addCertFromFile(certdb, `${certsPath}/test-ca.pem`, "CTu,u,u");
 
   const CALLBACK_PORT = 8444;
 
-  let directoryService = Cc["@mozilla.org/file/directory_service;1"]
-                           .getService(Ci.nsIProperties);
   let envSvc = Cc["@mozilla.org/process/environment;1"]
                  .getService(Ci.nsIEnvironment);
-  let greBinDir = directoryService.get("GreBinD", Ci.nsIFile);
+  let greBinDir = Services.dirsvc.get("GreBinD", Ci.nsIFile);
   envSvc.set("DYLD_LIBRARY_PATH", greBinDir.path);
   // TODO(bug 1107794): Android libraries are in /data/local/xpcb, but "GreBinD"
   // does not return this path on Android, so hard code it here.
   envSvc.set("LD_LIBRARY_PATH", greBinDir.path + ":/data/local/xpcb");
   envSvc.set("MOZ_TLS_SERVER_DEBUG_LEVEL", "3");
   envSvc.set("MOZ_TLS_SERVER_CALLBACK_PORT", CALLBACK_PORT);
 
   let httpServer = new HttpServer();
@@ -527,17 +520,17 @@ function _setupTLSServerTest(serverBinNa
           httpServer.stop(run_next_test);
         });
       });
   httpServer.start(CALLBACK_PORT);
 
   let serverBin = _getBinaryUtil(serverBinName);
   let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
   process.init(serverBin);
-  let certDir = directoryService.get("CurWorkD", Ci.nsIFile);
+  let certDir = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
   certDir.append(`${certsPath}`);
   Assert.ok(certDir.exists(), `certificate folder (${certsPath}) should exist`);
   // Using "sql:" causes the SQL DB to be used so we can run tests on Android.
   process.run(false, [ "sql:" + certDir.path ], 1);
 
   registerCleanupFunction(function() {
     process.kill();
   });
--- a/security/manager/ssl/tests/unit/test_cert_overrides.js
+++ b/security/manager/ssl/tests/unit/test_cert_overrides.js
@@ -9,18 +9,17 @@
 // 1. Attempt to connect to the given host. This should fail with the
 //    given error and override bits.
 // 2. Add an override for that host/port/certificate/override bits.
 // 3. Connect again. This should succeed.
 
 do_get_profile();
 
 function check_telemetry() {
-  let histogram = Cc["@mozilla.org/base/telemetry;1"]
-                    .getService(Ci.nsITelemetry)
+  let histogram = Services.telemetry
                     .getHistogramById("SSL_CERT_ERROR_OVERRIDES")
                     .snapshot();
   equal(histogram.counts[0], 0, "Should have 0 unclassified counts");
   equal(histogram.counts[2], 9,
         "Actual and expected SEC_ERROR_UNKNOWN_ISSUER counts should match");
   equal(histogram.counts[3], 1,
         "Actual and expected SEC_ERROR_CA_CERT_INVALID counts should match");
   equal(histogram.counts[4], 0,
@@ -47,18 +46,17 @@ function check_telemetry() {
         "Actual and expected MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE counts should match");
   equal(histogram.counts[15], 1,
         "Actual and expected MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE counts should match");
   equal(histogram.counts[16], 2,
         "Actual and expected SEC_ERROR_INVALID_TIME counts should match");
   equal(histogram.counts[17], 1,
         "Actual and expected MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME counts should match");
 
-  let keySizeHistogram = Cc["@mozilla.org/base/telemetry;1"]
-                           .getService(Ci.nsITelemetry)
+  let keySizeHistogram = Services.telemetry
                            .getHistogramById("CERT_CHAIN_KEY_SIZE_STATUS")
                            .snapshot();
   equal(keySizeHistogram.counts[0], 0,
         "Actual and expected unchecked key size counts should match");
   equal(keySizeHistogram.counts[1], 16,
         "Actual and expected successful verifications of 2048-bit keys should match");
   equal(keySizeHistogram.counts[2], 0,
         "Actual and expected successful verifications of 1024-bit keys should match");
--- a/security/manager/ssl/tests/unit/test_ocsp_stapling.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_stapling.js
@@ -160,18 +160,17 @@ function add_tests() {
   add_ocsp_test("keysize-ocsp-delegated.example.com",
                 SEC_ERROR_OCSP_INVALID_SIGNING_CERT, true, true);
 
   add_ocsp_test("revoked-ca-cert-used-as-end-entity.example.com",
                 SEC_ERROR_REVOKED_CERTIFICATE, true);
 }
 
 function check_ocsp_stapling_telemetry() {
-  let histogram = Cc["@mozilla.org/base/telemetry;1"]
-                    .getService(Ci.nsITelemetry)
+  let histogram = Services.telemetry
                     .getHistogramById("SSL_OCSP_STAPLING")
                     .snapshot();
   equal(histogram.counts[0], 0,
         "Should have 0 connections for unused histogram bucket 0");
   equal(histogram.counts[1], 5,
         "Actual and expected connections with a good response should match");
   equal(histogram.counts[2], 18,
         "Actual and expected connections with no stapled response should match");
--- a/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js
+++ b/security/manager/ssl/tests/unit/test_ocsp_stapling_expired.js
@@ -188,18 +188,17 @@ function run_test() {
                 PRErrorCodeSuccess, ocspResponseGoodMustStaple, willNotRetry);
 
   add_test(function () { ocspResponder.stop(run_next_test); });
   add_test(check_ocsp_stapling_telemetry);
   run_next_test();
 }
 
 function check_ocsp_stapling_telemetry() {
-  let histogram = Cc["@mozilla.org/base/telemetry;1"]
-                    .getService(Ci.nsITelemetry)
+  let histogram = Services.telemetry
                     .getHistogramById("SSL_OCSP_STAPLING")
                     .snapshot();
   equal(histogram.counts[0], 0,
         "Should have 0 connections for unused histogram bucket 0");
   equal(histogram.counts[1], 0,
         "Actual and expected connections with a good response should match");
   equal(histogram.counts[2], 0,
         "Actual and expected connections with no stapled response should match");
--- a/security/manager/ssl/tests/unit/test_pinning.js
+++ b/security/manager/ssl/tests/unit/test_pinning.js
@@ -202,47 +202,46 @@ function test_enforce_test_mode() {
   add_prevented_cert_override_test(
     "unknownissuer.test-mode.pinning.example.com",
     Ci.nsICertOverrideService.ERROR_UNTRUSTED,
     SEC_ERROR_UNKNOWN_ISSUER);
   add_clear_override("unknownissuer.test-mode.pinning.example.com");
 }
 
 function check_pinning_telemetry() {
-  let service = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
-  let prod_histogram = service.getHistogramById("CERT_PINNING_RESULTS")
+  let prod_histogram = Services.telemetry.getHistogramById("CERT_PINNING_RESULTS")
                          .snapshot();
-  let test_histogram = service.getHistogramById("CERT_PINNING_TEST_RESULTS")
+  let test_histogram = Services.telemetry.getHistogramById("CERT_PINNING_TEST_RESULTS")
                          .snapshot();
   // Because all of our test domains are pinned to user-specified trust
   // anchors, effectively only strict mode and enforce test-mode get evaluated
   equal(prod_histogram.counts[0], 4,
         "Actual and expected prod (non-Mozilla) failure count should match");
   equal(prod_histogram.counts[1], 4,
         "Actual and expected prod (non-Mozilla) success count should match");
   equal(test_histogram.counts[0], 2,
         "Actual and expected test (non-Mozilla) failure count should match");
   equal(test_histogram.counts[1], 0,
         "Actual and expected test (non-Mozilla) success count should match");
 
-  let moz_prod_histogram = service.getHistogramById("CERT_PINNING_MOZ_RESULTS")
+  let moz_prod_histogram = Services.telemetry.getHistogramById("CERT_PINNING_MOZ_RESULTS")
                              .snapshot();
   let moz_test_histogram =
-    service.getHistogramById("CERT_PINNING_MOZ_TEST_RESULTS").snapshot();
+    Services.telemetry.getHistogramById("CERT_PINNING_MOZ_TEST_RESULTS").snapshot();
   equal(moz_prod_histogram.counts[0], 0,
         "Actual and expected prod (Mozilla) failure count should match");
   equal(moz_prod_histogram.counts[1], 0,
         "Actual and expected prod (Mozilla) success count should match");
   equal(moz_test_histogram.counts[0], 0,
         "Actual and expected test (Mozilla) failure count should match");
   equal(moz_test_histogram.counts[1], 0,
         "Actual and expected test (Mozilla) success count should match");
 
   let per_host_histogram =
-    service.getHistogramById("CERT_PINNING_MOZ_RESULTS_BY_HOST").snapshot();
+    Services.telemetry.getHistogramById("CERT_PINNING_MOZ_RESULTS_BY_HOST").snapshot();
   equal(per_host_histogram.counts[0], 0,
         "Actual and expected per host (Mozilla) failure count should match");
   equal(per_host_histogram.counts[1], 2,
         "Actual and expected per host (Mozilla) success count should match");
   run_next_test();
 }
 
 function run_test() {
--- a/security/manager/ssl/tests/unit/test_pkcs11_module.js
+++ b/security/manager/ssl/tests/unit/test_pkcs11_module.js
@@ -61,19 +61,17 @@ function checkTestModuleExists() {
 function checkModuleTelemetry(additionalExpectedModule = undefined) {
   let expectedModules = [
     "NSS Internal PKCS #11 Module",
   ];
   if (additionalExpectedModule) {
     expectedModules.push(additionalExpectedModule);
   }
   expectedModules.sort();
-  let telemetryService = Cc["@mozilla.org/base/telemetry;1"]
-                           .getService(Ci.nsITelemetry);
-  let telemetry = telemetryService.snapshotKeyedScalars(
+  let telemetry = Services.telemetry.snapshotKeyedScalars(
     Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT).parent;
   let moduleTelemetry = telemetry["security.pkcs11_modules_loaded"];
   let actualModules = [];
   Object.keys(moduleTelemetry).forEach((key) => {
     ok(moduleTelemetry[key], "each keyed scalar should be true");
     actualModules.push(key);
   });
   actualModules.sort();
--- a/security/manager/ssl/tests/unit/test_pkcs11_token.js
+++ b/security/manager/ssl/tests/unit/test_pkcs11_token.js
@@ -13,20 +13,18 @@
 //      We want to test such functionality.
 //   3. Using the internal token lets us actually test the internal token works
 //      as expected.
 
 // Ensure that the appropriate initialization has happened.
 do_get_profile();
 
 function checkBasicAttributes(token) {
-  let strBundleSvc = Cc["@mozilla.org/intl/stringbundle;1"]
-                       .getService(Ci.nsIStringBundleService);
   let bundle =
-    strBundleSvc.createBundle("chrome://pipnss/locale/pipnss.properties");
+    Services.strings.createBundle("chrome://pipnss/locale/pipnss.properties");
 
   let expectedTokenName = bundle.GetStringFromName("PrivateTokenDescription");
   equal(token.tokenName, expectedTokenName,
         "Actual and expected name should match");
   equal(token.tokenLabel, expectedTokenName,
         "Actual and expected label should match");
   equal(token.tokenManID, bundle.GetStringFromName("ManufacturerID"),
         "Actual and expected manufacturer ID should match");
--- a/security/manager/tools/getHSTSPreloadList.js
+++ b/security/manager/tools/getHSTSPreloadList.js
@@ -342,19 +342,17 @@ function getHSTSStatuses(inHosts, outSta
     }
   }
 }
 
 // Since all events are processed on the main thread, and since event
 // handlers are not preemptible, there shouldn't be any concurrency issues.
 function waitForAResponse(outputList) {
   // From <https://developer.mozilla.org/en/XPConnect/xpcshell/HOWTO>
-  var threadManager = Cc["@mozilla.org/thread-manager;1"]
-                      .getService(Ci.nsIThreadManager);
-  threadManager.spinEventLoopUntil(() => outputList.length != 0);
+  Services.tm.spinEventLoopUntil(() => outputList.length != 0);
 }
 
 function readCurrentList(filename) {
   var currentHosts = {};
   var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
   file.initWithPath(filename);
   var fis = Cc["@mozilla.org/network/file-input-stream;1"]
               .createInstance(Ci.nsILineInputStream);
--- a/security/sandbox/test/browser_content_sandbox_fs.js
+++ b/security/sandbox/test/browser_content_sandbox_fs.js
@@ -1,16 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
  /* import-globals-from browser_content_sandbox_utils.js */
  "use strict";
 
-var prefs = Cc["@mozilla.org/preferences-service;1"]
-            .getService(Ci.nsIPrefBranch);
-
 Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/" +
     "security/sandbox/test/browser_content_sandbox_utils.js", this);
 
 const FONT_EXTENSIONS = [ "otf", "ttf", "ttc", "otc", "dfont" ];
 
 /*
  * This test exercises file I/O from web and file content processes using
  * OS.File methods to validate that calls that are meant to be blocked by
@@ -185,17 +182,17 @@ add_task(async function() {
   let prefExists = true;
 
   // Read the security.sandbox.content.level pref.
   // If the pref isn't set and we're running on Linux on !isNightly(),
   // exit without failing. The Linux content sandbox is only enabled
   // on Nightly at this time.
   // eslint-disable-next-line mozilla/use-default-preference-values
   try {
-    level = prefs.getIntPref("security.sandbox.content.level");
+    level = Services.prefs.getIntPref("security.sandbox.content.level");
   } catch (e) {
     prefExists = false;
   }
 
   ok(prefExists, "pref security.sandbox.content.level exists");
   if (!prefExists) {
     return;
   }
@@ -302,31 +299,31 @@ function getBadFontTestPaths(baseDir) {
 
 // Test reading files and dirs from web and file content processes.
 async function testFileAccess() {
   // for tests that run in a web content process
   let webBrowser = gBrowser.selectedBrowser;
 
   // Ensure that the file content process is enabled.
   let fileContentProcessEnabled =
-    prefs.getBoolPref("browser.tabs.remote.separateFileUriProcess");
+    Services.prefs.getBoolPref("browser.tabs.remote.separateFileUriProcess");
   ok(fileContentProcessEnabled, "separate file content process is enabled");
 
   // for tests that run in a file content process
   let fileBrowser = undefined;
   if (fileContentProcessEnabled) {
     // open a tab in a file content process
     gBrowser.selectedTab =
       BrowserTestUtils.addTab(gBrowser, "about:blank", {preferredRemoteType: "file"});
     // get the browser for the file content process tab
     fileBrowser = gBrowser.getBrowserForTab(gBrowser.selectedTab);
   }
 
   // Current level
-  let level = prefs.getIntPref("security.sandbox.content.level");
+  let level = Services.prefs.getIntPref("security.sandbox.content.level");
 
   // Directories/files to test accessing from content processes.
   // For directories, we test whether a directory listing is allowed
   // or blocked. For files, we test if we can read from the file.
   // Each entry in the array represents a test file or directory
   // that will be read from either a web or file process.
   let tests = [];
 
--- a/security/sandbox/test/browser_content_sandbox_syscalls.js
+++ b/security/sandbox/test/browser_content_sandbox_syscalls.js
@@ -1,16 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
  /* import-globals-from browser_content_sandbox_utils.js */
 "use strict";
 
-var prefs = Cc["@mozilla.org/preferences-service;1"]
-            .getService(Ci.nsIPrefBranch);
-
 Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/" +
     "security/sandbox/test/browser_content_sandbox_utils.js", this);
 
 /*
  * This test is for executing system calls in content processes to validate
  * that calls that are meant to be blocked by content sandboxing are blocked.
  * We use the term system calls loosely so that any OS API call such as
  * fopen could be included.
@@ -147,17 +144,17 @@ add_task(async function() {
   let prefExists = true;
 
   // Read the security.sandbox.content.level pref.
   // If the pref isn't set and we're running on Linux on !isNightly(),
   // exit without failing. The Linux content sandbox is only enabled
   // on Nightly at this time.
   // eslint-disable-next-line mozilla/use-default-preference-values
   try {
-    level = prefs.getIntPref("security.sandbox.content.level");
+    level = Services.prefs.getIntPref("security.sandbox.content.level");
   } catch (e) {
     prefExists = false;
   }
 
   ok(prefExists, "pref security.sandbox.content.level exists");
   if (!prefExists) {
     return;
   }
--- a/security/sandbox/test/browser_content_sandbox_utils.js
+++ b/security/sandbox/test/browser_content_sandbox_utils.js
@@ -11,18 +11,17 @@ const environment = Cc["@mozilla.org/pro
  * Utility functions for the browser content sandbox tests.
  */
 
 function isMac() { return Services.appinfo.OS == "Darwin"; }
 function isWin() { return Services.appinfo.OS == "WINNT"; }
 function isLinux() { return Services.appinfo.OS == "Linux"; }
 
 function isNightly() {
-  let version = SpecialPowers.Cc["@mozilla.org/xre/app-info;1"].
-    getService(SpecialPowers.Ci.nsIXULAppInfo).version;
+  let version = SpecialPowers.Services.appinfo.version;
   return (version.endsWith("a1"));
 }
 
 function uuid() {
   return uuidGenerator.generateUUID().toString();
 }
 
 // Returns a file object for a new file in the home dir ($HOME/<UUID>).
--- a/services/sync/modules-testing/utils.js
+++ b/services/sync/modules-testing/utils.js
@@ -13,16 +13,17 @@ this.EXPORTED_SYMBOLS = [
   "SyncTestingInfrastructure",
   "waitForZeroTimer",
   "promiseZeroTimer",
   "promiseNamedTimer",
   "MockFxaStorageManager",
   "AccountState", // from a module import
   "sumHistogram",
   "getLoginTelemetryScalar",
+  "syncTestLogging",
 ];
 
 var {utils: Cu} = Components;
 
 Cu.import("resource://services-sync/status.js");
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://services-crypto/utils.js");
 Cu.import("resource://services-sync/util.js");
@@ -225,24 +226,31 @@ this.configureIdentity = async function(
   await ns.Service.identity.whenReadyToAuthenticate.promise;
   // and cheat to avoid requiring each test do an explicit login - give it
   // a cluster URL.
   if (config.fxaccount.token.endpoint) {
     ns.Service.clusterURL = config.fxaccount.token.endpoint;
   }
 };
 
+function syncTestLogging(level = "Trace") {
+  let logStats = initTestLogging(level);
+  Services.prefs.setStringPref("services.sync.log.logger", level);
+  Services.prefs.setStringPref("services.sync.log.logger.engine", "");
+  return logStats;
+}
+
 this.SyncTestingInfrastructure = async function(server, username) {
   let ns = {};
   Cu.import("resource://services-sync/service.js", ns);
 
   let config = makeIdentityConfig({ username });
   await configureIdentity(config, server);
   return {
-    logStats: initTestLogging(),
+    logStats: syncTestLogging(),
     fakeFilesystem: new FakeFilesystemService({}),
     fakeGUIDService: new FakeGUIDService(),
     fakeCryptoService: new FakeCryptoService(),
   };
 };
 
 /**
  * Turn WBO cleartext into fake "encrypted" payload as it goes over the wire.
--- a/services/sync/tests/unit/head_errorhandler_common.js
+++ b/services/sync/tests/unit/head_errorhandler_common.js
@@ -100,16 +100,17 @@ const EHTestsCommon = {
     let newSyncKeyBundle = new BulkKeyBundle("crypto");
     await newSyncKeyBundle.generateRandom();
     let keys = Service.collectionKeys.asWBO();
     await keys.encrypt(newSyncKeyBundle);
     return keys.upload(Service.resource(Service.cryptoKeysURL));
   },
 
   async setUp(server) {
+    syncTestLogging();
     await configureIdentity({ username: "johndoe" }, server);
     return EHTestsCommon.generateAndUploadKeys();
   },
 
   async generateAndUploadKeys() {
     await generateNewKeys(Service.collectionKeys);
     let serverKeys = Service.collectionKeys.asWBO("crypto", "keys");
     await serverKeys.encrypt(Service.identity.syncKeyBundle);
--- a/services/sync/tests/unit/head_helpers.js
+++ b/services/sync/tests/unit/head_helpers.js
@@ -13,18 +13,22 @@
 Cu.import("resource://services-common/async.js");
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://testing-common/PlacesTestUtils.jsm");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/PlacesUtils.jsm");
 Cu.import("resource://gre/modules/PlacesSyncUtils.jsm");
 Cu.import("resource://gre/modules/ObjectUtils.jsm");
+Cu.import("resource://testing-common/services/sync/utils.js");
 
 add_task(async function head_setup() {
+  // Initialize logging. This will sometimes be reset by a pref reset,
+  // so it's also called as part of SyncTestingInfrastructure().
+  syncTestLogging();
   // If a test imports Service, make sure it is initialized first.
   if (this.Service) {
     await this.Service.promiseInitialized;
   }
 });
 
 // ================================================
 // Load mocking/stubbing library, sinon
--- a/services/sync/tests/unit/test_412.js
+++ b/services/sync/tests/unit/test_412.js
@@ -1,18 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://testing-common/services/sync/rotaryengine.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
-
-initTestLogging("Trace");
-
-Services.prefs.setCharPref("services.sync.log.logger.service.main", "Trace");
 
 add_task(async function test_412_not_treated_as_failure() {
   await Service.engineManager.register(RotaryEngine);
   let engine = Service.engineManager.get("rotary");
 
   let server = await serverForFoo(engine);
 
   await SyncTestingInfrastructure(server);
--- a/services/sync/tests/unit/test_addon_utils.js
+++ b/services/sync/tests/unit/test_addon_utils.js
@@ -36,17 +36,17 @@ function createAndStartHTTPServer(port =
     _("Got exception starting HTTP server on port " + port);
     _("Error: " + Log.exceptionStr(ex));
     do_throw(ex);
   }
   return null; /* not hit, but keeps eslint happy! */
 }
 
 function run_test() {
-  initTestLogging("Trace");
+  syncTestLogging();
 
   run_next_test();
 }
 
 add_test(function test_handle_empty_source_uri() {
   _("Ensure that search results without a sourceURI are properly ignored.");
 
   let server = createAndStartHTTPServer();
--- a/services/sync/tests/unit/test_addons_engine.js
+++ b/services/sync/tests/unit/test_addons_engine.js
@@ -6,17 +6,16 @@
 Cu.import("resource://gre/modules/AddonManager.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://services-common/async.js");
 Cu.import("resource://services-sync/addonsreconciler.js");
 Cu.import("resource://services-sync/engines/addons.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 
 const prefs = new Preferences();
 prefs.set("extensions.getAddons.get.url",
           "http://localhost:8888/search/guid:%IDS%");
 prefs.set("extensions.install.requireSecureOrigin", false);
 
 let engine;
 let reconciler;
@@ -27,25 +26,16 @@ async function resetReconciler() {
   reconciler._changes = [];
 
   await reconciler.saveState();
 
   tracker.clearChangedIDs();
 }
 
 add_task(async function setup() {
-  initTestLogging("Trace");
-  Log.repository.getLogger("Sync.Engine.Addons").level =
-    Log.Level.Trace;
-  Log.repository.getLogger("Sync.Store.Addons").level = Log.Level.Trace;
-  Log.repository.getLogger("Sync.Tracker.Addons").level =
-    Log.Level.Trace;
-  Log.repository.getLogger("Sync.AddonsRepository").level =
-    Log.Level.Trace;
-
   loadAddonTestFunctions();
   startupManager();
 
   await Service.engineManager.register(AddonsEngine);
   engine = Service.engineManager.get("addons");
   reconciler = engine._reconciler;
   tracker = engine._tracker;
 
--- a/services/sync/tests/unit/test_addons_reconciler.js
+++ b/services/sync/tests/unit/test_addons_reconciler.js
@@ -8,21 +8,16 @@ Cu.import("resource://services-sync/addo
 Cu.import("resource://services-sync/engines/addons.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
 loadAddonTestFunctions();
 startupManager();
 
 add_task(async function run_test() {
-  initTestLogging("Trace");
-  Log.repository.getLogger("Sync.AddonsReconciler").level = Log.Level.Trace;
-  Log.repository.getLogger("Sync.AddonsReconciler").level =
-    Log.Level.Trace;
-
   Svc.Prefs.set("engine.addons", true);
   await Service.engineManager.register(AddonsEngine);
 });
 
 add_task(async function test_defaults() {
   _("Ensure new objects have reasonable defaults.");
 
   let reconciler = new AddonsReconciler();
--- a/services/sync/tests/unit/test_addons_store.js
+++ b/services/sync/tests/unit/test_addons_store.js
@@ -4,17 +4,16 @@
 "use strict";
 
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://services-sync/addonutils.js");
 Cu.import("resource://services-sync/engines/addons.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 
 const HTTP_PORT = 8888;
 
 const prefs = new Preferences();
 
 prefs.set("extensions.getAddons.get.url", "http://localhost:8888/search/guid:%IDS%");
 prefs.set("extensions.install.requireSecureOrigin", false);
@@ -99,21 +98,16 @@ function createAndStartHTTPServer(port) 
 function checkReconcilerUpToDate(addon) {
   let stateBefore = Object.assign({}, store.reconciler.addons[addon.id]);
   store.reconciler.rectifyStateFromAddon(addon);
   let stateAfter = store.reconciler.addons[addon.id];
   deepEqual(stateBefore, stateAfter);
 }
 
 add_task(async function setup() {
-  initTestLogging("Trace");
-  Log.repository.getLogger("Sync.Engine.Addons").level = Log.Level.Trace;
-  Log.repository.getLogger("Sync.Tracker.Addons").level = Log.Level.Trace;
-  Log.repository.getLogger("Sync.AddonsRepository").level = Log.Level.Trace;
-
   await Service.engineManager.register(AddonsEngine);
   engine     = Service.engineManager.get("addons");
   tracker    = engine._tracker;
   store      = engine._store;
   reconciler = engine._reconciler;
 
   reconciler.startListening();
 
--- a/services/sync/tests/unit/test_addons_tracker.js
+++ b/services/sync/tests/unit/test_addons_tracker.js
@@ -28,20 +28,16 @@ async function cleanup() {
   tracker.clearChangedIDs();
 
   reconciler._addons = {};
   reconciler._changes = [];
   await reconciler.saveState();
 }
 
 add_task(async function setup() {
-  initTestLogging("Trace");
-  Log.repository.getLogger("Sync.Engine.Addons").level = Log.Level.Trace;
-  Log.repository.getLogger("Sync.AddonsReconciler").level = Log.Level.Trace;
-
   await Service.engineManager.register(AddonsEngine);
   engine     = Service.engineManager.get("addons");
   reconciler = engine._reconciler;
   store      = engine._store;
   tracker    = engine._tracker;
 
   // Don't write out by default.
   tracker.persistChangedIDs = false;
--- a/services/sync/tests/unit/test_bookmark_decline_undecline.js
+++ b/services/sync/tests/unit/test_bookmark_decline_undecline.js
@@ -3,34 +3,28 @@
 
 Cu.import("resource://gre/modules/BookmarkJSONUtils.jsm");
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/engines/bookmarks.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
-
-add_task(async function setup() {
-  initTestLogging("Trace");
-  await Service.engineManager.register(BookmarksEngine);
-});
 
 // A stored reference to the collection won't be valid after disabling.
 function getBookmarkWBO(server, guid) {
   let coll = server.user("foo").collection("bookmarks");
   if (!coll) {
     return null;
   }
   return coll.wbo(guid);
 }
 
 add_task(async function setup() {
-  initTestLogging("Trace");
+  await Service.engineManager.register(BookmarksEngine);
   await generateNewKeys(Service.collectionKeys);
 });
 
 add_task(async function test_decline_undecline() {
   let engine = Service.engineManager.get("bookmarks");
   let server = await serverForFoo(engine);
   await SyncTestingInfrastructure(server);
 
--- a/services/sync/tests/unit/test_bookmark_duping.js
+++ b/services/sync/tests/unit/test_bookmark_duping.js
@@ -1,35 +1,28 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-common/async.js");
-Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/engines/bookmarks.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 Cu.import("resource://services-sync/bookmark_validator.js");
 
 const bms = PlacesUtils.bookmarks;
 
 add_task(async function setup() {
-  initTestLogging("Trace");
   await Service.engineManager.unregister("bookmarks");
 });
 
 async function sharedSetup() {
   let engine = new BookmarksEngine(Service);
   await engine.initialize();
   let store = engine._store;
-
-  store._log.level = Log.Level.Trace;
-  engine._log.level = Log.Level.Trace;
-
   let server = await serverForFoo(engine);
   await SyncTestingInfrastructure(server);
 
   let collection = server.user("foo").collection("bookmarks");
 
   Svc.Obs.notify("weave:engine:start-tracking"); // We skip usual startup...
 
   return { engine, store, server, collection };
--- a/services/sync/tests/unit/test_bookmark_engine.js
+++ b/services/sync/tests/unit/test_bookmark_engine.js
@@ -6,20 +6,16 @@ Cu.import("resource://gre/modules/Bookma
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/engines/bookmarks.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
-
-
-initTestLogging("Trace");
 
 async function fetchAllRecordIds() {
   let db = await PlacesUtils.promiseDBConnection();
   let rows = await db.executeCached(`
     WITH RECURSIVE
     syncedItems(id, guid) AS (
       SELECT b.id, b.guid FROM moz_bookmarks b
       WHERE b.guid IN ('menu________', 'toolbar_____', 'unfiled_____',
@@ -33,25 +29,18 @@ async function fetchAllRecordIds() {
   for (let row of rows) {
     let recordId = PlacesSyncUtils.bookmarks.guidToRecordId(
       row.getResultByName("guid"));
     recordIds.add(recordId);
   }
   return recordIds;
 }
 add_task(async function setup() {
-  initTestLogging("Trace");
   await generateNewKeys(Service.collectionKeys);
-});
-
-add_task(async function setup() {
   await Service.engineManager.unregister("bookmarks");
-
-  initTestLogging("Trace");
-  generateNewKeys(Service.collectionKeys);
 });
 
 add_task(async function test_delete_invalid_roots_from_server() {
   _("Ensure that we delete the Places and Reading List roots from the server.");
 
   let engine  = new BookmarksEngine(Service);
   await engine.initialize();
   let store   = engine._store;
--- a/services/sync/tests/unit/test_bookmark_invalid.js
+++ b/services/sync/tests/unit/test_bookmark_invalid.js
@@ -4,17 +4,16 @@ Cu.import("resource://services-sync/engi
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
 let engine;
 let store;
 let tracker;
 
 add_task(async function setup() {
-  initTestLogging("Trace");
   engine = new BookmarksEngine(Service);
   store = engine._store;
   tracker = engine._tracker;
 });
 
 add_task(async function test_ignore_invalid_uri() {
   _("Ensure that we don't die with invalid bookmarks.");
 
--- a/services/sync/tests/unit/test_bookmark_livemarks.js
+++ b/services/sync/tests/unit/test_bookmark_livemarks.js
@@ -54,22 +54,16 @@ function makeLivemark(p, mintGUID) {
   b.cleartext = Cu.cloneInto(p, {});
 
   if (mintGUID)
     b.id = Utils.makeGUID();
 
   return b;
 }
 
-add_task(async function setup() {
-  initTestLogging("Trace");
-  Log.repository.getLogger("Sync.Engine.Bookmarks").level = Log.Level.Trace;
-  Log.repository.getLogger("Sync.Store.Bookmarks").level  = Log.Level.Trace;
-});
-
 add_task(async function test_livemark_descriptions() {
   let engine = new BookmarksEngine(Service);
   await engine.initialize();
   let store = engine._store;
 
   let record = record631361.payload;
 
   async function doRecord(r) {
--- a/services/sync/tests/unit/test_bookmark_order.js
+++ b/services/sync/tests/unit/test_bookmark_order.js
@@ -2,21 +2,16 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 _("Making sure after processing incoming bookmarks, they show up in the right order");
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://services-sync/engines/bookmarks.js");
 Cu.import("resource://services-sync/main.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
-
-Svc.Prefs.set("log.logger.engine.bookmarks", "Trace");
-initTestLogging("Trace");
-Log.repository.getLogger("Sqlite").level = Log.Level.Info;
 
 async function serverForFoo(engine) {
   await generateNewKeys(Service.collectionKeys);
 
   let clientsEngine = Service.clientsEngine;
   return serverForUsers({"foo": "password"}, {
     meta: {
       global: {
--- a/services/sync/tests/unit/test_bookmark_places_query_rewriting.js
+++ b/services/sync/tests/unit/test_bookmark_places_query_rewriting.js
@@ -16,19 +16,16 @@ function makeTagRecord(id, uri) {
   tagRecord.bmkUri = uri;
   tagRecord.title = "tagtag";
   tagRecord.folderName = "bar";
   tagRecord.parentid = PlacesUtils.bookmarks.toolbarGuid;
   return tagRecord;
 }
 
 add_task(async function run_test() {
-  initTestLogging("Trace");
-  Log.repository.getLogger("Sync.Engine.Bookmarks").level = Log.Level.Trace;
-  Log.repository.getLogger("Sync.Store.Bookmarks").level = Log.Level.Trace;
 
   let uri = "place:folder=499&type=7&queryType=1";
   let tagRecord = makeTagRecord("abcdefabcdef", uri);
 
   _("Type: " + tagRecord.type);
   _("Folder name: " + tagRecord.folderName);
   await store.applyIncoming(tagRecord);
 
--- a/services/sync/tests/unit/test_bookmark_record.js
+++ b/services/sync/tests/unit/test_bookmark_record.js
@@ -2,45 +2,41 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://services-sync/engines/bookmarks.js");
 Cu.import("resource://services-sync/keys.js");
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 
 function prepareBookmarkItem(collection, id) {
   let b = new Bookmark(collection, id);
   b.cleartext.stuff = "my payload here";
   return b;
 }
 
 add_task(async function test_bookmark_record() {
   await configureIdentity();
 
   await generateNewKeys(Service.collectionKeys);
   let keyBundle = Service.identity.syncKeyBundle;
 
-  let log = Log.repository.getLogger("Test");
-  Log.repository.rootLogger.addAppender(new Log.DumpAppender());
-
-  log.info("Creating a record");
+  _("Creating a record");
 
   let placesItem = new PlacesItem("bookmarks", "foo", "bookmark");
   let bookmarkItem = prepareBookmarkItem("bookmarks", "foo");
 
-  log.info("Checking getTypeObject");
+  _("Checking getTypeObject");
   Assert.equal(placesItem.getTypeObject(placesItem.type), Bookmark);
   Assert.equal(bookmarkItem.getTypeObject(bookmarkItem.type), Bookmark);
 
   await bookmarkItem.encrypt(keyBundle);
-  log.info("Ciphertext is " + bookmarkItem.ciphertext);
+  _("Ciphertext is " + bookmarkItem.ciphertext);
   Assert.ok(bookmarkItem.ciphertext != null);
 
-  log.info("Decrypting the record");
+  _("Decrypting the record");
 
   let payload = await bookmarkItem.decrypt(keyBundle);
   Assert.equal(payload.stuff, "my payload here");
   Assert.equal(bookmarkItem.getTypeObject(bookmarkItem.type), Bookmark);
   Assert.notEqual(payload, bookmarkItem.payload); // wrap.data.payload is the encrypted one
 });
--- a/services/sync/tests/unit/test_bookmark_repair.js
+++ b/services/sync/tests/unit/test_bookmark_repair.js
@@ -6,17 +6,16 @@
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://services-sync/bookmark_repair.js");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/doctor.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/engines/clients.js");
 Cu.import("resource://services-sync/engines/bookmarks.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 
 const LAST_BOOKMARK_SYNC_PREFS = [
   "bookmarks.lastSync",
   "bookmarks.lastSyncLocal",
 ];
 
 const BOOKMARK_REPAIR_STATE_PREFS = [
   "client.GUID",
@@ -36,21 +35,16 @@ add_task(async function setup() {
   clientsEngine.ignoreLastModifiedOnProcessCommands = true;
   bookmarksEngine = Service.engineManager.get("bookmarks");
 
   await generateNewKeys(Service.collectionKeys);
 
   Service.recordTelemetryEvent = (object, method, value, extra = undefined) => {
     recordedEvents.push({ object, method, value, extra });
   };
-
-  initTestLogging("Trace");
-  Log.repository.getLogger("Sync.Engine.Bookmarks").level = Log.Level.Trace;
-  Log.repository.getLogger("Sync.Engine.Clients").level = Log.Level.Trace;
-  Log.repository.getLogger("Sqlite").level = Log.Level.Info; // less noisy
 });
 
 function checkRecordedEvents(expected, message) {
   deepEqual(recordedEvents, expected, message);
   // and clear the list so future checks are easier to write.
   recordedEvents = [];
 }
 
--- a/services/sync/tests/unit/test_bookmark_repair_requestor.js
+++ b/services/sync/tests/unit/test_bookmark_repair_requestor.js
@@ -1,14 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 Cu.import("resource://services-sync/bookmark_repair.js");
 
-initTestLogging("Trace");
-
 function makeClientRecord(id, fields = {}) {
   return {
     id,
     version: fields.version || "54.0a1",
     type: fields.type || "desktop",
     stale: fields.stale || false,
     serverLastModified: fields.serverLastModified || 0,
   };
--- a/services/sync/tests/unit/test_bookmark_repair_responder.js
+++ b/services/sync/tests/unit/test_bookmark_repair_responder.js
@@ -1,21 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource:///modules/PlacesUIUtils.jsm");
-Cu.import("resource://gre/modules/Log.jsm");
 
 Cu.import("resource://services-sync/engines/bookmarks.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/bookmark_repair.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
-
-initTestLogging("Trace");
-Log.repository.getLogger("Sync.Engine.Bookmarks").level = Log.Level.Trace;
 
 // Disable validation so that we don't try to automatically repair the server
 // when we sync.
 Svc.Prefs.set("engine.bookmarks.validation.enabled", false);
 
 // stub telemetry so we can easily check the right things are recorded.
 var recordedEvents = [];
 
--- a/services/sync/tests/unit/test_bookmark_smart_bookmarks.js
+++ b/services/sync/tests/unit/test_bookmark_smart_bookmarks.js
@@ -2,17 +2,16 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/engines/bookmarks.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 
 async function newSmartBookmark(parentGuid, url, position, title, queryID) {
   let info = await PlacesUtils.bookmarks.insert({
     parentGuid,
     url,
     position,
     title,
   });
@@ -31,19 +30,16 @@ function smartBookmarkCount() {
     PlacesSyncUtils.bookmarks.SMART_BOOKMARKS_ANNO, out);
   return out.value;
 }
 
 let engine;
 let store;
 
 add_task(async function setup() {
-  initTestLogging("Trace");
-  Log.repository.getLogger("Sync.Engine.Bookmarks").level = Log.Level.Trace;
-
   await generateNewKeys(Service.collectionKeys);
 });
 
 add_task(async function setup() {
   await Service.engineManager.register(BookmarksEngine);
   engine = Service.engineManager.get("bookmarks");
   store = engine._store;
 });
--- a/services/sync/tests/unit/test_bookmark_store.js
+++ b/services/sync/tests/unit/test_bookmark_store.js
@@ -562,14 +562,8 @@ add_task(async function test_delete_buff
     equal(tbItem.parentGuid, PlacesUtils.bookmarks.toolbarGuid);
 
   } finally {
     _("Clean up.");
     await store.wipe();
     await engine.finalize();
   }
 });
-
-
-function run_test() {
-  initTestLogging("Trace");
-  run_next_test();
-}
--- a/services/sync/tests/unit/test_browserid_identity.js
+++ b/services/sync/tests/unit/test_browserid_identity.js
@@ -2,17 +2,16 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://gre/modules/FxAccounts.jsm");
 Cu.import("resource://services-sync/browserid_identity.js");
 Cu.import("resource://services-sync/resource.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://services-crypto/utils.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 Cu.import("resource://testing-common/services/sync/fxa_utils.js");
 Cu.import("resource://services-common/hawkclient.js");
 Cu.import("resource://gre/modules/FxAccounts.jsm");
 Cu.import("resource://gre/modules/FxAccountsClient.jsm");
 Cu.import("resource://gre/modules/FxAccountsCommon.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/status.js");
 Cu.import("resource://services-sync/constants.js");
@@ -57,23 +56,16 @@ function MockFxAccounts() {
       validUntil: fxa.internal.now() + CERT_LIFETIME,
       cert: "certificate",
     };
     return Promise.resolve(this.cert.cert);
   };
   return fxa;
 }
 
-function run_test() {
-  initTestLogging("Trace");
-  Log.repository.getLogger("Sync.Identity").level = Log.Level.Trace;
-  Log.repository.getLogger("Sync.BrowserIDManager").level = Log.Level.Trace;
-  run_next_test();
-}
-
 add_test(function test_initial_state() {
     _("Verify initial state");
     Assert.ok(!globalBrowseridManager._token);
     Assert.ok(!globalBrowseridManager.hasValidToken());
     run_next_test();
   }
 );
 
--- a/services/sync/tests/unit/test_browserid_identity_telemetry.js
+++ b/services/sync/tests/unit/test_browserid_identity_telemetry.js
@@ -1,10 +1,9 @@
 Cu.import("resource://gre/modules/Preferences.jsm");
-Cu.import("resource://testing-common/services/sync/utils.js");
 let {telemetryHelper} = Cu.import("resource://services-sync/browserid_identity.js", {});
 
 const prefs = new Preferences("services.sync.");
 
 function cleanup() {
   prefs.resetBranch();
 }
 
--- a/services/sync/tests/unit/test_clients_engine.js
+++ b/services/sync/tests/unit/test_clients_engine.js
@@ -2,17 +2,16 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/engines/clients.js");
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 
 const MORE_THAN_CLIENTS_TTL_REFRESH = 691200; // 8 days
 const LESS_THAN_CLIENTS_TTL_REFRESH = 86400; // 1 day
 
 let engine;
 
 /**
  * Unpack the record with this ID, and verify that it has the same version that
@@ -56,16 +55,19 @@ async function syncClientsEngine(server)
 add_task(async function setup() {
   engine = Service.clientsEngine;
 });
 
 async function cleanup() {
   Svc.Prefs.resetBranch("");
   engine._tracker.clearChangedIDs();
   await engine._resetClient();
+  // un-cleanup the logs (the resetBranch will have reset their levels), since
+  // not all the tests use SyncTestingInfrastructure, and it's cheap.
+  syncTestLogging();
   // We don't finalize storage at cleanup, since we use the same clients engine
   // instance across all tests.
 }
 
 add_task(async function test_bad_hmac() {
   _("Ensure that Clients engine deletes corrupt records.");
   let deletedCollections = [];
   let deletedItems       = [];
@@ -1887,14 +1889,8 @@ add_task(async function test_create_reco
     try {
       let collection = server.getCollection("foo", "clients");
       collection.remove(remoteId);
     } finally {
       await promiseStopServer(server);
     }
   }
 });
-
-function run_test() {
-  initTestLogging("Trace");
-  Log.repository.getLogger("Sync.Engine.Clients").level = Log.Level.Trace;
-  run_next_test();
-}
--- a/services/sync/tests/unit/test_clients_escape.js
+++ b/services/sync/tests/unit/test_clients_escape.js
@@ -1,16 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/keys.js");
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 
 add_task(async function test_clients_escape() {
   _("Set up test fixtures.");
 
   await configureIdentity();
   let keyBundle = Service.identity.syncKeyBundle;
 
   let engine = Service.clientsEngine;
--- a/services/sync/tests/unit/test_collection_getBatched.js
+++ b/services/sync/tests/unit/test_collection_getBatched.js
@@ -1,20 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/service.js");
 
-function run_test() {
-  initTestLogging("Trace");
-  Log.repository.getLogger("Sync.Collection").level = Log.Level.Trace;
-  run_next_test();
-}
-
 function recordRange(lim, offset, total) {
   let res = [];
   for (let i = offset; i < Math.min(lim + offset, total); ++i) {
     res.push(JSON.stringify({ id: String(i), payload: "test:" + i }));
   }
   return res;
 }
 
--- a/services/sync/tests/unit/test_collections_recovery.js
+++ b/services/sync/tests/unit/test_collections_recovery.js
@@ -1,17 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Verify that we wipe the server if we have to regenerate keys.
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
-
-initTestLogging("Trace");
 
 add_task(async function test_missing_crypto_collection() {
   enableValidationPrefs();
 
   let johnHelper = track_collections_helper();
   let johnU      = johnHelper.with_updated_collection;
   let johnColls  = johnHelper.collections;
 
--- a/services/sync/tests/unit/test_corrupt_keys.js
+++ b/services/sync/tests/unit/test_corrupt_keys.js
@@ -7,17 +7,16 @@ Cu.import("resource://services-sync/cons
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/main.js");
 Cu.import("resource://services-sync/engines/tabs.js");
 Cu.import("resource://services-sync/engines/history.js");
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/status.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 
 add_task(async function test_locally_changed_keys() {
   enableValidationPrefs();
 
   let hmacErrorCount = 0;
   function counting(f) {
     return async function() {
       hmacErrorCount++;
--- a/services/sync/tests/unit/test_doctor.js
+++ b/services/sync/tests/unit/test_doctor.js
@@ -1,16 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const { Doctor, REPAIR_ADVANCE_PERIOD } = Cu.import("resource://services-sync/doctor.js", {});
 Cu.import("resource://gre/modules/Services.jsm");
 
-initTestLogging("Trace");
-
 function mockDoctor(mocks) {
   // Clone the object and put mocks in that.
   return Object.assign({}, Doctor, mocks);
 }
 
 add_task(async function test_validation_interval() {
   let now = 1000;
   let doctor = mockDoctor({
--- a/services/sync/tests/unit/test_engine_abort.js
+++ b/services/sync/tests/unit/test_engine_abort.js
@@ -1,17 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://testing-common/services/sync/rotaryengine.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 
 add_task(async function test_processIncoming_abort() {
   _("An abort exception, raised in applyIncoming, will abort _processIncoming.");
   let engine = new RotaryEngine(Service);
 
   let collection = new ServerCollection();
   let id = Utils.makeGUID();
   let payload = encryptPayload({id, denomination: "Record No. " + id});
--- a/services/sync/tests/unit/test_engine_changes_during_sync.js
+++ b/services/sync/tests/unit/test_engine_changes_during_sync.js
@@ -1,17 +1,16 @@
 Cu.import("resource://gre/modules/FormHistory.jsm");
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/engines/bookmarks.js");
 Cu.import("resource://services-sync/engines/history.js");
 Cu.import("resource://services-sync/engines/forms.js");
 Cu.import("resource://services-sync/engines/passwords.js");
 Cu.import("resource://services-sync/engines/prefs.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 
 const LoginInfo = Components.Constructor(
   "@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo, "init");
 
 /**
  * We don't test the clients or tabs engines because neither has conflict
  * resolution logic. The clients engine syncs twice per global sync, and
  * custom conflict resolution logic for commands that doesn't use
--- a/services/sync/tests/unit/test_errorhandler_1.js
+++ b/services/sync/tests/unit/test_errorhandler_1.js
@@ -4,17 +4,16 @@
 Cu.import("resource://services-sync/engines/clients.js");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/keys.js");
 Cu.import("resource://services-sync/policies.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/status.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/PromiseUtils.jsm");
 
 var fakeServer = new SyncServer();
 fakeServer.start();
 
 registerCleanupFunction(function() {
   return new Promise(resolve => {
@@ -38,34 +37,30 @@ function setLastSync(lastSyncValue) {
 }
 
 // This relies on Service/ErrorHandler being a singleton. Fixing this will take
 // a lot of work.
 let errorHandler = Service.errorHandler;
 let engine;
 
 add_task(async function setup() {
-  initTestLogging("Trace");
-
-  Log.repository.getLogger("Sync.Service").level = Log.Level.Trace;
-  Log.repository.getLogger("Sync.SyncScheduler").level = Log.Level.Trace;
-  Log.repository.getLogger("Sync.ErrorHandler").level = Log.Level.Trace;
-
   Service.engineManager.clear();
   await Service.engineManager.register(EHTestsCommon.CatapultEngine);
   engine = Service.engineManager.get("catapult");
 });
 
 async function clean() {
   let promiseLogReset = promiseOneObserver("weave:service:reset-file-log");
   await Service.startOver();
   await promiseLogReset;
   Status.resetSync();
   Status.resetBackoff();
   errorHandler.didReportProlongedError = false;
+  // Move log levels back to trace (startOver will have reversed this), sicne
+  syncTestLogging();
 }
 
 add_task(async function test_401_logout() {
   enableValidationPrefs();
 
   let server = await EHTestsCommon.sync_httpd_setup();
   await EHTestsCommon.setUp(server);
 
@@ -132,16 +127,17 @@ add_task(async function test_credentials
   Assert.ok(!Service.isLoggedIn);
 
   // Clean up.
   await Service.startOver();
   await promiseStopServer(server);
 });
 
 add_task(function test_no_lastSync_pref() {
+  syncTestLogging();
   // Test reported error.
   Status.resetSync();
   errorHandler.dontIgnoreErrors = true;
   Status.sync = CREDENTIALS_CHANGED;
   Assert.ok(errorHandler.shouldReportError());
 
   // Test unreported error.
   Status.resetSync();
--- a/services/sync/tests/unit/test_errorhandler_2.js
+++ b/services/sync/tests/unit/test_errorhandler_2.js
@@ -4,17 +4,16 @@
 Cu.import("resource://services-sync/engines/clients.js");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/keys.js");
 Cu.import("resource://services-sync/policies.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/status.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/PromiseUtils.jsm");
 
 var fakeServer = new SyncServer();
 fakeServer.start();
 
 registerCleanupFunction(function() {
   return new Promise(resolve => {
@@ -71,36 +70,31 @@ async function syncAndReportErrorsAndWai
   let promise1 = promiseOneObserver(topic);
   // also wait for the log file to be written
   let promise2 = promiseOneObserver("weave:service:reset-file-log");
   errorHandler.syncAndReportErrors();
   await promise1;
   await promise2;
 }
 add_task(async function setup() {
-  initTestLogging("Trace");
-
-  Log.repository.getLogger("Sync.Service").level = Log.Level.Trace;
-  Log.repository.getLogger("Sync.SyncScheduler").level = Log.Level.Trace;
-  Log.repository.getLogger("Sync.ErrorHandler").level = Log.Level.Trace;
-  Log.repository.getLogger("Sync.LogManager").level = Log.Level.Trace;
-
   Service.engineManager.clear();
   await Service.engineManager.register(EHTestsCommon.CatapultEngine);
   engine = Service.engineManager.get("catapult");
 });
 
 async function clean() {
   let promiseLogReset = promiseOneObserver("weave:service:reset-file-log");
   await Service.startOver();
   await promiseLogReset;
   Status.resetSync();
   Status.resetBackoff();
   errorHandler.didReportProlongedError = false;
   removeLogFiles();
+  // Move log levels back to trace (startOver will have reversed this), sicne
+  syncTestLogging();
 }
 
 add_task(async function test_crypto_keys_login_server_maintenance_error() {
   enableValidationPrefs();
 
   Status.resetSync();
   // Test crypto/keys server maintenance errors are not reported.
   let server = await EHTestsCommon.sync_httpd_setup();
--- a/services/sync/tests/unit/test_errorhandler_eol.js
+++ b/services/sync/tests/unit/test_errorhandler_eol.js
@@ -1,17 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/status.js");
 Cu.import("resource://services-sync/util.js");
 
 Cu.import("resource://testing-common/services/sync/fakeservices.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 
 function baseHandler(eolCode, request, response, statusCode, status, body) {
   let alertBody = {
     code: eolCode,
     message: "Service is EOLed.",
     url: "http://getfirefox.com",
   };
   response.setHeader("X-Weave-Timestamp", "" + new_timestamp(), false);
--- a/services/sync/tests/unit/test_errorhandler_filelog.js
+++ b/services/sync/tests/unit/test_errorhandler_filelog.js
@@ -23,25 +23,17 @@ const PROLONGED_ERROR_DURATION =
 
 var errorHandler = Service.errorHandler;
 
 function setLastSync(lastSyncValue) {
   Svc.Prefs.set("lastSync", (new Date(Date.now() - lastSyncValue)).toString());
 }
 
 function run_test() {
-  initTestLogging("Trace");
-
-  Log.repository.getLogger("Sync.LogManager").level = Log.Level.Trace;
-  Log.repository.getLogger("Sync.Service").level = Log.Level.Trace;
-  Log.repository.getLogger("Sync.SyncScheduler").level = Log.Level.Trace;
-  Log.repository.getLogger("Sync.ErrorHandler").level = Log.Level.Trace;
-
   validate_all_future_pings();
-
   run_next_test();
 }
 
 add_test(function test_noOutput() {
   // Ensure that the log appender won't print anything.
   errorHandler._logManager._fileAppender.level = Log.Level.Fatal + 1;
 
   // Clear log output from startup.
--- a/services/sync/tests/unit/test_errorhandler_sync_checkServerError.js
+++ b/services/sync/tests/unit/test_errorhandler_sync_checkServerError.js
@@ -4,19 +4,16 @@
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/policies.js");
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/status.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://testing-common/services/sync/fakeservices.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
-
-initTestLogging("Trace");
 
 var engineManager = Service.engineManager;
 engineManager.clear();
 
 function CatapultEngine() {
   SyncEngine.call(this, "Catapult", Service);
 }
 CatapultEngine.prototype = {
@@ -50,16 +47,17 @@ async function sync_httpd_setup() {
     "/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler())
   };
   return httpd_setup(handlers);
 }
 
 async function setUp(server) {
   await configureIdentity({username: "johndoe"}, server);
   new FakeCryptoService();
+  syncTestLogging();
 }
 
 async function generateAndUploadKeys(server) {
   await generateNewKeys(Service.collectionKeys);
   let serverKeys = Service.collectionKeys.asWBO("crypto", "keys");
   await serverKeys.encrypt(Service.identity.syncKeyBundle);
   let res = Service.resource(server.baseURI + "/1.1/johndoe/storage/crypto/keys");
   return (await serverKeys.upload(res)).success;
--- a/services/sync/tests/unit/test_extension_storage_engine.js
+++ b/services/sync/tests/unit/test_extension_storage_engine.js
@@ -2,17 +2,16 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/engines/extension-storage.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 Cu.import("resource://gre/modules/ExtensionStorageSync.jsm");
 /* globals extensionStorageSync */
 
 let engine;
 
 function mock(options) {
   let calls = [];
   let ret = function() {
--- a/services/sync/tests/unit/test_fxa_node_reassignment.js
+++ b/services/sync/tests/unit/test_fxa_node_reassignment.js
@@ -11,27 +11,19 @@ Cu.import("resource://gre/modules/Log.js
 Cu.import("resource://services-common/rest.js");
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/status.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://testing-common/services/sync/rotaryengine.js");
 Cu.import("resource://services-sync/browserid_identity.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 Cu.import("resource://gre/modules/PromiseUtils.jsm");
 
 add_task(async function setup() {
-  Log.repository.getLogger("Sync.ErrorHandler").level  = Log.Level.Trace;
-  Log.repository.getLogger("Sync.Resource").level      = Log.Level.Trace;
-  Log.repository.getLogger("Sync.RESTRequest").level   = Log.Level.Trace;
-  Log.repository.getLogger("Sync.Service").level       = Log.Level.Trace;
-  Log.repository.getLogger("Sync.SyncScheduler").level = Log.Level.Trace;
-  initTestLogging();
-
   // Disables all built-in engines. Important for avoiding errors thrown by the
   // add-ons engine.
   Service.engineManager.clear();
 
   // Setup the FxA identity manager and cluster manager.
   Status.__authManager = Service.identity = new BrowserIDManager();
   Service._clusterManager = Service.identity.createClusterManager(Service);
 
@@ -51,16 +43,17 @@ function handleReassign(handler, req, re
   resp.setHeader("Content-Type", "application/json");
   let reassignBody = JSON.stringify({error: "401inator in place"});
   resp.bodyOutputStream.write(reassignBody, reassignBody.length);
 }
 
 var numTokenRequests = 0;
 
 function prepareServer(cbAfterTokenFetch) {
+  syncTestLogging();
   let config = makeIdentityConfig({username: "johndoe"});
   // A server callback to ensure we don't accidentally hit the wrong endpoint
   // after a node reassignment.
   let callback = {
     __proto__: SyncServerCallback,
     onRequest(req, resp) {
       let full = `${req.scheme}://${req.host}:${req.port}${req.path}`;
       Assert.ok(full.startsWith(config.fxaccount.token.endpoint),
--- a/services/sync/tests/unit/test_fxa_service_cluster.js
+++ b/services/sync/tests/unit/test_fxa_service_cluster.js
@@ -1,15 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://testing-common/services/sync/fxa_utils.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 
 add_task(async function test_findCluster() {
   _("Test FxA _findCluster()");
 
   _("_findCluster() throws on 500 errors.");
   initializeIdentityWithTokenServerResponse({
     status: 500,
     headers: [],
@@ -56,13 +55,8 @@ add_task(async function test_findCluster
   await Service.identity.initializeWithCurrentIdentity();
   await Service.identity.whenReadyToAuthenticate.promise;
   cluster = Service._clusterManager._findCluster();
   // The cluster manager ensures a trailing "/"
   Assert.strictEqual(cluster, endpoint + "/");
 
   Svc.Prefs.resetBranch("");
 });
-
-function run_test() {
-  initTestLogging();
-  run_next_test();
-}
--- a/services/sync/tests/unit/test_history_engine.js
+++ b/services/sync/tests/unit/test_history_engine.js
@@ -1,19 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/engines/history.js");
 Cu.import("resource://services-common/utils.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
-
-add_task(async function setup() {
-  initTestLogging("Trace");
-});
 
 async function rawAddVisit(id, uri, visitPRTime, transitionType) {
   return new Promise((resolve, reject) => {
     let results = [];
     let handler = {
       handleResult(result) {
         results.push(result);
       },
--- a/services/sync/tests/unit/test_history_store.js
+++ b/services/sync/tests/unit/test_history_store.js
@@ -47,21 +47,16 @@ var engine = new HistoryEngine(Service);
 Async.promiseSpinningly(engine.initialize());
 var store = engine._store;
 async function applyEnsureNoFailures(records) {
   Assert.equal((await store.applyIncomingBatch(records)).length, 0);
 }
 
 var fxuri, fxguid, tburi, tbguid;
 
-function run_test() {
-  initTestLogging("Trace");
-  run_next_test();
-}
-
 add_task(async function test_store() {
   _("Verify that we've got an empty store to work with.");
   do_check_empty((await store.getAllIDs()));
 
   _("Let's create an entry in the database.");
   fxuri = CommonUtils.makeURI("http://getfirefox.com/");
 
   await PlacesTestUtils.addVisits({ uri: fxuri, title: "Get Firefox!",
--- a/services/sync/tests/unit/test_history_tracker.js
+++ b/services/sync/tests/unit/test_history_tracker.js
@@ -9,18 +9,16 @@ Cu.import("resource://services-sync/cons
 Cu.import("resource://services-sync/engines/history.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
 let engine;
 let tracker;
 
 add_task(async function setup() {
-  initTestLogging("Trace");
-  Log.repository.getLogger("Sync.Tracker.History").level = Log.Level.Trace;
 
   Service.engineManager.clear();
   await Service.engineManager.register(HistoryEngine);
   engine = Service.engineManager.get("history");
   tracker = engine._tracker;
 
   // Don't write out by default.
   tracker.persistChangedIDs = false;
--- a/services/sync/tests/unit/test_hmac_error.js
+++ b/services/sync/tests/unit/test_hmac_error.js
@@ -1,29 +1,29 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://testing-common/services/sync/rotaryengine.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 
 // Track HMAC error counts.
 var hmacErrorCount = 0;
 (function() {
   let hHE = Service.handleHMACEvent;
   Service.handleHMACEvent = async function() {
     hmacErrorCount++;
     return hHE.call(Service);
   };
 })();
 
 async function shared_setup() {
   enableValidationPrefs();
+  syncTestLogging();
 
   hmacErrorCount = 0;
 
   // Make sure RotaryEngine is the only one we sync.
   let { engine, tracker } = await registerRotaryEngine();
   engine.lastSync = 123; // Needs to be non-zero so that tracker is queried.
   engine._store.items = {flying: "LNER Class A3 4472",
                          scotsman: "Flying Scotsman"};
@@ -231,13 +231,8 @@ add_task(async function hmac_error_durin
         },
         this);
       };
     };
 
     onwards();
   });
 });
-
-function run_test() {
-  initTestLogging("Trace");
-  run_next_test();
-}
--- a/services/sync/tests/unit/test_httpd_sync_server.js
+++ b/services/sync/tests/unit/test_httpd_sync_server.js
@@ -1,20 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://services-sync/util.js");
 
-function run_test() {
-  Log.repository.getLogger("Sync.Test.Server").level = Log.Level.Trace;
-  initTestLogging();
-  run_next_test();
-}
-
 add_test(function test_creation() {
   // Explicit callback for this one.
   let server = new SyncServer({
     __proto__: SyncServerCallback,
   });
   Assert.ok(!!server); // Just so we have a check.
   server.start(null, function() {
     _("Started on " + server.port);
--- a/services/sync/tests/unit/test_interval_triggers.js
+++ b/services/sync/tests/unit/test_interval_triggers.js
@@ -1,17 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/engines/clients.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 
 Svc.Prefs.set("registerEngines", "");
 Cu.import("resource://services-sync/service.js");
 
 let scheduler;
 let clientsEngine;
 
 function sync_httpd_setup() {
@@ -32,29 +31,25 @@ function sync_httpd_setup() {
     "/1.1/johndoe/info/collections": collectionsHelper.handler,
     "/1.1/johndoe/storage/crypto/keys":
       upd("crypto", (new ServerWBO("keys")).handler()),
     "/1.1/johndoe/storage/clients": upd("clients", clientsColl.handler())
   });
 }
 
 async function setUp(server) {
+  syncTestLogging();
   await configureIdentity({username: "johndoe"}, server);
   await generateNewKeys(Service.collectionKeys);
   let serverKeys = Service.collectionKeys.asWBO("crypto", "keys");
   await serverKeys.encrypt(Service.identity.syncKeyBundle);
   await serverKeys.upload(Service.resource(Service.cryptoKeysURL));
 }
 
 add_task(async function setup() {
-  initTestLogging("Trace");
-
-  Log.repository.getLogger("Sync.Service").level = Log.Level.Trace;
-  Log.repository.getLogger("Sync.SyncScheduler").level = Log.Level.Trace;
-
   scheduler = Service.scheduler;
   clientsEngine = Service.clientsEngine;
 
   // Don't remove stale clients when syncing. This is a test-only workaround
   // that lets us add clients directly to the store, without losing them on
   // the next sync.
   clientsEngine._removeRemoteClient = async (id) => {};
 });
--- a/services/sync/tests/unit/test_keys.js
+++ b/services/sync/tests/unit/test_keys.js
@@ -2,17 +2,16 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/keys.js");
 Cu.import("resource://services-sync/main.js");
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://services-sync/browserid_identity.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 
 var collectionKeys = new CollectionKeyManager();
 
 function sha256HMAC(message, key) {
   let h = Utils.makeHMACHasher(Ci.nsICryptoHMAC.SHA256, key);
   return Utils.digestBytes(message, h);
 }
 
--- a/services/sync/tests/unit/test_node_reassignment.js
+++ b/services/sync/tests/unit/test_node_reassignment.js
@@ -6,27 +6,20 @@
 
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://services-common/rest.js");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/status.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://testing-common/services/sync/rotaryengine.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 Cu.import("resource://gre/modules/PromiseUtils.jsm");
 
 
 add_task(async function setup() {
-  Log.repository.getLogger("Sync.ErrorHandler").level  = Log.Level.Trace;
-  Log.repository.getLogger("Sync.Resource").level      = Log.Level.Trace;
-  Log.repository.getLogger("Sync.RESTRequest").level   = Log.Level.Trace;
-  Log.repository.getLogger("Sync.Service").level       = Log.Level.Trace;
-  Log.repository.getLogger("Sync.SyncScheduler").level = Log.Level.Trace;
-  initTestLogging();
   validate_all_future_pings();
 
   // None of the failures in this file should result in a UI error.
   function onUIError() {
     do_throw("Errors should not be presented in the UI.");
   }
   Svc.Obs.add("weave:ui:login:error", onUIError);
   Svc.Obs.add("weave:ui:sync:error", onUIError);
@@ -51,16 +44,17 @@ function handleReassign(handler, req, re
   resp.setHeader("Content-Type", "application/json");
   resp.bodyOutputStream.write(reassignBody, reassignBody.length);
 }
 
 async function prepareServer() {
   let server = new SyncServer();
   server.registerUser("johndoe");
   server.start();
+  syncTestLogging();
   await configureIdentity({username: "johndoe"}, server);
   return server;
 }
 
 function getReassigned() {
   try {
     return Services.prefs.getBoolPref("services.sync.lastSyncReassigned");
   } catch (ex) {
--- a/services/sync/tests/unit/test_password_engine.js
+++ b/services/sync/tests/unit/test_password_engine.js
@@ -1,12 +1,11 @@
 Cu.import("resource://gre/modules/FxAccountsCommon.js");
 Cu.import("resource://services-sync/engines/passwords.js");
 Cu.import("resource://services-sync/service.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 
 const LoginInfo = Components.Constructor(
   "@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo, "init");
 
 const PropertyBag = Components.Constructor(
   "@mozilla.org/hash-property-bag;1", Ci.nsIWritablePropertyBag);
 
 function run_test() {
--- a/services/sync/tests/unit/test_password_store.js
+++ b/services/sync/tests/unit/test_password_store.js
@@ -143,20 +143,16 @@ async function test_apply_same_record_wi
 
 async function test_LoginRec_toString(store, recordData) {
   let rec = await store.createRecord(recordData.id);
   ok(rec);
   ok(!rec.toString().includes(rec.password));
 }
 
 add_task(async function run_test() {
-  initTestLogging("Trace");
-  Log.repository.getLogger("Sync.Engine.Passwords").level = Log.Level.Trace;
-  Log.repository.getLogger("Sync.Store.Passwords").level = Log.Level.Trace;
-
   const BOGUS_GUID_A = "zzzzzzzzzzzz";
   const BOGUS_GUID_B = "yyyyyyyyyyyy";
   let recordA = {id: BOGUS_GUID_A,
                   hostname: "http://foo.bar.com",
                   formSubmitURL: "http://foo.bar.com/baz",
                   httpRealm: "secure",
                   username: "john",
                   password: "smith",
--- a/services/sync/tests/unit/test_password_tracker.js
+++ b/services/sync/tests/unit/test_password_tracker.js
@@ -7,17 +7,16 @@ Cu.import("resource://services-sync/engi
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 
 let engine;
 let store;
 let tracker;
 
 add_task(async function setup() {
-  initTestLogging("Trace");
   await Service.engineManager.register(PasswordEngine);
   engine = Service.engineManager.get("passwords");
   store  = engine._store;
   tracker = engine._tracker;
 
   // Don't do asynchronous writes.
   tracker.persistChangedIDs = false;
 });
--- a/services/sync/tests/unit/test_postqueue.js
+++ b/services/sync/tests/unit/test_postqueue.js
@@ -1,15 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 let { PostQueue } = Cu.import("resource://services-sync/record.js", {});
 
-initTestLogging("Trace");
-
 function makeRecord(nbytes) {
   return {
     toJSON: () => ({ payload: "x".repeat(nbytes) }),
   };
 }
 
 // Note: This is 14 bytes. Tests make assumptions about this (even if it's just
 // in setting config.max_request_bytes to a specific value).
--- a/services/sync/tests/unit/test_records_crypto.js
+++ b/services/sync/tests/unit/test_records_crypto.js
@@ -3,17 +3,16 @@
 
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/keys.js");
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/resource.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 
 var cryptoWrap;
 
 function crypted_resource_handler(metadata, response) {
   let obj = {id: "resource",
              modified: cryptoWrap.modified,
              payload: JSON.stringify(cryptoWrap.payload)};
   return httpd_basic_auth_handler(JSON.stringify(obj), metadata, response);
--- a/services/sync/tests/unit/test_records_wbo.js
+++ b/services/sync/tests/unit/test_records_wbo.js
@@ -1,18 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/resource.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
-
-initTestLogging("Trace");
 
 add_test(function test_toJSON() {
   _("Create a record, for now without a TTL.");
   let wbo = new WBORecord("coll", "a_record");
   wbo.modified = 12345;
   wbo.sortindex = 42;
   wbo.payload = {};
 
--- a/services/sync/tests/unit/test_resource.js
+++ b/services/sync/tests/unit/test_resource.js
@@ -2,17 +2,16 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://gre/modules/Log.jsm");
 Cu.import("resource://services-common/observers.js");
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://services-sync/resource.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://services-sync/browserid_identity.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 
 var logger;
 
 var fetched = false;
 function server_open(metadata, response) {
   let body;
   if (metadata.method == "GET") {
     fetched = true;
--- a/services/sync/tests/unit/test_resource_header.js
+++ b/services/sync/tests/unit/test_resource_header.js
@@ -1,21 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 Cu.import("resource://testing-common/httpd.js");
 Cu.import("resource://services-sync/resource.js");
 
-function run_test() {
-  initTestLogging("Trace");
-  run_next_test();
-}
-
 var httpServer = new HttpServer();
 httpServer.registerPathHandler("/content", contentHandler);
 httpServer.start(-1);
 
 const HTTP_PORT = httpServer.identity.primaryPort;
 const TEST_URL = "http://localhost:" + HTTP_PORT + "/content";
 const BODY = "response body";
 
--- a/services/sync/tests/unit/test_resource_ua.js
+++ b/services/sync/tests/unit/test_resource_ua.js
@@ -1,16 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/resource.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 
 var httpProtocolHandler = Cc["@mozilla.org/network/protocol;1?name=http"]
                           .getService(Ci.nsIHttpProtocolHandler);
 
 // Tracking info/collections.
 var collectionsHelper = track_collections_helper();
 var collections = collectionsHelper.collections;
 
--- a/services/sync/tests/unit/test_score_triggers.js
+++ b/services/sync/tests/unit/test_score_triggers.js
@@ -3,17 +3,16 @@
 
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/engines/clients.js");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/status.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://testing-common/services/sync/rotaryengine.js");
-Cu.import("resource://testing-common/services/sync/utils.js");
 
 // Tracking info/collections.
 var collectionsHelper = track_collections_helper();
 var upd = collectionsHelper.with_updated_collection;
 
 function sync_httpd_setup() {
   let handlers = {};
 
@@ -38,24 +37,16 @@ function sync_httpd_setup() {
 }
 
 async function setUp(server) {
   let engineInfo = await registerRotaryEngine();
   await SyncTestingInfrastructure(server, "johndoe", "ilovejane");
   return engineInfo;
 }
 
</