Merge m-c to autoland. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 30 May 2017 20:47:52 -0400
changeset 409533 c11a042b597c24a2b24e1eab57676dd08aac2c5d
parent 409532 657e72f1f725ef85bf9989bf5f5c82b3fec7573b (current diff)
parent 409524 9a5c710587f9e64bf044602d5e988b152ef4f40c (diff)
child 409534 862c6eb8b0e3bf1600e63dc7cefd442eb80486a4
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.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 m-c to autoland. a=merge
.eslintignore
accessible/tests/browser/e10s/events.js
--- a/.eslintignore
+++ b/.eslintignore
@@ -291,23 +291,21 @@ security/nss/**
 
 # services/ exclusions
 
 # Uses `#filter substitution`
 services/sync/modules/constants.js
 services/sync/services-sync.js
 
 # testing/ exclusions
-testing/firefox-ui/**
 testing/marionette/**
 testing/mochitest/**
 testing/modules/**
-testing/mozbase/**
-testing/profiles/**
-testing/specialpowers/**
+# octothorpe used for pref file comment causes parsing error
+testing/mozbase/mozprofile/tests/files/prefs_with_comments.js
 testing/talos/**
 testing/web-platform/**
 testing/xpcshell/moz-http2/**
 testing/xpcshell/node-http2/**
 
 # Third party services
 services/common/kinto-http-client.js
 services/common/kinto-offline-client.js
--- a/accessible/moz.build
+++ b/accessible/moz.build
@@ -27,13 +27,14 @@ DIRS += [ 'aom',
 
 if CONFIG['MOZ_XUL']:
     DIRS += ['xul']
 
 TEST_DIRS += ['tests/mochitest']
 
 BROWSER_CHROME_MANIFESTS += [
   'tests/browser/browser.ini',
-  'tests/browser/e10s/browser.ini'
+  'tests/browser/e10s/browser.ini',
+  'tests/browser/states/browser.ini'
 ]
 
 with Files("**"):
     BUG_COMPONENT = ("Core", "Disability Access APIs")
\ No newline at end of file
--- a/accessible/tests/browser/browser.ini
+++ b/accessible/tests/browser/browser.ini
@@ -1,11 +1,12 @@
 [DEFAULT]
 
 support-files =
+  events.js
   head.js
   shared-head.js
 
 [browser_shutdown_acc_reference.js]
 [browser_shutdown_doc_acc_reference.js]
 [browser_shutdown_multi_acc_reference_obj.js]
 [browser_shutdown_multi_acc_reference_doc.js]
 [browser_shutdown_multi_reference.js]
--- a/accessible/tests/browser/e10s/browser.ini
+++ b/accessible/tests/browser/e10s/browser.ini
@@ -1,18 +1,18 @@
 [DEFAULT]
 support-files =
-  events.js
   head.js
   doc_treeupdate_ariadialog.html
   doc_treeupdate_ariaowns.html
   doc_treeupdate_imagemap.html
   doc_treeupdate_removal.xhtml
   doc_treeupdate_visibility.html
   doc_treeupdate_whitespace.html
+  !/accessible/tests/browser/events.js
   !/accessible/tests/browser/shared-head.js
   !/accessible/tests/mochitest/*.js
   !/accessible/tests/mochitest/letters.gif
   !/accessible/tests/mochitest/moz.png
 
 # Caching tests
 [browser_caching_attributes.js]
 [browser_caching_description.js]
--- a/accessible/tests/browser/e10s/head.js
+++ b/accessible/tests/browser/e10s/head.js
@@ -1,125 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 'use strict';
 
-/* exported addAccessibleTask, findAccessibleChildByID, isDefunct */
-
 // Load the shared-head file first.
 /* import-globals-from ../shared-head.js */
 Services.scriptloader.loadSubScript(
   'chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js',
   this);
 
-/**
- * A wrapper around browser test add_task that triggers an accessible test task
- * as a new browser test task with given document, data URL or markup snippet.
- * @param  {String}                 doc  URL (relative to current directory) or
- *                                       data URL or markup snippet that is used
- *                                       to test content with
- * @param  {Function|AsyncFunction} task a generator or a function with tests to
- *                                       run
- */
-function addAccessibleTask(doc, task) {
-  add_task(async function() {
-    let url;
-    if (doc.includes('doc_')) {
-      url = `${CURRENT_CONTENT_DIR}e10s/${doc}`;
-    } else {
-      // Assume it's a markup snippet.
-      url = `data:text/html,
-        <html>
-          <head>
-            <meta charset="utf-8"/>
-            <title>Accessibility Test</title>
-          </head>
-          <body id="body">${doc}</body>
-        </html>`;
-    }
-
-    registerCleanupFunction(() => {
-      let observers = Services.obs.enumerateObservers('accessible-event');
-      while (observers.hasMoreElements()) {
-        Services.obs.removeObserver(
-          observers.getNext().QueryInterface(Ci.nsIObserver),
-          'accessible-event');
-      }
-    });
-
-    let onDocLoad = waitForEvent(EVENT_DOCUMENT_LOAD_COMPLETE, 'body');
-
-    await BrowserTestUtils.withNewTab({
-      gBrowser,
-      url: url
-    }, async function(browser) {
-      registerCleanupFunction(() => {
-        if (browser) {
-          let tab = gBrowser.getTabForBrowser(browser);
-          if (tab && !tab.closing && tab.linkedBrowser) {
-            gBrowser.removeTab(tab);
-          }
-        }
-      });
-
-      await SimpleTest.promiseFocus(browser);
-
-      loadFrameScripts(browser,
-        'let { document, window, navigator } = content;',
-        { name: 'common.js', dir: MOCHITESTS_DIR });
-
-      Logger.log(
-        `e10s enabled: ${Services.appinfo.browserTabsRemoteAutostart}`);
-      Logger.log(`Actually remote browser: ${browser.isRemoteBrowser}`);
-
-      let event = await onDocLoad;
-      await task(browser, event.accessible);
-    });
-  });
-}
-
-/**
- * Check if an accessible object has a defunct test.
- * @param  {nsIAccessible}  accessible object to test defunct state for
- * @return {Boolean}        flag indicating defunct state
- */
-function isDefunct(accessible) {
-  let defunct = false;
-  try {
-    let extState = {};
-    accessible.getState({}, extState);
-    defunct = extState.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT;
-  } catch (x) {
-    defunct = true;
-  } finally {
-    if (defunct) {
-      Logger.log(`Defunct accessible: ${prettyName(accessible)}`);
-    }
-  }
-  return defunct;
-}
-
-/**
- * Traverses the accessible tree starting from a given accessible as a root and
- * looks for an accessible that matches based on its DOMNode id.
- * @param  {nsIAccessible}  accessible root accessible
- * @param  {String}         id         id to look up accessible for
- * @return {nsIAccessible?}            found accessible if any
- */
-function findAccessibleChildByID(accessible, id) {
-  if (getAccessibleDOMNodeID(accessible) === id) {
-    return accessible;
-  }
-  for (let i = 0; i < accessible.children.length; ++i) {
-    let found = findAccessibleChildByID(accessible.getChildAt(i), id);
-    if (found) {
-      return found;
-    }
-  }
-}
-
 // Loading and common.js from accessible/tests/mochitest/ for all tests, as
 // well as events.js.
-/* import-globals-from ../../mochitest/common.js */
-/* import-globals-from events.js */
-loadScripts({ name: 'common.js', dir: MOCHITESTS_DIR }, 'e10s/events.js');
+loadScripts({ name: 'common.js', dir: MOCHITESTS_DIR }, 'events.js');
rename from accessible/tests/browser/e10s/events.js
rename to accessible/tests/browser/events.js
--- a/accessible/tests/browser/e10s/events.js
+++ b/accessible/tests/browser/events.js
@@ -1,17 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 'use strict';
 
 // This is loaded by head.js, so has the same globals, hence we import the
 // globals from there.
-/* import-globals-from head.js */
+/* import-globals-from shared-head.js */
+/* import-globals-from ../mochitest/common.js */
 
 /* exported EVENT_REORDER, EVENT_SHOW, EVENT_TEXT_INSERTED, EVENT_TEXT_REMOVED,
             EVENT_DOCUMENT_LOAD_COMPLETE, EVENT_HIDE, EVENT_TEXT_CARET_MOVED,
             EVENT_DESCRIPTION_CHANGE, EVENT_NAME_CHANGE, EVENT_STATE_CHANGE,
             EVENT_VALUE_CHANGE, EVENT_TEXT_VALUE_CHANGE, EVENT_FOCUS,
             waitForEvent, waitForMultipleEvents */
 
 const EVENT_DOCUMENT_LOAD_COMPLETE = nsIAccessibleEvent.EVENT_DOCUMENT_LOAD_COMPLETE;
--- a/accessible/tests/browser/head.js
+++ b/accessible/tests/browser/head.js
@@ -105,17 +105,17 @@ function shutdownPromise(contentBrowser)
     a11yInitOrShutdownPromise();
   return promiseOK(a11yShutdownPromise, '0').then(
     () => ok(true, 'Service shutdown correctly'),
     () => ok(false, 'Service initialized incorrectly'));
 }
 
 /**
  * Simpler verions of waitForEvent defined in
- * accessible/tests/browser/e10s/events.js
+ * accessible/tests/browser/events.js
  */
 function waitForEvent(eventType, expectedId) {
   return new Promise(resolve => {
     let eventObserver = {
       observe(subject) {
         let event = subject.QueryInterface(Ci.nsIAccessibleEvent);
         if (event.eventType === eventType &&
             event.accessible.id === expectedId) {
--- a/accessible/tests/browser/shared-head.js
+++ b/accessible/tests/browser/shared-head.js
@@ -1,16 +1,20 @@
 /* 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';
 
+/* import-globals-from ../mochitest/common.js */
+/* import-globals-from events.js */
+
 /* exported Logger, MOCHITESTS_DIR, invokeSetAttribute, invokeFocus,
             invokeSetStyle, getAccessibleDOMNodeID,
+            addAccessibleTask, findAccessibleChildByID, isDefunct,
             CURRENT_CONTENT_DIR, loadScripts, loadFrameScripts, Cc, Cu */
 
 const { interfaces: Ci, utils: Cu, classes: Cc } = Components;
 
 /**
  * Current browser test directory path used to load subscripts.
  */
 const CURRENT_DIR =
@@ -182,8 +186,114 @@ function loadFrameScripts(browser, ...sc
       }
     } else {
       // Script is a object that has { dir, name } format.
       frameScript = `${script.dir}${script.name}`;
     }
     mm.loadFrameScript(frameScript, false, true);
   }
 }
+
+/**
+ * A wrapper around browser test add_task that triggers an accessible test task
+ * as a new browser test task with given document, data URL or markup snippet.
+ * @param  {String}                 doc  URL (relative to current directory) or
+ *                                       data URL or markup snippet that is used
+ *                                       to test content with
+ * @param  {Function|AsyncFunction} task a generator or a function with tests to
+ *                                       run
+ */
+function addAccessibleTask(doc, task) {
+  add_task(async function() {
+    let url;
+    if (doc.includes('doc_')) {
+      url = `${CURRENT_CONTENT_DIR}e10s/${doc}`;
+    } else {
+      // Assume it's a markup snippet.
+      url = `data:text/html,
+        <html>
+          <head>
+            <meta charset="utf-8"/>
+            <title>Accessibility Test</title>
+          </head>
+          <body id="body">${doc}</body>
+        </html>`;
+    }
+
+    registerCleanupFunction(() => {
+      let observers = Services.obs.enumerateObservers('accessible-event');
+      while (observers.hasMoreElements()) {
+        Services.obs.removeObserver(
+          observers.getNext().QueryInterface(Ci.nsIObserver),
+          'accessible-event');
+      }
+    });
+
+    let onDocLoad = waitForEvent(EVENT_DOCUMENT_LOAD_COMPLETE, 'body');
+
+    await BrowserTestUtils.withNewTab({
+      gBrowser,
+      url: url
+    }, async function(browser) {
+      registerCleanupFunction(() => {
+        if (browser) {
+          let tab = gBrowser.getTabForBrowser(browser);
+          if (tab && !tab.closing && tab.linkedBrowser) {
+            gBrowser.removeTab(tab);
+          }
+        }
+      });
+
+      await SimpleTest.promiseFocus(browser);
+
+      loadFrameScripts(browser,
+        'let { document, window, navigator } = content;',
+        { name: 'common.js', dir: MOCHITESTS_DIR });
+
+      Logger.log(
+        `e10s enabled: ${Services.appinfo.browserTabsRemoteAutostart}`);
+      Logger.log(`Actually remote browser: ${browser.isRemoteBrowser}`);
+
+      let event = await onDocLoad;
+      await task(browser, event.accessible);
+    });
+  });
+}
+
+/**
+ * Check if an accessible object has a defunct test.
+ * @param  {nsIAccessible}  accessible object to test defunct state for
+ * @return {Boolean}        flag indicating defunct state
+ */
+function isDefunct(accessible) {
+  let defunct = false;
+  try {
+    let extState = {};
+    accessible.getState({}, extState);
+    defunct = extState.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT;
+  } catch (x) {
+    defunct = true;
+  } finally {
+    if (defunct) {
+      Logger.log(`Defunct accessible: ${prettyName(accessible)}`);
+    }
+  }
+  return defunct;
+}
+
+/**
+ * Traverses the accessible tree starting from a given accessible as a root and
+ * looks for an accessible that matches based on its DOMNode id.
+ * @param  {nsIAccessible}  accessible root accessible
+ * @param  {String}         id         id to look up accessible for
+ * @return {nsIAccessible?}            found accessible if any
+ */
+function findAccessibleChildByID(accessible, id) {
+  if (getAccessibleDOMNodeID(accessible) === id) {
+    return accessible;
+  }
+  for (let i = 0; i < accessible.children.length; ++i) {
+    let found = findAccessibleChildByID(accessible.getChildAt(i), id);
+    if (found) {
+      return found;
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/states/browser.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+support-files =
+  head.js
+  !/accessible/tests/browser/events.js
+  !/accessible/tests/browser/shared-head.js
+  !/accessible/tests/mochitest/*.js
+
+[browser_test_link.js]
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/states/browser_test_link.js
@@ -0,0 +1,38 @@
+/* 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';
+
+/* import-globals-from ../../mochitest/role.js */
+/* import-globals-from ../../mochitest/states.js */
+loadScripts({ name: 'role.js', dir: MOCHITESTS_DIR },
+            { name: 'states.js', dir: MOCHITESTS_DIR });
+
+async function runTests(browser, accDoc) {
+  let getAcc = id => findAccessibleChildByID(accDoc, id);
+
+  // a: no traversed state
+  testStates(getAcc("link_traversed"), 0, 0, STATE_TRAVERSED);
+
+  let onStateChanged = waitForEvent(EVENT_STATE_CHANGE, "link_traversed");
+  let newWinOpened = BrowserTestUtils.waitForNewWindow();
+
+  await BrowserTestUtils.synthesizeMouse('#link_traversed',
+    1, 1, { shiftKey: true }, browser);
+
+  await onStateChanged;
+  testStates(getAcc("link_traversed"), STATE_TRAVERSED);
+
+  let newWin = await newWinOpened;
+  await BrowserTestUtils.closeWindow(newWin);
+}
+
+/**
+ * Test caching of accessible object states
+ */
+addAccessibleTask(`
+  <a id="link_traversed" href="http://www.example.com" target="_top">
+    example.com
+  </a>`, runTests
+);
new file mode 100644
--- /dev/null
+++ b/accessible/tests/browser/states/head.js
@@ -0,0 +1,15 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+'use strict';
+
+// Load the shared-head file first.
+/* import-globals-from ../shared-head.js */
+Services.scriptloader.loadSubScript(
+  'chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js',
+  this);
+
+// Loading and common.js from accessible/tests/mochitest/ for all tests, as
+// well as events.js.
+loadScripts({ name: 'common.js', dir: MOCHITESTS_DIR }, 'events.js');
--- a/accessible/tests/mochitest/states/test_link.html
+++ b/accessible/tests/mochitest/states/test_link.html
@@ -16,55 +16,16 @@
   <script type="application/javascript"
           src="../role.js"></script>
   <script type="application/javascript"
           src="../states.js"></script>
   <script type="application/javascript"
           src="../events.js"></script>
 
   <script type="application/javascript">
-    //gA11yEventDumpToConsole = true; // debug stuff
-
-    var gLinkWindow = null;
-    function closeDocChecker()
-    {
-      this.__proto__ = new asyncInvokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE);
-
-      this.check = function closeDocChecker_check(aEvent)
-      {
-        gLinkWindow = aEvent.accessible.rootDocument.window;
-      }
-
-      this.match = function closeDocChecker_match(aEvent)
-      {
-        // A temporary about:blank document gets loaded before 'example.com'
-        // document.
-        return aEvent.DOMNode.URL == "http://www.example.com/";
-      }
-    }
-
-    function clickLink(aID)
-    {
-      this.eventSeq = [
-        new stateChangeChecker(STATE_TRAVERSED, false, true, "link_traversed"),
-        new closeDocChecker()
-      ];
-
-      this.invoke = function clickLink_invoke()
-      {
-        synthesizeMouse(getNode("link_traversed"), 1, 1, { shiftKey: true });
-      }
-
-      this.getID = function clickLink_getID()
-      {
-        return "link + '" + aID + "' clicked.";
-      }
-    }
-
-    var gQueue = null;
     function doTest()
     {
       // a@href and its text node
       testStates("link_href", STATE_LINKED);
       testStates(getAccessible("link_href").firstChild, STATE_LINKED);
 
       // a@onclick
       testStates("link_click", STATE_LINKED);
@@ -79,32 +40,17 @@
       testStates("link_arialink", STATE_LINKED);
 
       // a@role="button"
       testStates("link_ariabutton", 0, 0, STATE_LINKED);
 
       // a (no @href, no click event listener)
       testStates("link_notlink", 0, 0, STATE_LINKED);
 
-      // a: no traversed state
-      testStates("link_traversed", 0, 0, STATE_TRAVERSED);
-
-      // a: traversed state
-      //enableLogging("docload"); // debug stuff
-
-      gQueue = new eventQueue();
-      gQueue.push(new clickLink("link_traversed"));
-      gQueue.onFinish =
-        function()
-        {
-          gLinkWindow.close();
-          //disableLogging(); // debug stuff
-        }
-
-      gQueue.invoke(); // will call SimpleTest.finsih();
+      SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 
 </head>
 
@@ -133,12 +79,10 @@
   <a id="link_href" href="http://mozilla.org">link</a>
   <a id="link_click" onclick="">link</a>
   <a id="link_mousedown" onmousedown="">link</a>
   <a id="link_mouseup" onmouseup="">link</a>
   <a id="link_arialink" role="link">aria link</a>
   <a id="link_ariabutton" role="button">aria button</a>
   <a id="link_notlink">not link</a>
 
-  <a id="link_traversed" href="http://www.example.com" target="_top">example.com</a>
-
 </body>
 </html>
--- a/devtools/client/inspector/grids/components/Grid.js
+++ b/devtools/client/inspector/grids/components/Grid.js
@@ -47,17 +47,16 @@ module.exports = createClass({
       onHideBoxModelHighlighter,
       onSetGridOverlayColor,
       onShowBoxModelHighlighterForNode,
       onToggleGridHighlighter,
       onToggleShowGridLineNumbers,
       onToggleShowInfiniteLines,
       onShowGridAreaHighlight,
       onShowGridCellHighlight,
-      onShowGridLineNamesHighlight,
     } = this.props;
 
     return grids.length ?
       dom.div(
         {
           id: "layout-grid-container",
         },
         dom.div(
@@ -79,17 +78,16 @@ module.exports = createClass({
             onToggleShowInfiniteLines,
           })
         ),
         showGridOutline ?
           GridOutline({
             grids,
             onShowGridAreaHighlight,
             onShowGridCellHighlight,
-            onShowGridLineNamesHighlight,
           })
           :
           null
       )
       :
       dom.div(
         {
           className: "layout-no-grids",
--- a/devtools/client/inspector/grids/components/GridOutline.js
+++ b/devtools/client/inspector/grids/components/GridOutline.js
@@ -5,19 +5,16 @@
 "use strict";
 
 const { addons, createClass, DOM: dom, PropTypes } =
   require("devtools/client/shared/vendor/react");
 
 const Types = require("../types");
 const { getStr } = require("../utils/l10n");
 
-const COLUMNS = "cols";
-const ROWS = "rows";
-
 // The delay prior to executing the grid cell highlighting.
 const GRID_HIGHLIGHTING_DEBOUNCE = 50;
 
 // Minimum height/width a grid cell can be
 const MIN_CELL_HEIGHT = 5;
 const MIN_CELL_WIDTH = 5;
 
 // Move SVG grid to the right 100 units, so that it is not flushed against the edge of
@@ -33,17 +30,16 @@ const VIEWPORT_MAX_HEIGHT = 150;
 module.exports = createClass({
 
   displayName: "GridOutline",
 
   propTypes: {
     grids: PropTypes.arrayOf(PropTypes.shape(Types.grid)).isRequired,
     onShowGridAreaHighlight: PropTypes.func.isRequired,
     onShowGridCellHighlight: PropTypes.func.isRequired,
-    onShowGridLineNamesHighlight: PropTypes.func.isRequired,
   },
 
   mixins: [ addons.PureRenderMixin ],
 
   getInitialState() {
     return {
       height: 0,
       selectedGrid: null,
@@ -312,114 +308,16 @@ module.exports = createClass({
         x: 1,
         y: 1,
         width: borderWidth,
         height: borderHeight
       }
     );
   },
 
-  /**
- * Renders the grid line of a grid fragment.
-   *
-   * @param  {Number} id
-   *         The grid id stored on the grid fragment
-   * @param  {Number} gridFragmentIndex
-   *         The index of the grid fragment rendered to the document.
-   * @param  {String} color
-   *         The color of the grid.
-   * @param  {Number} x1
-   *         The starting x-coordinate of the grid line.
-   * @param  {Number} y1
-   *         The starting y-coordinate of the grid line.
-   * @param  {Number} x2
-   *         The ending x-coordinate of the grid line.
-   * @param  {Number} y2
-   *         The ending y-coordinate of the grid line.
-   * @param  {Number} gridLineNumber
-   *         The grid line number of the line being rendered.
-   * @param  {String} lineType
-   *         The grid line name(s) of the line being rendered.
-   */
-  renderGridLine(id, gridFragmentIndex, color, x1, y1, x2, y2,
-    gridLineNumber, lineType) {
-    return dom.line(
-      {
-        key: `${id}-${lineType}-${gridLineNumber}`,
-        className: "grid-outline-line",
-        "data-grid-fragment-index": gridFragmentIndex,
-        "data-grid-id": id,
-        "data-grid-line-color": color,
-        "data-grid-line-number": gridLineNumber,
-        "data-grid-line-type": lineType,
-        x1,
-        y1,
-        x2,
-        y2,
-        onMouseOver: this.onMouseOverLine,
-        onMouseOut: this.onMouseLeaveLine,
-        stroke: "#000000",
-      }
-    );
-  },
-
-  renderGridLines(grid) {
-    return dom.g(
-      {
-        className: "grid-outline-lines",
-      },
-      this.renderLines(grid)
-    );
-  },
-
-  renderLines(grid) {
-    const { id, color, gridFragments } = grid;
-    const { width, height } = this.state;
-    let gridFragmentIndex = 0;
-    const { rows, cols } = gridFragments[gridFragmentIndex];
-    const numberOfColumns = cols.lines.length - 1;
-    const numberOfRows = rows.lines.length - 1;
-    const lines = [];
-
-    let x = 1;
-    let y = 1;
-    let rowBreadth = 0;
-    let colBreadth = 0;
-
-    if (width > 0 && height > 0) {
-      for (let row = 0; row <= numberOfRows; row++) {
-        if (row < numberOfRows) {
-          rowBreadth = GRID_CELL_SCALE_FACTOR * (rows.tracks[row].breadth / 100);
-        }
-        const { number } = rows.lines[row];
-        const rowLine = this.renderGridLine(id, gridFragmentIndex, color,
-          x, y, width - 20, y, number, ROWS);
-
-        lines.push(rowLine);
-        y += rowBreadth;
-      }
-
-      y = 1;
-
-      for (let col = 0; col <= numberOfColumns; col++) {
-        if (col < numberOfColumns) {
-          colBreadth = GRID_CELL_SCALE_FACTOR * (cols.tracks[col].breadth / 100);
-        }
-        const { number } = cols.lines[col];
-        const colLine = this.renderGridLine(id, gridFragmentIndex, color,
-          x, y, x, height - 20, number, COLUMNS);
-
-        lines.push(colLine);
-        x += colBreadth;
-      }
-    }
-
-    return lines;
-  },
-
   onMouseLeaveCell({ target }) {
     const {
       grids,
       onShowGridAreaHighlight,
       onShowGridCellHighlight,
     } = this.props;
     const id = target.dataset.gridId;
     const color = target.closest(".grid-cell-group").dataset.gridLineColor;
@@ -428,54 +326,32 @@ module.exports = createClass({
     onShowGridCellHighlight(grids[id].nodeFront, color);
   },
 
   onMouseOverCell(event) {
     event.persist();
     this.highlightCell(event);
   },
 
-  onMouseLeaveLine({ target }) {
-    const { grids, onShowGridLineNamesHighlight } = this.props;
-    const fragmentIndex = target.dataset.gridFragmentIndex;
-    const id = target.dataset.gridId;
-    const color = target.closest(".grid-cell-group").dataset.gridLineColor;
-
-    onShowGridLineNamesHighlight(grids[id].nodeFront, fragmentIndex, color);
-  },
-
-  onMouseOverLine({ target }) {
-    const { grids, onShowGridLineNamesHighlight } = this.props;
-    const fragmentIndex = target.dataset.gridFragmentIndex;
-    const id = target.dataset.gridId;
-    const lineNumber = target.dataset.gridLineNumber;
-    const type = target.dataset.gridLineType;
-    const color = target.closest(".grid-cell-group").dataset.gridLineColor;
-
-    onShowGridLineNamesHighlight(grids[id].nodeFront, fragmentIndex, color,
-      lineNumber, type);
-  },
-
   renderOutline() {
     const {
       height,
       selectedGrid,
       showOutline,
       width,
     } = this.state;
 
     return showOutline ?
       dom.svg(
         {
           width: "100%",
           height: this.getHeight(),
           viewBox: `${TRANSLATE_X} ${TRANSLATE_Y} ${width} ${height}`,
         },
-        this.renderGridOutline(selectedGrid),
-        this.renderGridLines(selectedGrid)
+        this.renderGridOutline(selectedGrid)
       )
       :
       this.renderCannotShowOutlineText();
   },
 
   render() {
     const { selectedGrid } = this.state;
 
--- a/dom/base/DOMIntersectionObserver.cpp
+++ b/dom/base/DOMIntersectionObserver.cpp
@@ -250,16 +250,22 @@ EdgeInclusiveIntersection(const nsRect& 
   nscoord right = std::min(aRect.XMost(), aOtherRect.XMost());
   nscoord bottom = std::min(aRect.YMost(), aOtherRect.YMost());
   if (left > right || top > bottom) {
     return Nothing();
   }
   return Some(nsRect(left, top, right - left, bottom - top));
 }
 
+enum class BrowsingContextInfo {
+  SimilarOriginBrowsingContext,
+  DifferentOriginBrowsingContext,
+  UnknownBrowsingContext
+};
+
 void
 DOMIntersectionObserver::Update(nsIDocument* aDocument, DOMHighResTimeStamp time)
 {
   Element* root = nullptr;
   nsIFrame* rootFrame = nullptr;
   nsRect rootRect;
 
   if (mRoot) {
@@ -369,21 +375,32 @@ DOMIntersectionObserver::Update(nsIDocum
         }
 
         // TODO: Apply clip-path.
 
         containerFrame = nsLayoutUtils::GetCrossDocParentFrame(containerFrame);
       }
     }
 
-    nsRect rootIntersectionRect = rootRect;
-    bool isInSimilarOriginBrowsingContext = rootFrame && targetFrame &&
-                                            CheckSimilarOrigin(root, target);
+    nsRect rootIntersectionRect;
+    BrowsingContextInfo isInSimilarOriginBrowsingContext =
+      BrowsingContextInfo::UnknownBrowsingContext;
+
+    if (rootFrame && targetFrame) {
+      rootIntersectionRect = rootRect;
+    }
 
-    if (isInSimilarOriginBrowsingContext) {
+    if (root && target) {
+      isInSimilarOriginBrowsingContext = CheckSimilarOrigin(root, target) ?
+        BrowsingContextInfo::SimilarOriginBrowsingContext :
+        BrowsingContextInfo::DifferentOriginBrowsingContext;
+    }
+
+    if (isInSimilarOriginBrowsingContext ==
+        BrowsingContextInfo::SimilarOriginBrowsingContext) {
       rootIntersectionRect.Inflate(rootMargin);
     }
 
     if (intersectionRect.isSome()) {
       nsRect intersectionRectRelativeToRoot =
         nsLayoutUtils::TransformFrameRectToAncestor(
           targetFrame,
           intersectionRect.value(),
@@ -423,17 +440,19 @@ DOMIntersectionObserver::Update(nsIDocum
       }
     } else if (intersectionRect.isSome()) {
       threshold = 0;
     }
 
     if (target->UpdateIntersectionObservation(this, threshold)) {
       QueueIntersectionObserverEntry(
         target, time,
-        isInSimilarOriginBrowsingContext ? Some(rootIntersectionRect) : Nothing(),
+        isInSimilarOriginBrowsingContext ==
+          BrowsingContextInfo::DifferentOriginBrowsingContext ?
+          Nothing() : Some(rootIntersectionRect),
         targetRect, intersectionRect, intersectionRatio
       );
     }
   }
 }
 
 void
 DOMIntersectionObserver::QueueIntersectionObserverEntry(Element* aTarget,
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -2182,17 +2182,17 @@ imgLoader::LoadImage(nsIURI* aURI,
       // entry.
       if (entry->HasNoProxies()) {
         LOG_FUNC_WITH_PARAM(gImgLog,
           "imgLoader::LoadImage() adding proxyless entry", "uri", key.Spec());
         MOZ_ASSERT(!request->HasCacheEntry(),
           "Proxyless entry's request has cache entry!");
         request->SetCacheEntry(entry);
 
-        if (mCacheTracker) {
+        if (mCacheTracker && entry->GetExpirationState()->IsTracked()) {
           mCacheTracker->MarkUsed(entry);
         }
       }
 
       entry->Touch();
 
     } else {
       // We can't use this entry. We'll try to load it off the network, and if
@@ -2449,17 +2449,17 @@ imgLoader::LoadImageWithChannel(nsIChann
         if (entry->HasNoProxies()) {
           LOG_FUNC_WITH_PARAM(gImgLog,
             "imgLoader::LoadImageWithChannel() adding proxyless entry",
             "uri", key.Spec());
           MOZ_ASSERT(!request->HasCacheEntry(),
             "Proxyless entry's request has cache entry!");
           request->SetCacheEntry(entry);
 
-          if (mCacheTracker) {
+          if (mCacheTracker && entry->GetExpirationState()->IsTracked()) {
             mCacheTracker->MarkUsed(entry);
           }
         }
       }
     }
   }
 
   nsCOMPtr<nsILoadGroup> loadGroup;
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -9129,18 +9129,16 @@ BytecodeEmitter::emitStatement(ParseNode
                 }
             }
 
             if (directive) {
                 if (!reportExtraWarning(pn2, JSMSG_CONTRARY_NONDIRECTIVE, directive))
                     return false;
             }
         } else {
-            current->currentLine = parser.tokenStream().srcCoords.lineNum(pn2->pn_pos.begin);
-            current->lastColumn = 0;
             if (!reportExtraWarning(pn2, JSMSG_USELESS_EXPR))
                 return false;
         }
     }
 
     return true;
 }
 
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -186,19 +186,30 @@ struct MOZ_STACK_CLASS BytecodeEmitter
 
     Rooted<LazyScript*> lazyScript; /* the lazy script if mode is LazyFunction,
                                         nullptr otherwise. */
 
     struct EmitSection {
         BytecodeVector code;        /* bytecode */
         SrcNotesVector notes;       /* source notes, see below */
         ptrdiff_t   lastNoteOffset; /* code offset for last source note */
-        uint32_t    currentLine;    /* line number for tree-based srcnote gen */
-        uint32_t    lastColumn;     /* zero-based column index on currentLine of
-                                       last SRC_COLSPAN-annotated opcode */
+
+        // Line number for srcnotes.
+        //
+        // WARNING: If this becomes out of sync with already-emitted srcnotes,
+        // we can get undefined behavior.
+        uint32_t    currentLine;
+
+        // Zero-based column index on currentLine of last SRC_COLSPAN-annotated
+        // opcode.
+        //
+        // WARNING: If this becomes out of sync with already-emitted srcnotes,
+        // we can get undefined behavior.
+        uint32_t    lastColumn;
+
         JumpTarget lastTarget;      // Last jump target emitted.
 
         EmitSection(JSContext* cx, uint32_t lineNum)
           : code(cx), notes(cx), lastNoteOffset(0), currentLine(lineNum), lastColumn(0),
             lastTarget{ -1 - ptrdiff_t(JSOP_JUMPTARGET_LENGTH) }
         {}
     };
     EmitSection prologue, main, *current;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parser/bug-1355046.js
@@ -0,0 +1,8 @@
+// |jit-test| error: ReferenceError
+
+var localstr = "";
+for (var i = 0; i < 0xFFFC; ++i)
+  localstr += ('\f') + i + "; ";
+var arg = "x";
+var body = localstr + "for (var i = 0; i < 4; ++i) arr[i](x-1);";
+(new Function(arg, body))(1000);
--- a/layout/forms/test/test_bug348236.html
+++ b/layout/forms/test/test_bug348236.html
@@ -59,17 +59,17 @@ addLoadEvent(function test() {
         eSelect.onchangeCount = 0
 
         // Drop the SELECT down.
         keypressOnSelect(key, modifiers)
         // This timeout and the following are necessary to let the sent events take effect.
         setTimeout(cont1, timeout)
         function cont1() {
             // Move the mouse over option 3 (90 = 3 (rows) * 24 (row height) + 18).
-            WinUtils.sendMouseEvent("mousemove", 355, 90, 0, 0, 0, true)
+            WinUtils.sendMouseEvent("mousemove", 355, 92, 0, 0, 0, true)
             setTimeout(cont2, timeout)
         }
         function cont2() {
             // Close the select.
             keypressOnSelect(key, modifiers)
             setTimeout(cont3, timeout)
         }
         function cont3() {
--- a/layout/tools/reftest/reftest.jsm
+++ b/layout/tools/reftest/reftest.jsm
@@ -1722,17 +1722,17 @@ function RecordResult(testRunTime, error
             var expected = gURLs[0].expected;
 
             if (maxDifference.value > 0 && maxDifference.value <= gURLs[0].fuzzyMaxDelta &&
                 differences <= gURLs[0].fuzzyMaxPixels) {
                 if (equal) {
                     throw "Inconsistent result from compareCanvases.";
                 }
                 equal = expected == EXPECTED_FUZZY;
-                logger.info("REFTEST fuzzy match");
+                logger.info(`REFTEST fuzzy match (${maxDifference.value}, ${differences}) <= (${gURLs[0].fuzzyMaxDelta}, ${gURLs[0].fuzzyMaxPixels})`);
             }
 
             var failedExtraCheck = gFailedNoPaint || gFailedOpaqueLayer || gFailedAssignedLayer;
 
             // whether the comparison result matches what is in the manifest
             var test_passed = (equal == (gURLs[0].type == TYPE_REFTEST_EQUAL)) && !failedExtraCheck;
 
             output = outputs[expected][test_passed];
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4649,17 +4649,17 @@ pref("network.tcp.keepalive.idle_time", 
 pref("network.tcp.keepalive.retry_interval", 1); // seconds
 #endif
 // Default maximum probe retransmissions.
 // Linux only; not configurable on Win and Mac; fixed at 10 and 8 respectively.
 #if defined(XP_UNIX) && !defined(XP_MACOSX)
 pref("network.tcp.keepalive.probe_count", 4);
 #endif
 
-pref("network.tcp.tcp_fastopen_enable", false);
+pref("network.tcp.tcp_fastopen_enable", true);
 pref("network.tcp.tcp_fastopen_consecutive_failure_limit", 5);
 
 // Whether to disable acceleration for all widgets.
 pref("layers.acceleration.disabled", false);
 // Preference that when switched at runtime will run a series of benchmarks
 // and output the result to stderr.
 pref("layers.bench.enabled", false);
 
--- a/taskcluster/ci/android-stuff/kind.yml
+++ b/taskcluster/ci/android-stuff/kind.yml
@@ -22,16 +22,17 @@ jobs:
         treeherder:
             platform: android-4-0-armv7-api15/opt
             kind: other
             tier: 2
             symbol: tc(Deps)
         worker-type: aws-provisioner-v1/gecko-{level}-b-android
         worker:
             implementation: docker-worker
+            os: linux
             docker-image: {in-tree: android-gradle-build}
             env:
                 GRADLE_USER_HOME: "/home/worker/workspace/build/src/dotgradle-online"
                 MH_BUILD_POOL: "taskcluster"
                 MH_CUSTOM_BUILD_VARIANT_CFG: "api-15-gradle-dependencies"
                 MOZHARNESS_ACTIONS: "get-secrets build multi-l10n update"
                 MOZHARNESS_CONFIG: >
                     builds/releng_base_android_64_builds.py
@@ -67,16 +68,17 @@ jobs:
         treeherder:
             platform: android-4-0-armv7-api15/opt
             kind: test
             tier: 2
             symbol: tc(test)
         worker-type: aws-provisioner-v1/gecko-{level}-b-android
         worker:
             implementation: docker-worker
+            os: linux
             docker-image: {in-tree: desktop-build}
             env:
                 GRADLE_USER_HOME: "/home/worker/workspace/build/src/dotgradle"
                 MH_BUILD_POOL: "taskcluster"
                 MH_CUSTOM_BUILD_VARIANT_CFG: "android-test"
                 MOZHARNESS_ACTIONS: "get-secrets build multi-l10n update"
                 MOZHARNESS_CONFIG: >
                     builds/releng_base_android_64_builds.py
@@ -113,16 +115,17 @@ jobs:
         treeherder:
             platform: android-4-0-armv7-api15/opt
             kind: test
             tier: 2
             symbol: tc(lint)
         worker-type: aws-provisioner-v1/gecko-{level}-b-android
         worker:
             implementation: docker-worker
+            os: linux
             docker-image: {in-tree: desktop-build}
             env:
                 GRADLE_USER_HOME: "/home/worker/workspace/build/src/dotgradle"
                 MH_BUILD_POOL: "taskcluster"
                 MH_CUSTOM_BUILD_VARIANT_CFG: "android-lint"
                 MOZHARNESS_ACTIONS: "get-secrets build multi-l10n update"
                 MOZHARNESS_CONFIG: >
                     builds/releng_base_android_64_builds.py
@@ -181,16 +184,17 @@ jobs:
         treeherder:
             platform: android-4-0-armv7-api15/opt
             kind: test
             tier: 2
             symbol: tc(checkstyle)
         worker-type: aws-provisioner-v1/gecko-{level}-b-android
         worker:
             implementation: docker-worker
+            os: linux
             docker-image: {in-tree: desktop-build}
             env:
                 GRADLE_USER_HOME: "/home/worker/workspace/build/src/dotgradle"
                 MH_BUILD_POOL: "taskcluster"
                 MH_CUSTOM_BUILD_VARIANT_CFG: "android-checkstyle"
                 MOZHARNESS_ACTIONS: "get-secrets build multi-l10n update"
                 MOZHARNESS_CONFIG: >
                     builds/releng_base_android_64_builds.py
@@ -230,16 +234,17 @@ jobs:
         treeherder:
             platform: android-4-0-armv7-api15/opt
             kind: test
             tier: 2
             symbol: tc(findbugs)
         worker-type: aws-provisioner-v1/gecko-{level}-b-android
         worker:
             implementation: docker-worker
+            os: linux
             docker-image: {in-tree: desktop-build}
             env:
                 GRADLE_USER_HOME: "/home/worker/workspace/build/src/dotgradle"
                 MH_BUILD_POOL: "taskcluster"
                 MH_CUSTOM_BUILD_VARIANT_CFG: "android-findbugs"
                 MOZHARNESS_ACTIONS: "get-secrets build multi-l10n update"
                 MOZHARNESS_CONFIG: >
                     builds/releng_base_android_64_builds.py
--- a/taskcluster/ci/artifact-build/kind.yml
+++ b/taskcluster/ci/artifact-build/kind.yml
@@ -17,17 +17,16 @@ jobs:
             job-name: linux64-artifact-opt
         treeherder:
             platform: linux64/opt
             kind: build
             symbol: AB
             tier: 2
         worker-type: aws-provisioner-v1/gecko-{level}-b-linux
         worker:
-            implementation: docker-worker
             docker-image: {in-tree: desktop-build}
             max-run-time: 36000
         run:
             using: mozharness
             actions: [get-secrets build generate-build-stats]
             config:
                 - builds/releng_sub_linux_configs/64_artifact.py
                 - balrog/production.py
--- a/taskcluster/ci/build/android.yml
+++ b/taskcluster/ci/build/android.yml
@@ -3,17 +3,16 @@ android-api-15/debug:
     index:
         product: mobile
         job-name: android-api-15-debug
     treeherder:
         platform: android-4-0-armv7-api15/debug
         symbol: tc(B)
     worker-type: aws-provisioner-v1/gecko-{level}-b-android
     worker:
-        implementation: docker-worker
         max-run-time: 7200
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats multi-l10n update]
         config:
             - builds/releng_base_android_64_builds.py
             - disable_signing.py
             - platform_supports_post_upload_to_latest.py
@@ -27,17 +26,16 @@ android-x86/opt:
     index:
         product: mobile
         job-name: android-x86-opt
     treeherder:
         platform: android-4-2-x86/opt
         symbol: tc(B)
     worker-type: aws-provisioner-v1/gecko-{level}-b-android
     worker:
-        implementation: docker-worker
         max-run-time: 7200
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats multi-l10n update]
         config:
             - builds/releng_base_android_64_builds.py
             - disable_signing.py
             - platform_supports_post_upload_to_latest.py
@@ -54,17 +52,16 @@ android-x86-nightly/opt:
         product: mobile
         job-name: android-x86-opt
         type: nightly
     treeherder:
         platform: android-4-2-x86/opt
         symbol: tc(N)
     worker-type: aws-provisioner-v1/gecko-{level}-b-android
     worker:
-        implementation: docker-worker
         max-run-time: 7200
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats multi-l10n update]
         config:
             - builds/releng_base_android_64_builds.py
             - disable_signing.py
             - platform_supports_post_upload_to_latest.py
@@ -79,17 +76,16 @@ android-api-15/opt:
     index:
         product: mobile
         job-name: android-api-15-opt
     treeherder:
         platform: android-4-0-armv7-api15/opt
         symbol: tc(B)
     worker-type: aws-provisioner-v1/gecko-{level}-b-android
     worker:
-        implementation: docker-worker
         max-run-time: 7200
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats multi-l10n update]
         config:
             - builds/releng_base_android_64_builds.py
             - disable_signing.py
             - platform_supports_post_upload_to_latest.py
@@ -106,17 +102,16 @@ android-api-15-nightly/opt:
         product: mobile
         job-name: android-api-15-opt
         type: nightly-with-multi-l10n
     treeherder:
         platform: android-4-0-armv7-api15/opt
         symbol: tc(N)
     worker-type: aws-provisioner-v1/gecko-{level}-b-android
     worker:
-        implementation: docker-worker
         max-run-time: 7200
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats multi-l10n update]
         config:
             - builds/releng_base_android_64_builds.py
             - disable_signing.py
             - platform_supports_post_upload_to_latest.py
@@ -131,17 +126,16 @@ android-x86-old-id/opt:
     index:
         product: mobile
         job-name: android-x86-old-id-opt
     treeherder:
         platform: android-4-2-x86-old-id/opt
         symbol: tc(B)
     worker-type: aws-provisioner-v1/gecko-{level}-b-android
     worker:
-        implementation: docker-worker
         max-run-time: 7200
     run:
         using: mozharness
         actions: [get-secrets build multi-l10n update]
         config:
             - builds/releng_base_android_64_builds.py
             - disable_signing.py
             - platform_supports_post_upload_to_latest.py
@@ -159,17 +153,16 @@ android-x86-old-id-nightly/opt:
         product: mobile
         job-name: android-x86-old-id-opt
         type: nightly
     treeherder:
         platform: android-4-2-x86-old-id/opt
         symbol: tc(N)
     worker-type: aws-provisioner-v1/gecko-{level}-b-android
     worker:
-        implementation: docker-worker
         max-run-time: 7200
     run:
         using: mozharness
         actions: [get-secrets build multi-l10n update]
         config:
             - builds/releng_base_android_64_builds.py
             - disable_signing.py
             - platform_supports_post_upload_to_latest.py
@@ -185,17 +178,16 @@ android-api-15-old-id/opt:
     index:
         product: mobile
         job-name: android-api-15-old-id-opt
     treeherder:
         platform: android-4-0-armv7-api15-old-id/opt
         symbol: tc(B)
     worker-type: aws-provisioner-v1/gecko-{level}-b-android
     worker:
-        implementation: docker-worker
         max-run-time: 7200
     run:
         using: mozharness
         actions: [get-secrets build multi-l10n update]
         config:
             - builds/releng_base_android_64_builds.py
             - disable_signing.py
             - platform_supports_post_upload_to_latest.py
@@ -213,17 +205,16 @@ android-api-15-old-id-nightly/opt:
         product: mobile
         job-name: android-api-15-old-id-opt
         type: nightly-with-multi-l10n
     treeherder:
         platform: android-4-0-armv7-api15-old-id/opt
         symbol: tc(N)
     worker-type: aws-provisioner-v1/gecko-{level}-b-android
     worker:
-        implementation: docker-worker
         max-run-time: 7200
     run:
         using: mozharness
         actions: [get-secrets build multi-l10n update]
         config:
             - builds/releng_base_android_64_builds.py
             - disable_signing.py
             - platform_supports_post_upload_to_latest.py
@@ -240,17 +231,16 @@ android-api-15-gradle/opt:
         product: mobile
         job-name: android-api-15-gradle-opt
     treeherder:
         platform: android-api-15-gradle/opt
         symbol: tc(Bg)
         tier: 2
     worker-type: aws-provisioner-v1/gecko-{level}-b-android
     worker:
-        implementation: docker-worker
         max-run-time: 7200
         env:
             # Bug 1292762 - Set GRADLE_USER_HOME to avoid sdk-manager-plugin intermittent
             GRADLE_USER_HOME: /home/worker/workspace/build/src/dotgradle
         artifacts:
           - name: public/android/maven
             path: /home/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview/maven/
             type: directory
@@ -277,17 +267,16 @@ android-aarch64/opt:
     index:
         product: mobile
         job-name: android-aarch64-opt
     treeherder:
         platform: android-5-0-aarch64/opt
         symbol: tc(B)
     worker-type: aws-provisioner-v1/gecko-{level}-b-android
     worker:
-        implementation: docker-worker
         max-run-time: 7200
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats multi-l10n update]
         config:
             - builds/releng_base_android_64_builds.py
             - disable_signing.py
             - platform_supports_post_upload_to_latest.py
@@ -304,17 +293,16 @@ android-aarch64-nightly/opt:
         product: mobile
         job-name: android-aarch64-opt
         type: nightly
     treeherder:
         platform: android-5-0-aarch64/opt
         symbol: tc(N)
     worker-type: aws-provisioner-v1/gecko-{level}-b-android
     worker:
-        implementation: docker-worker
         max-run-time: 7200
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats multi-l10n update]
         config:
             - builds/releng_base_android_64_builds.py
             - disable_signing.py
             - platform_supports_post_upload_to_latest.py
--- a/taskcluster/ci/build/linux.yml
+++ b/taskcluster/ci/build/linux.yml
@@ -3,17 +3,16 @@ linux64/opt:
     index:
         product: firefox
         job-name: linux64-opt
     treeherder:
         platform: linux64/opt
         symbol: tc(B)
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         max-run-time: 36000
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats check-test update]
         config:
             - builds/releng_base_linux_64_builds.py
             - balrog/production.py
         script: "mozharness/scripts/fx_desktop_build.py"
@@ -26,17 +25,16 @@ linux64/pgo:
     index:
         product: firefox
         job-name: linux64-pgo
     treeherder:
         platform: linux64/pgo
         symbol: tc(B)
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         max-run-time: 36000
     coalesce-name: linux64-pgo
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats check-test update]
         options: [enable-pgo]
         config:
             - builds/releng_base_linux_64_builds.py
@@ -51,17 +49,16 @@ linux64/debug:
     index:
         product: firefox
         job-name: linux64-debug
     treeherder:
         platform: linux64/debug
         symbol: tc(B)
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         max-run-time: 36000
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats check-test update]
         config:
             - builds/releng_base_linux_64_builds.py
             - balrog/production.py
         script: "mozharness/scripts/fx_desktop_build.py"
@@ -78,17 +75,16 @@ linux64-devedition-nightly/opt:
         product: devedition
         job-name: linux64-opt
         type: nightly
     treeherder:
         platform: linux64-devedition/opt
         symbol: tc(N)
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         max-run-time: 36000
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats check-test update]
         config:
             - builds/releng_base_linux_64_builds.py
             - disable_signing.py
             - balrog/production.py
@@ -104,17 +100,16 @@ linux/opt:
     index:
         product: firefox
         job-name: linux-opt
     treeherder:
         platform: linux32/opt
         symbol: tc(B)
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         max-run-time: 36000
     coalesce-name: opt_linux32
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats check-test update]
         config:
             - builds/releng_base_linux_32_builds.py
             - balrog/production.py
@@ -128,17 +123,16 @@ linux/debug:
     index:
         product: firefox
         job-name: linux-debug
     treeherder:
         platform: linux32/debug
         symbol: tc(B)
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         max-run-time: 36000
     coalesce-name: dbg_linux32
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats check-test update]
         config:
             - builds/releng_base_linux_32_builds.py
             - balrog/production.py
@@ -153,17 +147,16 @@ linux/pgo:
     index:
         product: firefox
         job-name: linux-pgo
     treeherder:
         platform: linux32/pgo
         symbol: tc(B)
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         max-run-time: 36000
     coalesce-name: linux32-pgo
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats check-test update]
         options: [enable-pgo]
         config:
             - builds/releng_base_linux_32_builds.py
@@ -181,17 +174,16 @@ linux-devedition-nightly/opt:
         product: devedition
         job-name: linux-opt
         type: nightly
     treeherder:
         platform: linux32-devedition/opt
         symbol: tc(N)
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         max-run-time: 36000
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats check-test update]
         config:
             - builds/releng_base_linux_32_builds.py
             - disable_signing.py
             - balrog/production.py
@@ -210,17 +202,16 @@ linux-nightly/opt:
         product: firefox
         job-name: linux-opt
         type: nightly
     treeherder:
         platform: linux32/opt
         symbol: tc(N)
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         max-run-time: 36000
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats check-test update]
         config:
             - builds/releng_base_linux_32_builds.py
             - disable_signing.py
             - taskcluster_nightly.py
@@ -235,17 +226,16 @@ linux64-asan/opt:
     index:
         product: firefox
         job-name: linux64-asan-opt
     treeherder:
         platform: linux64/asan
         symbol: tc(Bo)
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         max-run-time: 36000
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats check-test update]
         config:
             - builds/releng_base_linux_64_builds.py
             - balrog/production.py
         script: "mozharness/scripts/fx_desktop_build.py"
@@ -260,17 +250,16 @@ linux64-asan-fuzzing/opt:
     index:
         product: firefox
         job-name: linux64-fuzzing-asan-opt
     treeherder:
         platform: linux64/asan
         symbol: tc(Bof)
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         max-run-time: 36000
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats check-test update]
         config:
             - builds/releng_base_linux_64_builds.py
             - balrog/production.py
         script: "mozharness/scripts/fx_desktop_build.py"
@@ -284,17 +273,16 @@ linux64-asan/debug:
     index:
         product: firefox
         job-name: linux64-asan-debug
     treeherder:
         platform: linux64/asan
         symbol: tc(Bd)
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         max-run-time: 36000
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats check-test update]
         config:
             - builds/releng_base_linux_64_builds.py
             - balrog/production.py
         script: "mozharness/scripts/fx_desktop_build.py"
@@ -311,17 +299,16 @@ linux64-nightly/opt:
         product: firefox
         job-name: linux64-opt
         type: nightly
     treeherder:
         platform: linux64/opt
         symbol: tc(N)
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         max-run-time: 36000
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats check-test update]
         config:
             - builds/releng_base_linux_64_builds.py
             - disable_signing.py
             - taskcluster_nightly.py
@@ -336,17 +323,16 @@ linux64-stylo/opt:
         product: firefox
         job-name: linux64-stylo-opt
     treeherder:
         platform: linux64-stylo/opt
         symbol: tc(B)
         tier: 2
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         max-run-time: 3600
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats check-test update]
         config:
             - builds/releng_base_linux_64_builds.py
             - balrog/production.py
         script: "mozharness/scripts/fx_desktop_build.py"
@@ -362,17 +348,16 @@ linux64-stylo/debug:
         product: firefox
         job-name: linux64-stylo-debug
     treeherder:
         platform: linux64-stylo/debug
         symbol: tc(B)
         tier: 2
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         max-run-time: 3600
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats check-test update]
         config:
             - builds/releng_base_linux_64_builds.py
             - balrog/production.py
         script: "mozharness/scripts/fx_desktop_build.py"
@@ -389,17 +374,16 @@ linux64-jsdcov/opt:
         job-name: linux64-jsdcov-opt
     treeherder:
         platform: linux64-jsdcov/opt
         symbol: tc(B)
         tier: 2
     run-on-projects: [ ]
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         max-run-time: 36000
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats check-test update]
         config:
             - builds/releng_base_linux_64_builds.py
             - balrog/production.py
         script: "mozharness/scripts/fx_desktop_build.py"
@@ -415,17 +399,16 @@ linux64-ccov/opt:
     needs-sccache: false
     treeherder:
         platform: linux64-ccov/opt
         symbol: tc(B)
         tier: 2
     run-on-projects: [ ]
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         max-run-time: 36000
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats check-test update]
         config:
             - builds/releng_base_linux_64_builds.py
             - balrog/production.py
         script: "mozharness/scripts/fx_desktop_build.py"
@@ -440,17 +423,16 @@ linux64-add-on-devel/opt:
         product: firefox
         job-name: linux64-add-on-devel
     treeherder:
         platform: linux64-add-on-devel/opt
         symbol: tc(B)
         tier: 2
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         max-run-time: 36000
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats check-test update]
         config:
             - builds/releng_base_linux_64_builds.py
             - balrog/production.py
         script: "mozharness/scripts/fx_desktop_build.py"
--- a/taskcluster/ci/build/macosx.yml
+++ b/taskcluster/ci/build/macosx.yml
@@ -4,17 +4,16 @@ macosx64/debug:
         product: firefox
         job-name: macosx64-debug
     treeherder:
         platform: osx-10-7/debug
         symbol: tc(B)
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
     worker:
-        implementation: docker-worker
         max-run-time: 36000
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats update]
         config:
             - builds/releng_base_mac_64_cross_builds.py
             - balrog/production.py
         script: "mozharness/scripts/fx_desktop_build.py"
@@ -27,18 +26,16 @@ macosx64/opt:
     index:
         product: firefox
         job-name: macosx64-opt
     treeherder:
         platform: osx-10-7/opt
         symbol: tc(B)
         tier: 2
     worker-type: buildbot-bridge/buildbot-bridge
-    worker:
-        implementation: buildbot-bridge
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats update]
         config:
             - builds/releng_base_mac_64_builds.py
             - balrog/production.py
         script: "mozharness/scripts/fx_desktop_build.py"
         secrets: true
@@ -50,17 +47,16 @@ macosx64-add-on-devel/opt:
         product: firefox
         job-name: macosx64-add-on-devel
     treeherder:
         platform: osx-10-7-add-on-devel/opt
         symbol: tc(B)
         tier: 2
     worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
     worker:
-        implementation: docker-worker
         max-run-time: 36000
     run:
        using: mozharness
        actions: [get-secrets build generate-build-stats update]
        config:
             - builds/releng_base_mac_64_cross_builds.py
             - balrog/production.py
        script: "mozharness/scripts/fx_desktop_build.py"
@@ -78,17 +74,16 @@ macosx64-nightly/opt:
         job-name: macosx64-opt
         type: nightly
     treeherder:
         platform: osx-10-7/opt
         symbol: tc(N)
         tier: 2
     worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
     worker:
-        implementation: docker-worker
         max-run-time: 36000
     run:
         using: mozharness
         actions: [get-secrets build generate-build-stats update]
         config:
             - builds/releng_base_mac_64_cross_builds.py
             - disable_signing.py
             - taskcluster_nightly.py
--- a/taskcluster/ci/build/windows.yml
+++ b/taskcluster/ci/build/windows.yml
@@ -4,17 +4,16 @@ win32/debug:
         product: firefox
         job-name: win32-debug
     treeherder:
         platform: windows2012-32/debug
         symbol: tc(B)
         tier: 2
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
-        implementation: generic-worker
         max-run-time: 7200
     run:
         using: mozharness
         script: mozharness/scripts/fx_desktop_build.py
         config:
             - builds/taskcluster_firefox_windows_32_debug.py
 
 win32/opt:
@@ -23,17 +22,16 @@ win32/opt:
         product: firefox
         job-name: win32-opt
     treeherder:
         platform: windows2012-32/opt
         symbol: tc(B)
         tier: 2
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
-        implementation: generic-worker
         max-run-time: 7200
     run:
         using: mozharness
         script: mozharness/scripts/fx_desktop_build.py
         config:
             - builds/taskcluster_firefox_windows_32_opt.py
 
 win32/pgo:
@@ -42,17 +40,16 @@ win32/pgo:
         product: firefox
         job-name: win32-pgo
     treeherder:
         platform: windows2012-32/pgo
         symbol: tc(B)
         tier: 2
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
-        implementation: generic-worker
         max-run-time: 9000
     run:
         using: mozharness
         options: [enable-pgo]
         script: mozharness/scripts/fx_desktop_build.py
         config:
             - builds/taskcluster_firefox_windows_32_opt.py
 
@@ -62,17 +59,16 @@ win64/debug:
         product: firefox
         job-name: win64-debug
     treeherder:
         platform: windows2012-64/debug
         symbol: tc(B)
         tier: 2
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
-        implementation: generic-worker
         max-run-time: 7200
     run:
         using: mozharness
         script: mozharness/scripts/fx_desktop_build.py
         config:
             - builds/taskcluster_firefox_windows_64_debug.py
 
 win64/opt:
@@ -81,17 +77,16 @@ win64/opt:
         product: firefox
         job-name: win64-opt
     treeherder:
         platform: windows2012-64/opt
         symbol: tc(B)
         tier: 2
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
-        implementation: generic-worker
         max-run-time: 7200
     run:
         using: mozharness
         script: mozharness/scripts/fx_desktop_build.py
         config:
             - builds/taskcluster_firefox_windows_64_opt.py
 
 win64-nightly/opt:
@@ -103,17 +98,16 @@ win64-nightly/opt:
     attributes:
         nightly: true
     treeherder:
         platform: windows2012-64/opt
         symbol: tc(N)
         tier: 2
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
-        implementation: generic-worker
         max-run-time: 7200
     run:
         using: mozharness
         script: mozharness/scripts/fx_desktop_build.py
         config:
             - builds/taskcluster_firefox_windows_64_opt.py
             - disable_signing.py
             - taskcluster_nightly.py
@@ -124,17 +118,16 @@ win64/pgo:
         product: firefox
         job-name: win64-pgo
     treeherder:
         platform: windows2012-64/pgo
         symbol: tc(B)
         tier: 2
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
-        implementation: generic-worker
         max-run-time: 10800
     run:
         using: mozharness
         options: [enable-pgo]
         script: mozharness/scripts/fx_desktop_build.py
         config:
             - builds/taskcluster_firefox_windows_64_opt.py
 
@@ -144,17 +137,16 @@ win32-add-on-devel/opt:
           product: firefox
           job-name: win32-add-on-devel
       treeherder:
           platform: windows2012-32-add-on-devel/opt
           symbol: tc(B)
           tier: 2
       worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
       worker:
-          implementation: generic-worker
           max-run-time: 10800
       run:
           using: mozharness
           script: "mozharness/scripts/fx_desktop_build.py"
           config:
               - builds/taskcluster_firefox_windows_32_addondevel.py
               - balrog/production.py
       run-on-projects: [ 'mozilla-beta', 'mozilla-release', 'mozilla-esr45' ]
@@ -165,17 +157,16 @@ win64-add-on-devel/opt:
          product: firefox
          job-name: win64-add-on-devel
      treeherder:
          platform: windows2012-64-add-on-devel/opt 
          symbol: tc(B)
          tier: 2
      worker-type: aws-provisioner-v1/gecko-{level}-b-win2012 
      worker:
-         implementation: generic-worker
          max-run-time: 10800
      run:
          using: mozharness
          script: "mozharness/scripts/fx_desktop_build.py"
          config:
              - builds/taskcluster_firefox_windows_64_addondevel.py 
              - balrog/production.py
      run-on-projects: [ 'mozilla-beta', 'mozilla-release', 'mozilla-esr45' ]
@@ -186,17 +177,16 @@ win64-asan/debug:
         product: firefox
         job-name: win64-asan-debug
     treeherder:
         platform: windows2012-64/asan
         symbol: tc(Bd)
         tier: 3
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
-        implementation: generic-worker
         max-run-time: 7200
     run:
         using: mozharness
         script: mozharness/scripts/fx_desktop_build.py
         config:
             - builds/taskcluster_firefox_win64_asan_debug.py
     run-on-projects: []
 
@@ -206,16 +196,15 @@ win64-asan/opt:
         product: firefox
         job-name: win64-asan-opt
     treeherder:
         platform: windows2012-64/asan
         symbol: tc(Bo)
         tier: 3
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
-        implementation: generic-worker
         max-run-time: 7200
     run:
         using: mozharness
         script: mozharness/scripts/fx_desktop_build.py
         config:
             - builds/taskcluster_firefox_win64_asan_opt.py
     run-on-projects: []
--- a/taskcluster/ci/hazard/kind.yml
+++ b/taskcluster/ci/hazard/kind.yml
@@ -10,17 +10,16 @@ transforms:
    - taskgraph.transforms.task:transforms
 
 job-defaults:
     treeherder:
         kind: build
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         max-run-time: 36000
         docker-image: {in-tree: desktop-build}
 
 jobs:
     linux64-shell-haz/debug:
         description: "JS Shell Hazard Analysis Linux"
         index:
             product: firefox
--- a/taskcluster/ci/source-test/doc.yml
+++ b/taskcluster/ci/source-test/doc.yml
@@ -2,17 +2,16 @@ sphinx:
     description: Generate the Sphinx documentation
     platform: lint/opt
     treeherder:
         symbol: tc(Doc)
         kind: test
         tier: 1
     worker-type: aws-provisioner-v1/gecko-t-linux-xlarge
     worker:
-        implementation: docker-worker
         docker-image: {in-tree: "lint"}
         max-run-time: 1800
         artifacts:
             - type: file
               name: public/docs.tar.gz
               path: /home/worker/checkouts/gecko/docs.tar.gz
     run:
         using: run-task
--- a/taskcluster/ci/source-test/mozlint.yml
+++ b/taskcluster/ci/source-test/mozlint.yml
@@ -2,17 +2,16 @@ mozlint-eslint:
     description: JS lint check
     platform: lint/opt
     treeherder:
         symbol: ES
         kind: test
         tier: 1
     worker-type: aws-provisioner-v1/gecko-t-linux-xlarge
     worker:
-        implementation: docker-worker
         docker-image: {in-tree: "lint"}
         max-run-time: 1800
     run:
         using: run-task
         command: >
             cd /home/worker/checkouts/gecko/ &&
             /build/tooltool.py fetch -m tools/lint/eslint/manifest.tt &&
             tar xvfz eslint.tar.gz &&
@@ -46,17 +45,16 @@ mozlint-flake8:
     description: flake8 run over the gecko codebase
     platform: lint/opt
     treeherder:
         symbol: f8
         kind: test
         tier: 1
     worker-type: aws-provisioner-v1/gecko-t-linux-xlarge
     worker:
-        implementation: docker-worker
         docker-image: {in-tree: "lint"}
         max-run-time: 1800
     run:
         using: mach
         mach: lint -l flake8 -f treeherder
     run-on-projects:
         - integration
         - release
@@ -71,17 +69,16 @@ wptlint-gecko:
     description: web-platform-tests linter
     platform: lint/opt
     treeherder:
         symbol: W
         kind: test
         tier: 1
     worker-type: aws-provisioner-v1/gecko-t-linux-xlarge
     worker:
-        implementation: docker-worker
         docker-image: {in-tree: "lint"}
         max-run-time: 1800
     run:
         using: mach
         mach: lint -l wpt -l wpt_manifest -f treeherder
     run-on-projects:
         - integration
         - release
--- a/taskcluster/ci/source-test/python-tests.yml
+++ b/taskcluster/ci/source-test/python-tests.yml
@@ -2,17 +2,16 @@ taskgraph-tests:
     description: taskcluster/taskgraph unit tests
     platform: linux64/opt
     treeherder:
         symbol: py(tg)
         kind: test
         tier: 2
     worker-type: aws-provisioner-v1/gecko-t-linux-xlarge
     worker:
-        implementation: docker-worker
         docker-image: {in-tree: "lint"}
         max-run-time: 1800
     run:
         using: mach
         mach: taskgraph python-tests
     run-on-projects:
         - integration
         - release
@@ -30,17 +29,16 @@ marionette-harness:
         kind: test
         tier: 2
     worker-type:
         by-platform:
             linux64.*: aws-provisioner-v1/gecko-t-linux-xlarge
     worker:
         by-platform:
             linux64.*:
-                implementation: docker-worker
                 docker-image: {in-tree: "lint"}
                 max-run-time: 3600
     run:
         using: mach
         mach: python-test --subsuite marionette-harness
     run-on-projects:
         - integration
         - release
@@ -61,17 +59,16 @@ mozbase:
         kind: test
         tier: 2
     worker-type:
         by-platform:
             linux64.*: aws-provisioner-v1/gecko-t-linux-xlarge
     worker:
         by-platform:
             linux64.*:
-                implementation: docker-worker
                 docker-image: {in-tree: "lint"}
                 max-run-time: 3600
     run:
         using: mach
         mach: python-test --subsuite mozbase
     run-on-projects:
         - integration
         - release
@@ -85,17 +82,16 @@ mozharness:
     description: mozharness integration tests
     platform: lint/opt
     treeherder:
         symbol: py(MH)
         kind: test
         tier: 2
     worker-type: aws-provisioner-v1/gecko-t-linux-xlarge
     worker:
-        implementation: docker-worker
         docker-image: {in-tree: "lint"}
         max-run-time: 1800
     run:
         using: run-task
         cache-dotcache: true
         command: >
             cd /home/worker/checkouts/gecko/testing/mozharness &&
             /usr/local/bin/tox -e py27-hg4.1
@@ -114,17 +110,16 @@ mozlint:
         kind: test
         tier: 2
     worker-type:
         by-platform:
             linux64.*: aws-provisioner-v1/gecko-t-linux-xlarge
     worker:
         by-platform:
             linux64.*:
-                implementation: docker-worker
                 docker-image: {in-tree: "lint"}
                 max-run-time: 3600
     run:
         using: mach
         mach: python-test --subsuite mozlint
     run-on-projects:
         - integration
         - release
--- a/taskcluster/ci/source-test/webidl.yml
+++ b/taskcluster/ci/source-test/webidl.yml
@@ -2,17 +2,16 @@ webidl-test:
     description: WebIDL parser tests
     platform: lint/opt
     treeherder:
         symbol: Wp
         kind: test
         tier: 1
     worker-type: aws-provisioner-v1/gecko-t-linux-xlarge
     worker:
-        implementation: docker-worker
         docker-image: {in-tree: "lint"}
         max-run-time: 1800
     run:
         using: mach
         mach: webidl-parser-test --verbose
     run-on-projects:
         - integration
         - release
--- a/taskcluster/ci/spidermonkey/kind.yml
+++ b/taskcluster/ci/spidermonkey/kind.yml
@@ -13,17 +13,16 @@ job-defaults:
     treeherder:
         platform: linux64/opt
         kind: build
         tier: 1
     index:
         product: firefox
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         max-run-time: 36000
         docker-image: {in-tree: desktop-build}
     run:
         using: spidermonkey
     when:
         files-changed:
             # any when.files-changed specified below in a job will be
             # appended to this list
--- a/taskcluster/ci/static-analysis/kind.yml
+++ b/taskcluster/ci/static-analysis/kind.yml
@@ -21,17 +21,16 @@ jobs:
     macosx64-st-an/debug:
         description: "MacOS X x64 Debug Cross-compile Static Analysis"
         index:
             job-name: macosx64-st-an-debug
         treeherder:
             platform: osx-10-7/debug
         worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
         worker:
-            implementation: docker-worker
             docker-image: {in-tree: desktop-build}
             max-run-time: 36000
         run:
             using: mozharness
             actions: [build generate-build-stats update]
             config:
                 - builds/releng_base_mac_64_cross_builds.py
                 - balrog/production.py
@@ -44,17 +43,16 @@ jobs:
     macosx64-st-an/opt:
         description: "MacOS X x64 Opt Cross-compile Static Analysis"
         index:
             job-name: macosx64-st-an-opt
         treeherder:
             platform: osx-10-7/opt
         worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
         worker:
-            implementation: docker-worker
             docker-image: {in-tree: desktop-build}
             max-run-time: 36000
         run:
             using: mozharness
             actions: [build generate-build-stats update]
             config:
                 - builds/releng_base_mac_64_cross_builds.py
                 - balrog/production.py
@@ -67,17 +65,16 @@ jobs:
     linux64-st-an/debug:
         description: "Linux64 Debug Static Analysis"
         index:
             job-name: linux64-st-an-debug
         treeherder:
             platform: linux64/debug
         worker-type: aws-provisioner-v1/gecko-{level}-b-linux
         worker:
-            implementation: docker-worker
             docker-image: {in-tree: desktop-build}
             max-run-time: 36000
         run:
             using: mozharness
             actions: [build generate-build-stats]
             config:
                 - builds/releng_sub_linux_configs/64_stat_and_debug.py
                 - balrog/production.py
@@ -88,17 +85,16 @@ jobs:
     linux64-st-an/opt:
         description: "Linux64 Opt Static Analysis"
         index:
             job-name: linux64-st-an-opt
         treeherder:
             platform: linux64/opt
         worker-type: aws-provisioner-v1/gecko-{level}-b-linux
         worker:
-            implementation: docker-worker
             docker-image: {in-tree: desktop-build}
             max-run-time: 36000
         run:
             using: mozharness
             actions: [build generate-build-stats]
             config:
                 - builds/releng_sub_linux_configs/64_stat_and_opt.py
                 - balrog/production.py
@@ -112,17 +108,16 @@ jobs:
             product: firefox
             job-name: win32-st-an-debug
         treeherder:
             platform: windows2012-32/debug
             symbol: tc(S)
             tier: 1
         worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
         worker:
-            implementation: generic-worker
             max-run-time: 7200
         run:
             using: mozharness
             script: mozharness/scripts/fx_desktop_build.py
             config:
                 - builds/taskcluster_firefox_win32_clang_debug.py
 
     win32-st-an/opt:
@@ -131,17 +126,16 @@ jobs:
             product: firefox
             job-name: win32-st-an-opt
         treeherder:
             platform: windows2012-32/opt
             symbol: tc(S)
             tier: 1
         worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
         worker:
-            implementation: generic-worker
             max-run-time: 7200
         run:
             using: mozharness
             script: mozharness/scripts/fx_desktop_build.py
             config:
                 - builds/taskcluster_firefox_win32_clang.py
 
     win64-st-an/debug:
@@ -150,17 +144,16 @@ jobs:
             product: firefox
             job-name: win64-st-an-debug
         treeherder:
             platform: windows2012-64/debug
             symbol: tc(S)
             tier: 1
         worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
         worker:
-            implementation: generic-worker
             max-run-time: 7200
         run:
             using: mozharness
             script: mozharness/scripts/fx_desktop_build.py
             config:
                 - builds/taskcluster_firefox_win64_clang_debug.py
 
     win64-st-an/opt:
@@ -169,15 +162,14 @@ jobs:
             product: firefox
             job-name: win64-st-an-opt
         treeherder:
             platform: windows2012-64/opt
             symbol: tc(S)
             tier: 1
         worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
         worker:
-            implementation: generic-worker
             max-run-time: 7200
         run:
             using: mozharness
             script: mozharness/scripts/fx_desktop_build.py
             config:
                 - builds/taskcluster_firefox_win64_clang.py
--- a/taskcluster/ci/toolchain/linux.yml
+++ b/taskcluster/ci/toolchain/linux.yml
@@ -6,17 +6,16 @@ linux64-clang:
     description: "Clang toolchain build"
     treeherder:
         kind: build
         platform: toolchains/opt
         symbol: TL(clang)
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         docker-image: {in-tree: desktop-build}
         max-run-time: 36000
     run:
         using: toolchain-script
         script: build-clang-linux.sh
         tooltool-downloads: public
         resources:
             - 'build/build-clang/**'
@@ -29,17 +28,16 @@ linux64-clang-tidy:
         job-name: linux64-clang-tidy
     treeherder:
         kind: build
         platform: toolchains/opt
         symbol: TL(clang-tidy)
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         docker-image: {in-tree: desktop-build}
         max-run-time: 36000
     run:
         using: toolchain-script
         script: build-clang-tidy-linux.sh
         tooltool-downloads: public
         resources:
             - 'build/clang-plugin/**'
@@ -50,17 +48,16 @@ linux64-gcc:
     description: "GCC toolchain build"
     treeherder:
         kind: build
         platform: toolchains/opt
         symbol: TL(gcc)
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         docker-image: {in-tree: desktop-build}
         max-run-time: 36000
     run:
         using: toolchain-script
         script: build-gcc-linux.sh
         resources:
             - 'build/unix/build-gcc/**'
 
@@ -68,17 +65,16 @@ linux64-binutils:
     description: "Binutils toolchain build"
     treeherder:
         kind: build
         platform: toolchains/opt
         symbol: TL(binutil)
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         docker-image: {in-tree: desktop-build}
         max-run-time: 36000
     run:
         using: toolchain-script
         script: build-binutils-linux.sh
         resources:
             - 'build/unix/build-binutils/**'
 
@@ -86,17 +82,16 @@ linux64-cctools-port:
     description: "cctools-port toolchain build"
     treeherder:
         kind: build
         platform: toolchains/opt
         symbol: TL(cctools)
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         docker-image: {in-tree: desktop-build}
         max-run-time: 36000
     run:
         using: toolchain-script
         script: build-cctools-port.sh
         tooltool-downloads: public
         resources:
             - 'taskcluster/scripts/misc/tooltool-download.sh'
@@ -105,17 +100,16 @@ linux64-hfsplus:
     description: "hfsplus toolchain build"
     treeherder:
         kind: build
         platform: toolchains/opt
         symbol: TL(hfs+)
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         docker-image: {in-tree: desktop-build}
         max-run-time: 36000
     run:
         using: toolchain-script
         script: build-hfsplus-linux.sh
         tooltool-downloads: public
         resources:
             - 'build/unix/build-hfsplus/**'
@@ -125,14 +119,13 @@ linux64-libdmg:
     description: "libdmg-hfsplus toolchain build"
     treeherder:
         kind: build
         platform: toolchains/opt
         symbol: TL(libdmg-hfs+)
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-linux
     worker:
-        implementation: docker-worker
         docker-image: {in-tree: desktop-build}
         max-run-time: 36000
     run:
         using: toolchain-script
         script: build-libdmg-hfsplus.sh
--- a/taskcluster/ci/toolchain/macosx.yml
+++ b/taskcluster/ci/toolchain/macosx.yml
@@ -6,17 +6,16 @@ macosx64-clang:
     description: "Clang toolchain build"
     treeherder:
         kind: build
         platform: toolchains/opt
         symbol: TM(clang)
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
     worker:
-        implementation: docker-worker
         docker-image: {in-tree: desktop-build}
         max-run-time: 36000
     run:
         using: toolchain-script
         script: build-clang-macosx.sh
         tooltool-downloads: internal
         resources:
             - 'build/build-clang/**'
@@ -29,17 +28,16 @@ macosx64-clang-tidy:
         job-name: macosx64-clang-tidy
     treeherder:
         kind: build
         platform: toolchains/opt
         symbol: TM(clang-tidy)
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
     worker:
-        implementation: docker-worker
         docker-image: {in-tree: desktop-build}
         max-run-time: 36000
     run:
         using: toolchain-script
         script: build-clang-tidy-macosx.sh
         tooltool-downloads: internal
         resources:
             - 'build/clang-plugin/**'
@@ -50,17 +48,16 @@ macosx64-cctools-port:
     description: "cctools-port toolchain build"
     treeherder:
         kind: build
         platform: toolchains/opt
         symbol: TM(cctools)
         tier: 1
     worker-type: aws-provisioner-v1/gecko-{level}-b-macosx64
     worker:
-        implementation: docker-worker
         docker-image: {in-tree: desktop-build}
         max-run-time: 36000
     run:
         using: toolchain-script
         script: build-cctools-port-macosx.sh
         tooltool-downloads: internal
         resources:
             - 'taskcluster/scripts/misc/tooltool-download.sh'
--- a/taskcluster/ci/toolchain/windows.yml
+++ b/taskcluster/ci/toolchain/windows.yml
@@ -6,17 +6,16 @@ win32-clang-cl:
     description: "Clang-cl toolchain build"
     treeherder:
         kind: build
         platform: toolchains/opt
         symbol: TW32(clang-cl)
         tier: 2
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
-        implementation: generic-worker
         max-run-time: 36000
     run:
         using: toolchain-script
         script: build-clang32-windows.sh
         resources:
             - 'build/build-clang/**'
             - 'taskcluster/scripts/misc/build-clang-windows-helper32.sh'
 
@@ -24,17 +23,16 @@ win64-clang-cl:
     description: "Clang-cl toolchain build"
     treeherder:
         kind: build
         platform: toolchains/opt
         symbol: TW64(clang-cl)
         tier: 2
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
-        implementation: generic-worker
         max-run-time: 36000
     run:
         using: toolchain-script
         script: build-clang64-windows.sh
         resources:
             - 'build/build-clang/**'
             - 'taskcluster/scripts/misc/build-clang-windows-helper64.sh'
 
@@ -45,17 +43,16 @@ win32-clang-tidy:
         job-name: win32-clang-tidy
     treeherder:
         kind: build
         platform: toolchains/opt
         symbol: TW32(clang-tidy)
         tier: 2
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
-        implementation: generic-worker
         max-run-time: 36000
     run:
         using: toolchain-script
         script: build-clang-tidy32-windows.sh
         resources:
             - 'build/build-clang/**'
             - 'taskcluster/scripts/misc/build-clang-windows-helper32.sh'
 
@@ -66,16 +63,15 @@ win64-clang-tidy:
         job-name: win64-clang-tidy
     treeherder:
         kind: build
         platform: toolchains/opt
         symbol: TW64(clang-tidy)
         tier: 2
     worker-type: aws-provisioner-v1/gecko-{level}-b-win2012
     worker:
-        implementation: generic-worker
         max-run-time: 36000
     run:
         using: toolchain-script
         script: build-clang-tidy64-windows.sh
         resources:
             - 'build/build-clang/**'
             - 'taskcluster/scripts/misc/build-clang-windows-helper64.sh'
--- a/taskcluster/ci/upload-symbols/kind.yml
+++ b/taskcluster/ci/upload-symbols/kind.yml
@@ -32,16 +32,17 @@ job-template:
    expires-after: 7 days
    deadline-after: 24 hours
    run-on-projects:
        - try
        - release
    worker-type: aws-provisioner-v1/gecko-symbol-upload
    worker:
        implementation: docker-worker
+       os: linux
        max-run-time: 600
        command: ["/bin/bash", "bin/upload.sh"]
        docker-image: taskclusterprivate/upload_symbols:0.0.4
        env:
            GECKO_HEAD_REPOSITORY: # see transforms
            GECKO_HEAD_REV: # see transforms
            ARTIFACT_TASKID: {"task-reference": "<build>"}
    scopes:
--- a/taskcluster/ci/valgrind/kind.yml
+++ b/taskcluster/ci/valgrind/kind.yml
@@ -17,17 +17,16 @@ jobs:
             job-name: linux64-valgrind-opt
         treeherder:
             platform: linux64/opt
             symbol: tc(V)
             kind: build
             tier: 1
         worker-type: aws-provisioner-v1/gecko-{level}-b-linux
         worker:
-            implementation: docker-worker
             docker-image: {in-tree: desktop-build}
             max-run-time: 72000
         run:
             using: mozharness
             actions: [get-secrets build generate-build-stats valgrind-test]
             custom-build-variant-cfg: valgrind
             config:
                 - builds/releng_base_linux_64_builds.py
--- a/taskcluster/taskgraph/transforms/build.py
+++ b/taskcluster/taskgraph/transforms/build.py
@@ -4,37 +4,41 @@
 """
 Apply some defaults and minor modifications to the jobs defined in the build
 kind.
 """
 
 from __future__ import absolute_import, print_function, unicode_literals
 
 from taskgraph.transforms.base import TransformSequence
+from taskgraph.util.workertypes import worker_type_implementation
 
 transforms = TransformSequence()
 
 
 @transforms.add
 def set_defaults(config, jobs):
     """Set defaults, including those that differ per worker implementation"""
     for job in jobs:
         job['treeherder'].setdefault('kind', 'build')
         job['treeherder'].setdefault('tier', 1)
         job.setdefault('needs-sccache', True)
-        if job['worker']['implementation'] in ('docker-worker', 'docker-engine'):
-            job['worker'].setdefault('docker-image', {'in-tree': 'desktop-build'})
-            job['worker']['chain-of-trust'] = True
-            job.setdefault('extra', {})
-            job['extra'].setdefault('chainOfTrust', {})
-            job['extra']['chainOfTrust'].setdefault('inputs', {})
-            job['extra']['chainOfTrust']['inputs']['docker-image'] = {
+        _, worker_os = worker_type_implementation(job['worker-type'])
+        if worker_os == "linux":
+            worker = job.setdefault('worker')
+            worker.setdefault('docker-image', {'in-tree': 'desktop-build'})
+            worker['chain-of-trust'] = True
+            extra = job.setdefault('extra', {})
+            extra.setdefault('chainOfTrust', {})
+            extra['chainOfTrust'].setdefault('inputs', {})
+            extra['chainOfTrust']['inputs']['docker-image'] = {
                 "task-reference": "<docker-image>"
             }
-        job['worker'].setdefault('env', {})
+        elif worker_os in set(["macosx", "windows"]):
+            job['worker'].setdefault('env', {})
         yield job
 
 
 @transforms.add
 def set_env(config, jobs):
     """Set extra environment variables from try command line."""
     for job in jobs:
         env = config.config['args'].env
--- a/taskcluster/taskgraph/transforms/docker_image.py
+++ b/taskcluster/taskgraph/transforms/docker_image.py
@@ -89,16 +89,17 @@ def fill_template(config, tasks):
                 'tier': 1,
             },
             'run-on-projects': [],
             'worker-type': 'aws-provisioner-v1/gecko-images',
             # can't use {in-tree: ..} here, otherwise we might try to build
             # this image..
             'worker': {
                 'implementation': 'docker-worker',
+                'os': 'linux',
                 'docker-image': docker_image('image_builder'),
                 'caches': [{
                     'type': 'persistent',
                     'name': 'level-{}-imagebuilder-v1'.format(config.params['level']),
                     'mount-point': '/home/worker/checkouts',
                 }],
                 'artifacts': [{
                     'type': 'file',
--- a/taskcluster/taskgraph/transforms/job/__init__.py
+++ b/taskcluster/taskgraph/transforms/job/__init__.py
@@ -14,18 +14,18 @@ from __future__ import absolute_import, 
 import copy
 import logging
 import os
 
 from taskgraph.transforms.base import TransformSequence
 from taskgraph.util.schema import (
     validate_schema,
     Schema,
-    optionally_keyed_by,
 )
+from taskgraph.util.workertypes import worker_type_implementation
 from taskgraph.transforms.task import task_description_schema
 from voluptuous import (
     Any,
     Extra,
     Optional,
     Required,
 )
 
@@ -76,27 +76,21 @@ job_description_schema = Schema({
         # The key to a job implementation in a peer module to this one
         'using': basestring,
 
         # Any remaining content is verified against that job implementation's
         # own schema.
         Extra: object,
     },
 
-    Required('worker-type'): optionally_keyed_by(
-        'platform',
-        task_description_schema['worker-type']),
+    Required('worker-type'): task_description_schema['worker-type'],
 
-    # for `worker`, all we need is the implementation; the rest will be verified
-    # by the task description schema
-    Required('worker'): optionally_keyed_by(
-        'platform', {
-            Required('implementation'): basestring,
-            Extra: object,
-        }),
+    # This object will be passed through to the task description, with additions
+    # provided by the job's run-using function
+    Optional('worker'): dict,
 })
 
 transforms = TransformSequence()
 
 
 @transforms.add
 def validate(config, jobs):
     for job in jobs:
@@ -113,17 +107,17 @@ def rewrite_when_to_optimization(config,
             yield job
             continue
 
         # add some common files
         files_changed.extend([
             '{}/**'.format(config.path),
             'taskcluster/taskgraph/**',
         ])
-        if 'in-tree' in job['worker'].get('docker-image', {}):
+        if 'in-tree' in job.get('worker', {}).get('docker-image', {}):
             files_changed.append('taskcluster/docker/{}/**'.format(
                 job['worker']['docker-image']['in-tree']))
 
         # "only when files changed" implies "skip if files have not changed"
         job.setdefault('optimizations', []).append(['skip-unless-changed', files_changed])
 
         assert 'when' not in job
         yield job
@@ -136,28 +130,36 @@ def make_task_description(config, jobs):
     import_all()
     for job in jobs:
         if 'label' not in job:
             if 'name' not in job:
                 raise Exception("job has neither a name nor a label")
             job['label'] = '{}-{}'.format(config.kind, job['name'])
         if job['name']:
             del job['name']
+
+        impl, os = worker_type_implementation(job['worker-type'])
+        worker = job.setdefault('worker', {})
+        assert 'implementation' not in worker
+        worker['implementation'] = impl
+        if os:
+            worker['os'] = os
+
         taskdesc = copy.deepcopy(job)
 
         # fill in some empty defaults to make run implementations easier
         taskdesc.setdefault('attributes', {})
         taskdesc.setdefault('dependencies', {})
         taskdesc.setdefault('routes', [])
         taskdesc.setdefault('scopes', [])
         taskdesc.setdefault('extra', {})
 
         # give the function for job.run.using on this worker implementation a
         # chance to set up the task description.
-        configure_taskdesc_for_run(config, job, taskdesc)
+        configure_taskdesc_for_run(config, job, taskdesc, impl)
         del taskdesc['run']
 
         # yield only the task description, discarding the job description
         yield taskdesc
 
 # A registry of all functions decorated with run_job_using
 registry = {}
 
@@ -175,29 +177,28 @@ def run_job_using(worker_implementation,
         if worker_implementation in for_run_using:
             raise Exception("run_job_using({!r}, {!r}) already exists: {!r}".format(
                 run_using, worker_implementation, for_run_using[run_using]))
         for_run_using[worker_implementation] = (func, schema)
         return func
     return wrap
 
 
-def configure_taskdesc_for_run(config, job, taskdesc):
+def configure_taskdesc_for_run(config, job, taskdesc, worker_implementation):
     """
     Run the appropriate function for this job against the given task
     description.
 
     This will raise an appropriate error if no function exists, or if the job's
     run is not valid according to the schema.
     """
     run_using = job['run']['using']
     if run_using not in registry:
         raise Exception("no functions for run.using {!r}".format(run_using))
 
-    worker_implementation = job['worker']['implementation']
     if worker_implementation not in registry[run_using]:
         raise Exception("no functions for run.using {!r} on {!r}".format(
             run_using, worker_implementation))
 
     func, schema = registry[run_using][worker_implementation]
     if schema:
         job['run'] = validate_schema(
                 schema, job['run'],
--- a/taskcluster/taskgraph/transforms/job/mozharness.py
+++ b/taskcluster/taskgraph/transforms/job/mozharness.py
@@ -100,16 +100,17 @@ def mozharness_on_docker_worker_setup(co
     env = worker.setdefault('env', {})
     env.update({
         'MOZHARNESS_CONFIG': ' '.join(run['config']),
         'MOZHARNESS_SCRIPT': run['script'],
         'MH_BRANCH': config.params['project'],
         'MH_BUILD_POOL': 'taskcluster',
         'MOZ_BUILD_DATE': config.params['moz_build_date'],
         'MOZ_SCM_LEVEL': config.params['level'],
+        'MOZ_AUTOMATION': '1',
     })
 
     if 'actions' in run:
         env['MOZHARNESS_ACTIONS'] = ' '.join(run['actions'])
 
     if 'options' in run:
         env['MOZHARNESS_OPTIONS'] = ' '.join(run['options'])
 
@@ -164,19 +165,20 @@ def mozharness_on_docker_worker_setup(co
     command.append("/home/worker/workspace/build/src/{}".format(
         run.get('job-script',
                 "taskcluster/scripts/builder/build-linux.sh"
                 )))
 
     worker['command'] = command
 
 
-# We use the generic worker to run tasks on Windows
 @run_job_using("generic-worker", "mozharness", schema=mozharness_run_schema)
 def mozharness_on_generic_worker(config, job, taskdesc):
+    assert job['worker']['os'] == 'windows', 'only supports windows right now'
+
     run = job['run']
 
     # fail if invalid run options are included
     invalid = []
     for prop in ['actions', 'custom-build-variant-cfg',
                  'tooltool-downloads', 'secrets', 'taskcluster-proxy',
                  'need-xvfb']:
         if prop in run and run[prop]:
@@ -196,16 +198,17 @@ def mozharness_on_generic_worker(config,
 
     docker_worker_add_gecko_vcs_env_vars(config, job, taskdesc)
 
     env = worker['env']
     env.update({
         'MOZ_BUILD_DATE': config.params['moz_build_date'],
         'MOZ_SCM_LEVEL': config.params['level'],
         'MOZ_SIMPLE_PACKAGE_NAME': 'target',
+        'MOZ_AUTOMATION': '1',
     })
 
     if not job['attributes']['build_platform'].startswith('win'):
         raise Exception(
             "Task generation for mozharness build jobs currently only supported on Windows"
         )
 
     mh_command = [r'c:\mozilla-build\python\python.exe']
--- a/taskcluster/taskgraph/transforms/job/mozharness_test.py
+++ b/taskcluster/taskgraph/transforms/job/mozharness_test.py
@@ -36,33 +36,53 @@ BUILDER_NAME_PREFIX = {
 test_description_schema = {str(k): v for k, v in test_description_schema.schema.iteritems()}
 
 mozharness_test_run_schema = Schema({
     Required('using'): 'mozharness-test',
     Required('test'): test_description_schema,
 })
 
 
+def test_packages_url(taskdesc):
+    """Account for different platforms that name their test packages differently"""
+    build_platform = taskdesc['attributes']['build_platform']
+    build_type = taskdesc['attributes']['build_type']
+
+    if build_platform == 'macosx64' and build_type == 'opt':
+        target = 'firefox-{}.en-US.{}'.format(get_firefox_version(), 'mac')
+    else:
+        target = 'target'
+
+    return get_artifact_url(
+        '<build>', 'public/build/{}.test_packages.json'.format(target))
+
+
 @run_job_using('docker-engine', 'mozharness-test', schema=mozharness_test_run_schema)
 @run_job_using('docker-worker', 'mozharness-test', schema=mozharness_test_run_schema)
 def mozharness_test_on_docker(config, job, taskdesc):
     test = taskdesc['run']['test']
     mozharness = test['mozharness']
     worker = taskdesc['worker']
 
+    # apply some defaults
+    worker['docker-image'] = test['docker-image']
+    worker['allow-ptrace'] = True  # required for all tests, for crashreporter
+    worker['loopback-video'] = test['loopback-video']
+    worker['loopback-audio'] = test['loopback-audio']
+    worker['max-run-time'] = test['max-run-time']
+    worker['retry-exit-status'] = test['retry-exit-status']
+
     artifacts = [
         # (artifact name prefix, in-image path)
         ("public/logs/", "/home/worker/workspace/build/upload/logs/"),
         ("public/test", "/home/worker/artifacts/"),
         ("public/test_info/", "/home/worker/workspace/build/blobber_upload_dir/"),
     ]
 
     installer_url = get_artifact_url('<build>', mozharness['build-artifact-name'])
-    test_packages_url = get_artifact_url('<build>',
-                                         'public/build/target.test_packages.json')
     mozharness_url = get_artifact_url('<build>',
                                       'public/build/mozharness.zip')
 
     worker['artifacts'] = [{
         'name': prefix,
         'path': os.path.join('/home/worker/workspace', path),
         'type': 'directory',
     } for (prefix, path) in artifacts]
@@ -76,16 +96,17 @@ def mozharness_test_on_docker(config, jo
 
     env = worker['env'] = {
         'MOZHARNESS_CONFIG': ' '.join(mozharness['config']),
         'MOZHARNESS_SCRIPT': mozharness['script'],
         'MOZILLA_BUILD_URL': {'task-reference': installer_url},
         'NEED_PULSEAUDIO': 'true',
         'NEED_WINDOW_MANAGER': 'true',
         'ENABLE_E10S': str(bool(test.get('e10s'))).lower(),
+        'MOZ_AUTOMATION': '1',
     }
 
     if mozharness.get('mochitest-flavor'):
         env['MOCHITEST_FLAVOR'] = mozharness['mochitest-flavor']
 
     if mozharness['set-moz-node-path']:
         env['MOZ_NODE_PATH'] = '/usr/local/bin/node'
 
@@ -135,17 +156,17 @@ def mozharness_test_on_docker(config, jo
         '--',
         '/home/worker/bin/test-linux.sh',
     ])
 
     if mozharness.get('no-read-buildbot-config'):
         command.append("--no-read-buildbot-config")
     command.extend([
         {"task-reference": "--installer-url=" + installer_url},
-        {"task-reference": "--test-packages-url=" + test_packages_url},
+        {"task-reference": "--test-packages-url=" + test_packages_url(taskdesc)},
     ])
     command.extend(mozharness.get('extra-options', []))
 
     # TODO: remove the need for run['chunked']
     if mozharness.get('chunked') or test['chunks'] > 1:
         # Implement mozharness['chunking-args'], modifying command in place
         if mozharness['chunking-args'] == 'this-chunk':
             command.append('--total-chunk={}'.format(test['chunks']))
@@ -165,103 +186,93 @@ def mozharness_test_on_docker(config, jo
 
 
 @run_job_using('generic-worker', 'mozharness-test', schema=mozharness_test_run_schema)
 def mozharness_test_on_generic_worker(config, job, taskdesc):
     test = taskdesc['run']['test']
     mozharness = test['mozharness']
     worker = taskdesc['worker']
 
+    is_macosx = worker['os'] == 'macosx'
+    is_windows = worker['os'] == 'windows'
+    assert is_macosx or is_windows
+
     artifacts = [
         {
             'name': 'public/logs',
             'path': 'logs',
             'type': 'directory'
         },
     ]
 
     # jittest doesn't have blob_upload_dir
     if test['test-name'] != 'jittest':
         artifacts.append({
             'name': 'public/test_info',
             'path': 'build/blobber_upload_dir',
             'type': 'directory'
         })
 
-    build_platform = taskdesc['attributes']['build_platform']
-    build_type = taskdesc['attributes']['build_type']
-
-    if build_platform == 'macosx64' and build_type == 'opt':
-        target = 'firefox-{}.en-US.{}'.format(get_firefox_version(), 'mac')
-    else:
-        target = 'target'
-
     installer_url = get_artifact_url('<build>', mozharness['build-artifact-name'])
 
-    test_packages_url = get_artifact_url(
-        '<build>', 'public/build/{}.test_packages.json'.format(target))
-
     taskdesc['scopes'].extend(
         ['generic-worker:os-group:{}'.format(group) for group in test['os-groups']])
 
     worker['os-groups'] = test['os-groups']
 
     if test['reboot']:
         raise Exception('reboot: {} not supported on generic-worker'.format(test['reboot']))
 
     worker['max-run-time'] = test['max-run-time']
     worker['artifacts'] = artifacts
 
+    env = worker.setdefault('env', {})
+    env['MOZ_AUTOMATION'] = '1'
+
     # this list will get cleaned up / reduced / removed in bug 1354088
-    if build_platform.startswith('macosx'):
-        worker['env'] = {
+    if is_macosx:
+        env.update({
             'IDLEIZER_DISABLE_SHUTDOWN': 'true',
             'LANG': 'en_US.UTF-8',
             'LC_ALL': 'en_US.UTF-8',
             'MOZ_HIDE_RESULTS_TABLE': '1',
             'MOZ_NODE_PATH': '/usr/local/bin/node',
             'MOZ_NO_REMOTE': '1',
             'NO_EM_RESTART': '1',
             'NO_FAIL_ON_TEST_ERRORS': '1',
             'PATH': '/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin',
             'SHELL': '/bin/bash',
             'XPCOM_DEBUG_BREAK': 'warn',
             'XPC_FLAGS': '0x0',
-            'XPC_SERVICE_NAME': '0'
-        }
+            'XPC_SERVICE_NAME': '0',
+        })
 
-    if build_platform.startswith('macosx'):
+    if is_macosx:
         mh_command = [
             'python2.7',
             '-u',
             'mozharness/scripts/' + mozharness['script']
         ]
-    elif build_platform.startswith('win'):
+    elif is_windows:
         mh_command = [
             'c:\\mozilla-build\\python\\python.exe',
             '-u',
             'mozharness\\scripts\\' + normpath(mozharness['script'])
         ]
-    else:
-        mh_command = [
-            'python',
-            '-u',
-            'mozharness/scripts/' + mozharness['script']
-        ]
 
     for mh_config in mozharness['config']:
         cfg_path = 'mozharness/configs/' + mh_config
-        if build_platform.startswith('win'):
+        if is_windows:
             cfg_path = normpath(cfg_path)
         mh_command.extend(['--cfg', cfg_path])
     mh_command.extend(mozharness.get('extra-options', []))
     if mozharness.get('no-read-buildbot-config'):
         mh_command.append('--no-read-buildbot-config')
     mh_command.extend(['--installer-url', installer_url])
-    mh_command.extend(['--test-packages-url', test_packages_url])
+    mh_command.extend(['--test-packages-url', test_packages_url(taskdesc)])
     if mozharness.get('download-symbols'):
         if isinstance(mozharness['download-symbols'], basestring):
             mh_command.extend(['--download-symbols', mozharness['download-symbols']])
         else:
             mh_command.extend(['--download-symbols', 'true'])
     if mozharness.get('include-blob-upload-branch'):
         mh_command.append('--blob-upload-branch=' + config.params['project'])
     mh_command.extend(mozharness.get('extra-options', []))
@@ -284,44 +295,39 @@ def mozharness_test_on_generic_worker(co
             'artifact': 'public/build/mozharness.zip',
             'task-id': {
                 'task-reference': '<build>'
             }
         },
         'format': 'zip'
     }]
 
-    if build_platform.startswith('win'):
+    if is_windows:
         worker['command'] = [
             {'task-reference': ' '.join(mh_command)}
         ]
-    else:
+    else:  # is_macosx
         mh_command_task_ref = []
         for token in mh_command:
             mh_command_task_ref.append({'task-reference': token})
         worker['command'] = [
             mh_command_task_ref
         ]
 
 
 @run_job_using('native-engine', 'mozharness-test', schema=mozharness_test_run_schema)
 def mozharness_test_on_native_engine(config, job, taskdesc):
     test = taskdesc['run']['test']
     mozharness = test['mozharness']
     worker = taskdesc['worker']
     is_talos = test['suite'] == 'talos'
 
-    build_platform = taskdesc['attributes']['build_platform']
-    build_type = taskdesc['attributes']['build_type']
-    target = 'firefox-{}.en-US.{}'.format(get_firefox_version(), 'mac') \
-        if build_platform == 'macosx64' and build_type == 'opt' else 'target'
+    assert worker['os'] == 'macosx'
 
     installer_url = get_artifact_url('<build>', mozharness['build-artifact-name'])
-    test_packages_url = get_artifact_url('<build>',
-                                         'public/build/{}.test_packages.json'.format(target))
     mozharness_url = get_artifact_url('<build>',
                                       'public/build/mozharness.zip')
 
     worker['artifacts'] = [{
         'name': prefix.rstrip('/'),
         'path': path.rstrip('/'),
         'type': 'directory',
     } for (prefix, path) in [
@@ -342,32 +348,33 @@ def mozharness_test_on_native_engine(con
         'MOZHARNESS_URL': {'task-reference': mozharness_url},
         'MOZILLA_BUILD_URL': {'task-reference': installer_url},
         "MOZ_NO_REMOTE": '1',
         "NO_EM_RESTART": '1',
         "XPCOM_DEBUG_BREAK": 'warn',
         "NO_FAIL_ON_TEST_ERRORS": '1',
         "MOZ_HIDE_RESULTS_TABLE": '1',
         "MOZ_NODE_PATH": "/usr/local/bin/node",
+        'MOZ_AUTOMATION': '1',
     }
     # talos tests don't need Xvfb
     if is_talos:
         env['NEED_XVFB'] = 'false'
 
     script = 'test-macosx.sh' if test['test-platform'].startswith('macosx') else 'test-linux.sh'
     worker['context'] = '{}/raw-file/{}/taskcluster/scripts/tester/{}'.format(
         config.params['head_repository'], config.params['head_rev'], script
     )
 
     command = worker['command'] = ["./{}".format(script)]
     if mozharness.get('no-read-buildbot-config'):
         command.append("--no-read-buildbot-config")
     command.extend([
         {"task-reference": "--installer-url=" + installer_url},
-        {"task-reference": "--test-packages-url=" + test_packages_url},
+        {"task-reference": "--test-packages-url=" + test_packages_url(taskdesc)},
     ])
     if mozharness.get('include-blob-upload-branch'):
         command.append('--blob-upload-branch=' + config.params['project'])
     command.extend(mozharness.get('extra-options', []))
 
     # TODO: remove the need for run['chunked']
     if mozharness.get('chunked') or test['chunks'] > 1:
         # Implement mozharness['chunking-args'], modifying command in place
--- a/taskcluster/taskgraph/transforms/job/toolchain.py
+++ b/taskcluster/taskgraph/transforms/job/toolchain.py
@@ -86,16 +86,17 @@ def docker_worker_toolchain(config, job,
     docker_worker_add_gecko_vcs_env_vars(config, job, taskdesc)
     support_vcs_checkout(config, job, taskdesc)
 
     env = worker['env']
     env.update({
         'MOZ_BUILD_DATE': config.params['moz_build_date'],
         'MOZ_SCM_LEVEL': config.params['level'],
         'TOOLS_DISABLE': 'true',
+        'MOZ_AUTOMATION': '1',
     })
 
     # tooltool downloads.  By default we download using the API endpoint, but
     # the job can optionally request relengapi-proxy (for example when downloading
     # internal tooltool resources.  So we define the tooltool cache unconditionally.
     worker['caches'].append({
         'type': 'persistent',
         'name': 'tooltool-cache',
@@ -153,16 +154,17 @@ def windows_toolchain(config, job, taskd
     taskdesc['scopes'].extend([
         'generic-worker:cache:' + svn_cache,
     ])
 
     env = worker['env']
     env.update({
         'MOZ_BUILD_DATE': config.params['moz_build_date'],
         'MOZ_SCM_LEVEL': config.params['level'],
+        'MOZ_AUTOMATION': '1',
     })
 
     hg = r'c:\Program Files\Mercurial\hg.exe'
     hg_command = ['"{}"'.format(hg)]
     hg_command.append('robustcheckout')
     hg_command.extend(['--sharebase', 'y:\\hg-shared'])
     hg_command.append('--purge')
     hg_command.extend(['--upstream', 'https://hg.mozilla.org/mozilla-unified'])
--- a/taskcluster/taskgraph/transforms/l10n.py
+++ b/taskcluster/taskgraph/transforms/l10n.py
@@ -343,17 +343,16 @@ def validate_again(config, jobs):
 
 
 @transforms.add
 def make_job_description(config, jobs):
     for job in jobs:
         job_description = {
             'name': job['name'],
             'worker': {
-                'implementation': 'docker-worker',
                 'docker-image': {'in-tree': 'desktop-build'},
                 'max-run-time': job['run-time'],
                 'chain-of-trust': True,
             },
             'extra': job['extra'],
             'worker-type': job['worker-type'],
             'description': job['description'],
             'run': {
--- a/taskcluster/taskgraph/transforms/repackage.py
+++ b/taskcluster/taskgraph/transforms/repackage.py
@@ -125,16 +125,17 @@ def make_task_description(config, jobs):
             'routes': job.get('routes', []),
             'extra': job.get('extra', {}),
             'scopes':
             ['docker-worker:relengapi-proxy:tooltool.download.internal',
              'secrets:get:project/taskcluster/gecko/hgfingerprint',
              'docker-worker:relengapi-proxy:tooltool.download.public',
              'project:releng:signing:format:dmg'],
             'worker': {'implementation': 'docker-worker',
+                       'os': 'linux',
                        'docker-image': {"in-tree": "desktop-build"},
                        'caches': [{
                                    'type': 'persistent',
                                    'name': 'tooltool-cache',
                                    'mount-point': '/home/worker/tooltool-cache',
                                  }, {
                                    'type': 'persistent',
                                    'name': 'level-%s-checkouts-v1' % level,
--- a/taskcluster/taskgraph/transforms/task.py
+++ b/taskcluster/taskgraph/transforms/task.py
@@ -149,16 +149,17 @@ task_description_schema = Schema({
     'worker-type': basestring,
 
     # Whether the job should use sccache compiler caching.
     Required('needs-sccache', default=False): bool,
 
     # information specific to the worker implementation that will run this task
     'worker': Any({
         Required('implementation'): Any('docker-worker', 'docker-engine'),
+        Required('os'): 'linux',
 
         # For tasks that will run in docker-worker or docker-engine, this is the
         # name of the docker image or in-tree docker image to run the task in.  If
         # in-tree, then a dependency will be created automatically.  This is
         # generally `desktop-test`, or an image that acts an awful lot like it.
         Required('docker-image'): Any(
             # a raw Docker image path (repo/image:tag)
             basestring,
@@ -210,19 +211,20 @@ task_description_schema = Schema({
 
         # the maximum time to run, in seconds
         Required('max-run-time'): int,
 
         # the exit status code that indicates the task should be retried
         Optional('retry-exit-status'): int,
 
     }, {
+        Required('implementation'): 'generic-worker',
+        Required('os'): Any('windows', 'macosx'),
         # see http://schemas.taskcluster.net/generic-worker/v1/payload.json
         # and https://docs.taskcluster.net/reference/workers/generic-worker/payload
-        Required('implementation'): 'generic-worker',
 
         # command is a list of commands to run, sequentially
         # on Windows, each command is a string, on OS X and Linux, each command is
         # a string array
         Required('command'): Any(
             [taskref_or_string],   # Windows
             [[taskref_or_string]]  # Linux / OS X
         ),
@@ -303,16 +305,17 @@ task_description_schema = Schema({
             Optional('project'): basestring,
         },
         Required('properties'): {
             'product': basestring,
             Extra: basestring,  # additional properties are allowed
         },
     }, {
         Required('implementation'): 'native-engine',
+        Required('os'): Any('macosx', 'linux'),
 
         # A link for an executable to download
         Optional('context'): basestring,
 
         # Tells the worker whether machine should reboot
         # after the task is finished.
         Optional('reboot'):
             Any('always', 'on-exception', 'on-failure'),
@@ -392,16 +395,22 @@ task_description_schema = Schema({
             # Paths to the artifacts to sign
             Required('paths'): [basestring],
         }],
     }, {
         Required('implementation'): 'push-apk-breakpoint',
         Required('payload'): object,
 
     }, {
+        Required('implementation'): 'invalid',
+        # an invalid task is one which should never actually be created; this is used in
+        # release automation on branches where the task just doesn't make sense
+        Extra: object,
+
+    }, {
         Required('implementation'): 'push-apk',
 
         # list of artifact URLs for the artifacts that should be beetmoved
         Required('upstream-artifacts'): [{
             # taskId of the task with the artifact
             Required('taskId'): taskref_or_string,
 
             # type of signing task (for CoT)
@@ -736,16 +745,21 @@ def build_push_apk_payload(config, task,
         task_def['payload']['rollout_percentage'] = worker['rollout-percentage']
 
 
 @payload_builder('push-apk-breakpoint')
 def build_push_apk_breakpoint_payload(config, task, task_def):
     task_def['payload'] = task['worker']['payload']
 
 
+@payload_builder('invalid')
+def build_invalid_payload(config, task, task_def):
+    task_def['payload'] = 'invalid task - should never be created'
+
+
 @payload_builder('native-engine')
 def build_macosx_engine_payload(config, task, task_def):
     worker = task['worker']
     artifacts = map(lambda artifact: {
         'name': artifact['name'],
         'path': artifact['path'],
         'type': artifact['type'],
         'expires': task_def['expires'],
--- a/taskcluster/taskgraph/transforms/tests.py
+++ b/taskcluster/taskgraph/transforms/tests.py
@@ -33,23 +33,26 @@ from voluptuous import (
     Required,
 )
 
 import copy
 import logging
 import requests
 from collections import defaultdict
 
-WORKER_TYPE = {
-    # default worker types keyed by instance-size
+# default worker types keyed by instance-size
+LINUX_WORKER_TYPES = {
     'large': 'aws-provisioner-v1/gecko-t-linux-large',
     'xlarge': 'aws-provisioner-v1/gecko-t-linux-xlarge',
     'legacy': 'aws-provisioner-v1/gecko-t-linux-medium',
     'default': 'aws-provisioner-v1/gecko-t-linux-large',
-    # windows / os x worker types keyed by test-platform
+}
+
+# windows / os x worker types keyed by test-platform
+WINDOWS_WORKER_TYPES = {
     'windows7-32-vm': 'aws-provisioner-v1/gecko-t-win7-32',
     'windows7-32': 'aws-provisioner-v1/gecko-t-win7-32-gpu',
     'windows10-64-vm': 'aws-provisioner-v1/gecko-t-win10-64',
     'windows10-64': 'aws-provisioner-v1/gecko-t-win10-64-gpu',
     'windows10-64-asan': 'aws-provisioner-v1/gecko-t-win10-64-gpu',
     'macosx64': 'scl3-puppet/os-x-10-10-gw'
 }
 
@@ -145,33 +148,22 @@ test_description_schema = Schema({
     Required('loopback-video', default=False): bool,
 
     # Whether the test can run using a software GL implementation on Linux
     # using the GL compositor. May not be used with "legacy" sized instances
     # due to poor LLVMPipe performance (bug 1296086).  Defaults to true for
     # unit tests on linux platforms and false otherwise
     Optional('allow-software-gl-layers'): bool,
 
-    # The worker implementation for this test, as dictated by policy and by the
-    # test platform.
-    Optional('worker-implementation'): Any(
-        'docker-worker',
-        'native-engine',
-        'generic-worker',
-        # coming soon:
-        'docker-engine',
-        'buildbot-bridge',
-    ),
-
     # For tasks that will run in docker-worker or docker-engine, this is the
     # name of the docker image or in-tree docker image to run the task in.  If
     # in-tree, then a dependency will be created automatically.  This is
     # generally `desktop-test`, or an image that acts an awful lot like it.
     Required('docker-image', default={'in-tree': 'desktop-test'}): optionally_keyed_by(
-        'test-platform', 'test-platform-phylum',
+        'test-platform',
         Any(
             # a raw Docker image path (repo/image:tag)
             basestring,
             # an in-tree generated docker image (from `taskcluster/docker/<name>`)
             {'in-tree': basestring}
         )
     ),
 
@@ -188,17 +180,17 @@ test_description_schema = Schema({
     Required('checkout', default=False): bool,
 
     # Wheter to perform a machine reboot after test is done
     Optional('reboot', default=False):
         Any(False, 'always', 'on-exception', 'on-failure'),
 
     # What to run
     Required('mozharness'): optionally_keyed_by(
-        'test-platform', 'test-platform-phylum', {
+        'test-platform', {
             # the mozharness script used to run this task
             Required('script'): basestring,
 
             # the config files required for the task
             Required('config'): optionally_keyed_by(
                 'test-platform',
                 [basestring]),
 
@@ -423,39 +415,16 @@ def set_treeherder_machine_platform(conf
     }
     for test in tests:
         test['treeherder-machine-platform'] = translation.get(
             test['build-platform'], test['test-platform'])
         yield test
 
 
 @transforms.add
-def set_worker_implementation(config, tests):
-    """Set the worker implementation based on the test platform."""
-    for test in tests:
-        test_platform = test['test-platform']
-        if test_platform.startswith('macosx'):
-            if config.config['args'].taskcluster_worker:
-                test['worker-implementation'] = 'native-engine'
-            else:
-                test['worker-implementation'] = 'generic-worker'
-        elif test.get('suite', '') == 'talos':
-            if config.config['args'].taskcluster_worker:
-                test['worker-implementation'] = 'native-engine'
-            else:
-                test['worker-implementation'] = 'buildbot-bridge'
-        elif test_platform.startswith('win'):
-            test['worker-implementation'] = 'generic-worker'
-        else:
-            test['worker-implementation'] = 'docker-worker'
-
-        yield test
-
-
-@transforms.add
 def set_tier(config, tests):
     """Set the tier based on policy for all test descriptions that do not
     specify a tier otherwise."""
     for test in tests:
         if 'tier' in test:
             resolve_keyed_by(test, 'tier', item_name=test['test-name'])
 
         # only override if not set for the test
@@ -469,18 +438,16 @@ def set_tier(config, tests):
                                          'linux64/debug',
                                          'linux64-pgo/opt',
                                          'linux64-devedition/opt',
                                          'linux64-asan/opt',
                                          'android-4.3-arm7-api-15/opt',
                                          'android-4.3-arm7-api-15/debug',
                                          'android-4.2-x86/opt']:
                 test['tier'] = 1
-            elif test['worker-implementation'] == 'native-engine':
-                test['tier'] = 3
             else:
                 test['tier'] = 2
         yield test
 
 
 @transforms.add
 def set_expires_after(config, tests):
     """Try jobs expire after 2 weeks; everything else lasts 1 year.  This helps
@@ -718,16 +685,45 @@ def parallel_stylo_tests(config, tests):
         if test['test-platform'].startswith('linux64-stylo/'):
             # add parallel stylo tests
             test['mozharness'].setdefault('extra-options', [])\
                               .append('--parallel-stylo-traversal')
             yield test
 
 
 @transforms.add
+def set_worker_type(config, tests):
+    """Set the worker type based on the test platform."""
+    for test in tests:
+        # during the taskcluuster migration, this is a bit tortured, but it
+        # will get simpler eventually!
+        test_platform = test['test-platform']
+        if test_platform.startswith('macosx'):
+            # note that some portion of these will be allocated to BBB below
+            test['worker-type'] = 'tc-worker-provisioner/gecko-t-osx-10-10'
+        elif test_platform.startswith('win'):
+            if test.get('suite', '') == 'talos':
+                test['worker-type'] = 'buildbot-bridge/buildbot-bridge'
+            else:
+                test['worker-type'] = WINDOWS_WORKER_TYPES[test_platform.split('/')[0]]
+        elif test_platform.startswith('linux') or test_platform.startswith('android'):
+            if test.get('suite', '') == 'talos':
+                if config.config['args'].taskcluster_worker:
+                    test['worker-type'] = 'releng-hardware/gecko-t-linux-talos'
+                else:
+                    test['worker-type'] = 'buildbot-bridge/buildbot-bridge'
+            else:
+                test['worker-type'] = LINUX_WORKER_TYPES[test['instance-size']]
+        else:
+            raise Exception("unknown test_platform {}".format(test_platform))
+
+        yield test
+
+
+@transforms.add
 def allocate_to_bbb(config, tests):
     """Make the load balancing between taskcluster and buildbot"""
     j = get_load_balacing_settings()
 
     tests_set = defaultdict(list)
     for test in tests:
         tests_set[test['test-platform']].append(test)
 
@@ -738,17 +734,17 @@ def allocate_to_bbb(config, tests):
         # The json file tells the percentage of tasks that run on
         # taskcluster. The logic here is inverted, as tasks have been
         # previously assigned to taskcluster. Therefore we assign the
         # 1-p tasks to buildbot-bridge.
         n = j.get(test_platform, 1.0)
         if not (test_platform.startswith('mac')
                 and config.config['args'].taskcluster_worker):
             for i in range(int(n * len(t)), len(t)):
-                t[i]['worker-implementation'] = 'buildbot-bridge'
+                t[i]['worker-type'] = 'buildbot-bridge/buildbot-bridge'
 
         for y in t:
             yield y
 
 
 @transforms.add
 def make_job_description(config, tests):
     """Convert *test* descriptions to *job* descriptions (input to
@@ -819,39 +815,19 @@ def make_job_description(config, tests):
         # run SETA unless this is a try push
         jobdesc['optimizations'] = optimizations = []
         if config.params['project'] != 'try':
             optimizations.append(['seta'])
 
         run = jobdesc['run'] = {}
         run['using'] = 'mozharness-test'
         run['test'] = test
-        worker = jobdesc['worker'] = {}
-        implementation = worker['implementation'] = test['worker-implementation']
 
-        # TODO: need some better way to express this...
-        if implementation == 'buildbot-bridge':
-            jobdesc['worker-type'] = 'buildbot-bridge/buildbot-bridge'
-        elif implementation == 'native-engine':
-            if test['test-platform'].startswith('linux'):
-                jobdesc['worker-type'] = 'releng-hardware/gecko-t-linux-talos'
-            else:
-                jobdesc['worker-type'] = 'tc-worker-provisioner/gecko-t-osx-10-10'
-        elif implementation == 'generic-worker':
-            test_platform = test['test-platform'].split('/')[0]
-            jobdesc['worker-type'] = WORKER_TYPE[test_platform]
-        elif implementation == 'docker-worker' or implementation == 'docker-engine':
-            jobdesc['worker-type'] = WORKER_TYPE[test['instance-size']]
-            worker = jobdesc['worker']
-            worker['docker-image'] = test['docker-image']
-            worker['allow-ptrace'] = True  # required for all tests, for crashreporter
-            worker['loopback-video'] = test['loopback-video']
-            worker['loopback-audio'] = test['loopback-audio']
-            worker['max-run-time'] = test['max-run-time']
-            worker['retry-exit-status'] = test['retry-exit-status']
+        jobdesc['worker-type'] = test.pop('worker-type')
+
         yield jobdesc
 
 
 def normpath(path):
     return path.replace('/', '\\')
 
 
 def get_firefox_version():
new file mode 100644
--- /dev/null
+++ b/taskcluster/taskgraph/util/workertypes.py
@@ -0,0 +1,49 @@
+# 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/.
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+WORKER_TYPES = {
+    'aws-provisioner-v1/gecko-images': ('docker-worker', 'linux'),
+    'aws-provisioner-v1/gecko-1-b-android': ('docker-worker', 'linux'),
+    'aws-provisioner-v1/gecko-1-b-linux': ('docker-worker', 'linux'),
+    'aws-provisioner-v1/gecko-1-b-macosx64': ('docker-worker', 'linux'),
+    'aws-provisioner-v1/gecko-1-b-win2012': ('generic-worker', 'windows'),
+    'aws-provisioner-v1/gecko-2-b-android': ('docker-worker', 'linux'),
+    'aws-provisioner-v1/gecko-2-b-linux': ('docker-worker', 'linux'),
+    'aws-provisioner-v1/gecko-2-b-macosx64': ('docker-worker', 'linux'),
+    'aws-provisioner-v1/gecko-2-b-win2012': ('generic-worker', 'windows'),
+    'aws-provisioner-v1/gecko-3-b-android': ('docker-worker', 'linux'),
+    'aws-provisioner-v1/gecko-3-b-linux': ('docker-worker', 'linux'),
+    'aws-provisioner-v1/gecko-3-b-macosx64': ('docker-worker', 'linux'),
+    'aws-provisioner-v1/gecko-3-b-win2012': ('generic-worker', 'windows'),
+    'aws-provisioner-v1/gecko-symbol-upload': ('docker-worker', 'linux'),
+    'aws-provisioner-v1/gecko-t-linux-large': ('docker-worker', 'linux'),
+    'aws-provisioner-v1/gecko-t-linux-medium': ('docker-worker', 'linux'),
+    'aws-provisioner-v1/gecko-t-linux-xlarge': ('docker-worker', 'linux'),
+    'aws-provisioner-v1/gecko-t-win10-64': ('generic-worker', 'windows'),
+    'aws-provisioner-v1/gecko-t-win10-64-gpu': ('generic-worker', 'windows'),
+    'aws-provisioner-v1/gecko-t-win7-32': ('generic-worker', 'windows'),
+    'aws-provisioner-v1/gecko-t-win7-32-gpu': ('generic-worker', 'windows'),
+    'aws-provisioner-v1/taskcluster-generic': ('docker-worker', 'linux'),
+    'buildbot-bridge/buildbot-bridge': ('buildbot-bridge', None),
+    'invalid/invalid': ('invalid', None),
+    'null-provisioner/human-breakpoint': ('push-apk-breakpoint', None),
+    'null-provisioner/human-breakpoint': ('push-apk-breakpoint', None),
+    'releng-hardware/gecko-t-linux-talos': ('native-engine', 'linux'),
+    'scriptworker-prov-v1/balrogworker-v1': ('balrog', None),
+    'scriptworker-prov-v1/beetmoverworker-v1': ('beetmover', None),
+    'scriptworker-prov-v1/pushapk-v1': ('push-apk', None),
+    "scriptworker-prov-v1/signing-linux-v1": ('scriptworker-signing', None),
+    'tc-worker-provisioner/gecko-t-osx-10-10': ('native-engine', 'macosx'),
+}
+
+
+def worker_type_implementation(worker_type):
+    """Get the worker implementation and OS for the given workerType, where the
+    OS represents the host system, not the target OS, in the case of
+    cross-compiles."""
+    # assume that worker types for all levels are the same implementation
+    worker_type = worker_type.replace('{level}', '1')
+    return WORKER_TYPES[worker_type]
--- a/testing/firefox-ui/resources/cookies/cookie_single.html
+++ b/testing/firefox-ui/resources/cookies/cookie_single.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html lang="en" dir="ltr">
 <head>
 <script type="text/javascript">
-  function setCookie()
-  {
+  function setCookie() {
     var date = new Date();
     date.setDate(new Date().getDate() + 36);
     document.cookie = "litmus_1=true;expires=" + date.toGMTString();
   }
 </script>
 </head>
 
 <body onload="setCookie()">
--- a/testing/firefox-ui/resources/security/enable_privilege.html
+++ b/testing/firefox-ui/resources/security/enable_privilege.html
@@ -1,20 +1,20 @@
 <!DOCTYPE html>
 <html lang="en" dir="ltr">
   <head>
     <title>Test page for enablePrivilege</title>
     <script>
       function init() {
         var result = document.getElementById("result");
         try {
+          /* globals netscape */
           netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
           result.textContent = "FAIL";
-        }
-        catch (ex) {
+        } catch (ex) {
           result.textContent = "PASS";
         }
       }
     </script>
   </head>
   <body onload="init();">
     <p id="result"></p>
   </body>
--- a/testing/mochitest/jar.mn
+++ b/testing/mochitest/jar.mn
@@ -24,17 +24,17 @@ mochikit.jar:
   content/tests/SimpleTest/ExtensionTestUtils.js (tests/SimpleTest/ExtensionTestUtils.js)
   content/tests/SimpleTest/SpawnTask.js (tests/SimpleTest/SpawnTask.js)
   content/tests/SimpleTest/AsyncUtilsContent.js (tests/SimpleTest/AsyncUtilsContent.js)
   content/tests/SimpleTest/LogController.js (tests/SimpleTest/LogController.js)
   content/tests/SimpleTest/MemoryStats.js (tests/SimpleTest/MemoryStats.js)
   content/tests/SimpleTest/MozillaLogger.js (../specialpowers/content/MozillaLogger.js)
   content/tests/SimpleTest/specialpowers.js (../specialpowers/content/specialpowers.js)
   content/tests/SimpleTest/SpecialPowersObserverAPI.js (../specialpowers/content/SpecialPowersObserverAPI.js)
-* content/tests/SimpleTest/specialpowersAPI.js (../specialpowers/content/specialpowersAPI.js)
+  content/tests/SimpleTest/specialpowersAPI.js (../specialpowers/content/specialpowersAPI.js)
   content/tests/SimpleTest/setup.js (tests/SimpleTest/setup.js)
   content/tests/SimpleTest/SimpleTest.js (tests/SimpleTest/SimpleTest.js)
   content/tests/SimpleTest/StructuredLog.jsm (../modules/StructuredLog.jsm)
   content/tests/SimpleTest/test.css (tests/SimpleTest/test.css)
   content/tests/SimpleTest/TestRunner.js (tests/SimpleTest/TestRunner.js)
   content/tests/SimpleTest/iframe-between-tests.html (tests/SimpleTest/iframe-between-tests.html)
   content/tests/SimpleTest/WindowSnapshot.js (tests/SimpleTest/WindowSnapshot.js)
   content/tests/SimpleTest/MockObjects.js (tests/SimpleTest/MockObjects.js)
--- a/testing/mozbase/mozlog/mozlog/formatters/html/main.js
+++ b/testing/mozbase/mozlog/mozlog/formatters/html/main.js
@@ -23,62 +23,62 @@ function find_all(selector, elem) {
     return toArray(elem.querySelectorAll(selector));
 }
 
 addEventListener("DOMContentLoaded", function() {
     reset_sort_headers();
 
     split_debug_onto_two_rows();
 
-    find_all('.col-links a.screenshot').forEach(function(elem) {
+    find_all(".col-links a.screenshot").forEach(function(elem) {
         elem.addEventListener("click",
                               function(event) {
                                   var node = elem;
-                                  while (node && !node.classList.contains('results-table-row')) {
+                                  while (node && !node.classList.contains("results-table-row")) {
                                       node = node.parentNode;
                                   }
                                   if (node != null) {
                                       if (node.nextSibling &&
                                           node.nextSibling.classList.contains("debug")) {
-                                          var href = find('.screenshot img', node.nextSibling).src;
+                                          var href = find(".screenshot img", node.nextSibling).src;
                                           window.open(href);
                                       }
                                   }
                                   event.preventDefault();
                               })
     });
 
-    find_all('.screenshot a').forEach(function(elem) {
+    find_all(".screenshot a").forEach(function(elem) {
         elem.addEventListener("click",
                               function(event) {
-                                  window.open(find('img', elem).getAttribute('src'));
+                                  window.open(find("img", elem).getAttribute("src"));
                                   event.preventDefault();
                               })
     });
 
-    find_all('.sortable').forEach(function(elem) {
+    find_all(".sortable").forEach(function(elem) {
         elem.addEventListener("click",
                               function(event) {
                                   toggle_sort_states(elem);
                                   var colIndex = toArray(elem.parentNode.childNodes).indexOf(elem);
-                                  var key = elem.classList.contains('numeric') ? key_num : key_alpha;
+                                  var key = elem.classList.contains("numeric") ? key_num : key_alpha;
                                   sort_table(elem, key(colIndex));
                               })
     });
 
 });
 
 function sort_table(clicked, key_func) {
     one_row_for_data();
-    var rows = find_all('.results-table-row');
-    var reversed = !clicked.classList.contains('asc');
+    var rows = find_all(".results-table-row");
+    var reversed = !clicked.classList.contains("asc");
 
     var sorted_rows = sort(rows, key_func, reversed);
 
-    var parent = document.getElementById('results-table-body');
+    var parent = document.getElementById("results-table-body");
     sorted_rows.forEach(function(elem) {
         parent.appendChild(elem);
     });
 
     split_debug_onto_two_rows();
 }
 
 function sort(items, key_func, reversed) {
@@ -107,65 +107,65 @@ function key_alpha(col_index) {
 
 function key_num(col_index) {
     return function(elem) {
         return parseFloat(elem.childNodes[col_index].firstChild.data);
     };
 }
 
 function reset_sort_headers() {
-    find_all('.sort-icon').forEach(function(elem) {
+    find_all(".sort-icon").forEach(function(elem) {
         elem.remove();
     });
-    find_all('.sortable').forEach(function(elem) {
+    find_all(".sortable").forEach(function(elem) {
         var icon = document.createElement("div");
         icon.className = "sort-icon";
         icon.textContent = "vvv";
         elem.insertBefore(icon, elem.firstChild);
         elem.classList.remove("desc", "active");
         elem.classList.add("asc", "inactive");
     });
 }
 
 function toggle_sort_states(elem) {
-    //if active, toggle between asc and desc
-    if (elem.classList.contains('active')) {
-        elem.classList.toggle('asc');
-        elem.classList.toggle('desc');
+    // if active, toggle between asc and desc
+    if (elem.classList.contains("active")) {
+        elem.classList.toggle("asc");
+        elem.classList.toggle("desc");
     }
 
-    //if inactive, reset all other functions and add ascending active
-    if (elem.classList.contains('inactive')) {
+    // if inactive, reset all other functions and add ascending active
+    if (elem.classList.contains("inactive")) {
         reset_sort_headers();
-        elem.classList.remove('inactive');
-        elem.classList.add('active');
+        elem.classList.remove("inactive");
+        elem.classList.add("active");
     }
 }
 
 function split_debug_onto_two_rows() {
-    find_all('tr.results-table-row').forEach(function(elem) {
+    find_all("tr.results-table-row").forEach(function(elem) {
         var new_row = document.createElement("tr")
         new_row.className = "debug";
         elem.parentNode.insertBefore(new_row, elem.nextSibling);
-        find_all(".debug", elem).forEach(function (td_elem) {
+        find_all(".debug", elem).forEach(function(td_elem) {
             if (find(".log", td_elem)) {
                 new_row.appendChild(td_elem);
-                td_elem.colSpan=5;
+                td_elem.colSpan = 5;
             } else {
                 td_elem.remove();
             }
         });
     });
 }
 
 function one_row_for_data() {
-    find_all('tr.results-table-row').forEach(function(elem) {
-        if (elem.nextSibling.classList.contains('debug')) {
+    find_all("tr.results-table-row").forEach(function(elem) {
+        if (elem.nextSibling.classList.contains("debug")) {
             toArray(elem.nextSibling.childNodes).forEach(
-                function (td_elem) {
+                function(td_elem) {
                     elem.appendChild(td_elem);
                 })
         } else {
             var new_td = document.createElement("td");
             new_td.className = "debug";
             elem.appendChild(new_td);
         }
     });
--- a/testing/mozbase/mozprofile/tests/files/prefs_with_interpolation.js
+++ b/testing/mozbase/mozprofile/tests/files/prefs_with_interpolation.js
@@ -1,4 +1,5 @@
+/* globals user_pref */
 user_pref("browser.foo", "http://{server}");
 user_pref("zoom.minPercent", 30);
 user_pref("webgl.verbose", "false");
 user_pref("browser.bar", "{abc}xyz");
--- a/testing/profiles/prefs_general.js
+++ b/testing/profiles/prefs_general.js
@@ -1,10 +1,10 @@
 // Base preferences file used by most test harnesses
-
+/* globals user_pref */
 user_pref("browser.console.showInPanel", true);
 user_pref("browser.dom.window.dump.enabled", true);
 user_pref("browser.firstrun.show.localepicker", false);
 user_pref("browser.firstrun.show.uidiscovery", false);
 user_pref("browser.startup.page", 0); // use about:blank, not browser.startup.homepage
 user_pref("browser.search.suggest.timeout", 10000); // use a 10s suggestion timeout in tests
 user_pref("browser.ui.layout.tablet", 0); // force tablet UI off
 user_pref("dom.allow_scripts_to_close_windows", true);
@@ -232,48 +232,48 @@ user_pref("general.useragent.updates.url
 
 // Disable webapp updates.  Yes, it is supposed to be an integer.
 user_pref("browser.webapps.checkForUpdates", 0);
 
 user_pref("dom.presentation.testing.simulate-receiver", false);
 
 // Don't connect to Yahoo! for RSS feed tests.
 // en-US only uses .types.0.uri, but set all of them just to be sure.
-user_pref('browser.contentHandlers.types.0.uri', 'http://test1.example.org/rss?url=%%s')
-user_pref('browser.contentHandlers.types.1.uri', 'http://test1.example.org/rss?url=%%s')
-user_pref('browser.contentHandlers.types.2.uri', 'http://test1.example.org/rss?url=%%s')
-user_pref('browser.contentHandlers.types.3.uri', 'http://test1.example.org/rss?url=%%s')
-user_pref('browser.contentHandlers.types.4.uri', 'http://test1.example.org/rss?url=%%s')
-user_pref('browser.contentHandlers.types.5.uri', 'http://test1.example.org/rss?url=%%s')
+user_pref("browser.contentHandlers.types.0.uri", "http://test1.example.org/rss?url=%%s")
+user_pref("browser.contentHandlers.types.1.uri", "http://test1.example.org/rss?url=%%s")
+user_pref("browser.contentHandlers.types.2.uri", "http://test1.example.org/rss?url=%%s")
+user_pref("browser.contentHandlers.types.3.uri", "http://test1.example.org/rss?url=%%s")
+user_pref("browser.contentHandlers.types.4.uri", "http://test1.example.org/rss?url=%%s")
+user_pref("browser.contentHandlers.types.5.uri", "http://test1.example.org/rss?url=%%s")
 
 // We want to collect telemetry, but we don't want to send in the results.
-user_pref('toolkit.telemetry.server', 'https://%(server)s/telemetry-dummy/');
+user_pref("toolkit.telemetry.server", "https://%(server)s/telemetry-dummy/");
 // Don't new-profile' ping on new profiles during tests, otherwise the testing framework
 // might wait on the pingsender to finish and slow down tests.
 user_pref("toolkit.telemetry.newProfilePing.enabled", false);
 
 // A couple of preferences with default values to test that telemetry preference
 // watching is working.
-user_pref('toolkit.telemetry.test.pref1', true);
-user_pref('toolkit.telemetry.test.pref2', false);
+user_pref("toolkit.telemetry.test.pref1", true);
+user_pref("toolkit.telemetry.test.pref2", false);
 
 // We don't want to hit the real Firefox Accounts server for tests.  We don't
 // actually need a functioning FxA server, so just set it to something that
 // resolves and accepts requests, even if they all fail.
-user_pref('identity.fxaccounts.auth.uri', 'https://%(server)s/fxa-dummy/');
+user_pref("identity.fxaccounts.auth.uri", "https://%(server)s/fxa-dummy/");
 
 // Ditto for all the other Firefox accounts URIs used for about:accounts et al.:
 user_pref("identity.fxaccounts.remote.signup.uri", "https://%(server)s/fxa-signup");
 user_pref("identity.fxaccounts.remote.force_auth.uri", "https://%(server)s/fxa-force-auth");
 user_pref("identity.fxaccounts.remote.signin.uri", "https://%(server)s/fxa-signin");
 user_pref("identity.fxaccounts.settings.uri", "https://%(server)s/fxa-settings");
-user_pref('identity.fxaccounts.remote.webchannel.uri', 'https://%(server)s/');
+user_pref("identity.fxaccounts.remote.webchannel.uri", "https://%(server)s/");
 
 // We don't want browser tests to perform FxA device registration.
-user_pref('identity.fxaccounts.skipDeviceRegistration', true);
+user_pref("identity.fxaccounts.skipDeviceRegistration", true);
 
 // Increase the APZ content response timeout in tests to 1 minute.
 // This is to accommodate the fact that test environments tends to be slower
 // than production environments (with the b2g emulator being the slowest of them
 // all), resulting in the production timeout value sometimes being exceeded
 // and causing false-positive test failures. See bug 1176798, bug 1177018,
 // bug 1210465.
 user_pref("apz.content_response_timeout", 60000);
--- a/testing/specialpowers/content/MockColorPicker.jsm
+++ b/testing/specialpowers/content/MockColorPicker.jsm
@@ -16,82 +16,82 @@ Cu.import("resource://gre/modules/XPCOMU
 
 // Allow stuff from this scope to be accessed from non-privileged scopes. This
 // would crash if used outside of automation.
 Cu.forcePermissiveCOWs();
 
 var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
 var oldClassID = "", oldFactory = null;
 var newClassID = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID();
-var newFactory = function (window) {
+var newFactory = function(window) {
   return {
-    createInstance: function(aOuter, aIID) {
+    createInstance(aOuter, aIID) {
       if (aOuter)
         throw Components.results.NS_ERROR_NO_AGGREGATION;
       return new MockColorPickerInstance(window).QueryInterface(aIID);
     },
-    lockFactory: function(aLock) {
+    lockFactory(aLock) {
       throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
     },
     QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
   };
 }
 
 this.MockColorPicker = {
-  init: function(window) {
+  init(window) {
     this.reset();
     this.factory = newFactory(window);
     if (!registrar.isCIDRegistered(newClassID)) {
       try {
         oldClassID = registrar.contractIDToCID(CONTRACT_ID);
         oldFactory = Cm.getClassObject(Cc[CONTRACT_ID], Ci.nsIFactory);
-      } catch(ex) {
+      } catch (ex) {
         oldClassID = "";
         oldFactory = null;
         dump("TEST-INFO | can't get colorpicker registered component, " +
              "assuming there is none");
       }
       if (oldClassID != "" && oldFactory != null) {
         registrar.unregisterFactory(oldClassID, oldFactory);
       }
       registrar.registerFactory(newClassID, "", CONTRACT_ID, this.factory);
     }
   },
 
-  reset: function() {
+  reset() {
     this.returnColor = "";
     this.showCallback = null;
     this.shown = false;
     this.showing = false;
   },
 
-  cleanup: function() {
+  cleanup() {
     var previousFactory = this.factory;
     this.reset();
     this.factory = null;
 
     registrar.unregisterFactory(newClassID, previousFactory);
     if (oldClassID != "" && oldFactory != null) {
       registrar.registerFactory(oldClassID, "", CONTRACT_ID, oldFactory);
     }
   }
 };
 
 function MockColorPickerInstance(window) {
   this.window = window;
-};
+}
 MockColorPickerInstance.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIColorPicker]),
-  init: function(aParent, aTitle, aInitialColor) {
+  init(aParent, aTitle, aInitialColor) {
     this.parent = aParent;
     this.initialColor = aInitialColor;
   },
   initialColor: "",
   parent: null,
-  open: function(aColorPickerShownCallback) {
+  open(aColorPickerShownCallback) {
     MockColorPicker.showing = true;
     MockColorPicker.shown = true;
 
     this.window.setTimeout(() => {
       let result = "";
       try {
         if (typeof MockColorPicker.showCallback == "function") {
           var updateCb = function(color) {
@@ -100,17 +100,17 @@ MockColorPickerInstance.prototype = {
           };
           let returnColor = MockColorPicker.showCallback(this, updateCb);
           if (typeof returnColor === "string") {
             result = returnColor;
           }
         } else if (typeof MockColorPicker.returnColor === "string") {
           result = MockColorPicker.returnColor;
         }
-      } catch(ex) {
+      } catch (ex) {
         dump("TEST-UNEXPECTED-FAIL | Exception in MockColorPicker.jsm open() " +
              "method: " + ex + "\n");
       }
       if (aColorPickerShownCallback) {
         aColorPickerShownCallback.done(result);
       }
     }, 0);
   }
--- a/testing/specialpowers/content/MockFilePicker.jsm
+++ b/testing/specialpowers/content/MockFilePicker.jsm
@@ -17,24 +17,24 @@ Cu.import("resource://gre/modules/XPCOMU
 
 // Allow stuff from this scope to be accessed from non-privileged scopes. This
 // would crash if used outside of automation.
 Cu.forcePermissiveCOWs();
 
 var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
 var oldClassID, oldFactory;
 var newClassID = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID();
-var newFactory = function (window) {
+var newFactory = function(window) {
   return {
-    createInstance: function(aOuter, aIID) {
+    createInstance(aOuter, aIID) {
       if (aOuter)
         throw Components.results.NS_ERROR_NO_AGGREGATION;
       return new MockFilePickerInstance(window).QueryInterface(aIID);
     },
-    lockFactory: function(aLock) {
+    lockFactory(aLock) {
       throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
     },
     QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
   };
 }
 
 this.MockFilePicker = {
   returnOK: Ci.nsIFilePicker.returnOK,
@@ -50,45 +50,45 @@ this.MockFilePicker = {
   filterApps: Ci.nsIFilePicker.filterApps,
   filterAllowURLs: Ci.nsIFilePicker.filterAllowURLs,
   filterAudio: Ci.nsIFilePicker.filterAudio,
   filterVideo: Ci.nsIFilePicker.filterVideo,
 
   window: null,
   pendingPromises: [],
 
-  init: function(window) {
+  init(window) {
     this.window = window;
 
     this.reset();
     this.factory = newFactory(window);
     if (!registrar.isCIDRegistered(newClassID)) {
       oldClassID = registrar.contractIDToCID(CONTRACT_ID);
       oldFactory = Cm.getClassObject(Cc[CONTRACT_ID], Ci.nsIFactory);
       registrar.unregisterFactory(oldClassID, oldFactory);
       registrar.registerFactory(newClassID, "", CONTRACT_ID, this.factory);
     }
   },
 
-  reset: function() {
+  reset() {
     this.appendFilterCallback = null;
     this.appendFiltersCallback = null;
     this.displayDirectory = null;
     this.displaySpecialDirectory = "";
     this.filterIndex = 0;
     this.mode = null;
     this.returnData = [];
     this.returnValue = null;
     this.showCallback = null;
     this.afterOpenCallback = null;
     this.shown = false;
     this.showing = false;
   },
 
-  cleanup: function() {
+  cleanup() {
     var previousFactory = this.factory;
     this.reset();
     this.factory = null;
     if (oldFactory) {
       registrar.unregisterFactory(newClassID, previousFactory);
       registrar.registerFactory(oldClassID, "", CONTRACT_ID, oldFactory);
     }
   },
@@ -96,87 +96,87 @@ this.MockFilePicker = {
   internalFileData(obj) {
     return {
       nsIFile: "nsIFile" in obj ? obj.nsIFile : null,
       domFile: "domFile" in obj ? obj.domFile : null,
       domDirectory: "domDirectory" in obj ? obj.domDirectory : null,
     };
   },
 
-  useAnyFile: function() {
+  useAnyFile() {
     var file = FileUtils.getDir("TmpD", [], false);
     file.append("testfile");
     file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o644);
     let promise = this.window.File.createFromNsIFile(file)
                   .then(domFile => domFile, () => null)
                   // domFile can be null.
                   .then(domFile => {
-                    this.returnData = [this.internalFileData({ nsIFile: file, domFile: domFile })];
+                    this.returnData = [this.internalFileData({ nsIFile: file, domFile })];
                   }).then(() => file);
 
     this.pendingPromises = [promise];
 
     // We return a promise in order to support some existing mochitests.
     return promise;
   },
 
-  useBlobFile: function() {
+  useBlobFile() {
     var blob = new this.window.Blob([]);
-    var file = new this.window.File([blob], 'helloworld.txt', { type: 'plain/text' });
+    var file = new this.window.File([blob], "helloworld.txt", { type: "plain/text" });
     this.returnData = [this.internalFileData({ domFile: file })];
     this.pendingPromises = [];
   },
 
-  useDirectory: function(aPath) {
+  useDirectory(aPath) {
     var directory = new this.window.Directory(aPath);
     this.returnData = [this.internalFileData({ domDirectory: directory })];
     this.pendingPromises = [];
   },
 
   setFiles(files) {
     this.returnData = [];
     this.pendingPromises = [];
 
     for (let file of files) {
       if (file instanceof this.window.File) {
         this.returnData.push(this.internalFileData({ domFile: file }));
       } else {
         let promise = this.window.File.createFromNsIFile(file, { existenceCheck: false });
 
         promise.then(domFile => {
-          this.returnData.push(this.internalFileData({ nsIFile: file, domFile: domFile }));
+          this.returnData.push(this.internalFileData({ nsIFile: file, domFile }));
         });
         this.pendingPromises.push(promise);
       }
     }
   },
 
   getNsIFile() {
     if (this.returnData.length >= 1) {
       return this.returnData[0].nsIFile;
     }
     return null;
   }
 };
 
 function MockFilePickerInstance(window) {
   this.window = window;
-};
+}
 MockFilePickerInstance.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIFilePicker]),
-  init: function(aParent, aTitle, aMode) {
+  init(aParent, aTitle, aMode) {
     MockFilePicker.mode = aMode;
     this.filterIndex = MockFilePicker.filterIndex;
     this.parent = aParent;
   },
-  appendFilter: function(aTitle, aFilter) {
+  appendFilter(aTitle, aFilter) {
     if (typeof MockFilePicker.appendFilterCallback == "function")
       MockFilePicker.appendFilterCallback(this, aTitle, aFilter);
   },
-  appendFilters: function(aFilterMask) {
+  appendFilters(aFilterMask) {
     if (typeof MockFilePicker.appendFiltersCallback == "function")
       MockFilePicker.appendFiltersCallback(this, aFilterMask);
   },
   defaultString: "",
   defaultExtension: "",
   parent: null,
   filterIndex: 0,
   displayDirectory: null,
@@ -185,17 +185,17 @@ MockFilePickerInstance.prototype = {
     if (MockFilePicker.returnData.length >= 1) {
       return MockFilePicker.returnData[0].nsIFile;
     }
 
     return null;
   },
 
   // We don't support directories here.
-  get domFileOrDirectory()  {
+  get domFileOrDirectory() {
     if (MockFilePicker.returnData.length < 1) {
       return null;
     }
 
     if (MockFilePicker.returnData[0].domFile) {
       return MockFilePicker.returnData[0].domFile;
     }
 
@@ -212,54 +212,54 @@ MockFilePickerInstance.prototype = {
     }
 
     return null;
   },
   get files() {
     return {
       index: 0,
       QueryInterface: XPCOMUtils.generateQI([Ci.nsISimpleEnumerator]),
-      hasMoreElements: function() {
+      hasMoreElements() {
         return this.index < MockFilePicker.returnData.length;
       },
-      getNext: function() {
+      getNext() {
         if (!MockFilePicker.returnData[this.index].nsIFile) {
           return null;
         }
         return MockFilePicker.returnData[this.index++].nsIFile;
       }
     };
   },
-  get domFileOrDirectoryEnumerator()  {
-    let utils = this.parent.QueryInterface(Ci.nsIInterfaceRequestor)
-                           .getInterface(Ci.nsIDOMWindowUtils);
+  get domFileOrDirectoryEnumerator() {
+    this.parent.QueryInterface(Ci.nsIInterfaceRequestor)
+               .getInterface(Ci.nsIDOMWindowUtils);
     return {
       index: 0,
       QueryInterface: XPCOMUtils.generateQI([Ci.nsISimpleEnumerator]),
-      hasMoreElements: function() {
+      hasMoreElements() {
         return this.index < MockFilePicker.returnData.length;
       },
-      getNext: function() {
+      getNext() {
         // window.File does not implement nsIFile
         if (MockFilePicker.returnData[this.index].domFile) {
           return MockFilePicker.returnData[this.index++].domFile;
         }
 
         if (MockFilePicker.returnData[this.index].domDirectory) {
           return MockFilePicker.returnData[this.index++].domDirectory;
         }
 
         return null;
       }
     };
   },
-  show: function() {
+  show() {
     throw "This is not implemented";
   },
-  open: function(aFilePickerShownCallback) {
+  open(aFilePickerShownCallback) {
     MockFilePicker.showing = true;
     this.window.setTimeout(() => {
       // Maybe all the pending promises are already resolved, but we want to be sure.
       Promise.all(MockFilePicker.pendingPromises).then(() => {
         return Ci.nsIFilePicker.returnOK;
       }, () => {
         return Ci.nsIFilePicker.returnCancel;
       }).then(result => {
@@ -274,17 +274,17 @@ MockFilePickerInstance.prototype = {
         MockFilePicker.displaySpecialDirectory = this.displaySpecialDirectory;
         MockFilePicker.shown = true;
         if (typeof MockFilePicker.showCallback == "function") {
           try {
             var returnValue = MockFilePicker.showCallback(this);
             if (typeof returnValue != "undefined") {
               return returnValue;
             }
-          } catch(ex) {
+          } catch (ex) {
             return Ci.nsIFilePicker.returnCancel;
           }
         }
 
         return MockFilePicker.returnValue;
       }).then(result => {
         // Some additional result file can be set by the callback. Let's
         // resolve the pending promises again.
--- a/testing/specialpowers/content/MockPermissionPrompt.jsm
+++ b/testing/specialpowers/content/MockPermissionPrompt.jsm
@@ -14,29 +14,29 @@ const CONTRACT_ID = "@mozilla.org/conten
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar);
 var oldClassID, oldFactory;
 var newClassID = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID();
 var newFactory = {
-  createInstance: function(aOuter, aIID) {
+  createInstance(aOuter, aIID) {
     if (aOuter)
       throw Components.results.NS_ERROR_NO_AGGREGATION;
     return new MockPermissionPromptInstance().QueryInterface(aIID);
   },
-  lockFactory: function(aLock) {
+  lockFactory(aLock) {
     throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
   },
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
 };
 
 this.MockPermissionPrompt = {
-  init: function() {
+  init() {
     this.reset();
     if (!registrar.isCIDRegistered(newClassID)) {
       try {
         oldClassID = registrar.contractIDToCID(CONTRACT_ID);
         oldFactory = Cm.getClassObject(Cc[CONTRACT_ID], Ci.nsIFactory);
       } catch (ex) {
         oldClassID = "";
         oldFactory = null;
@@ -44,36 +44,36 @@ this.MockPermissionPrompt = {
             "assuming there is none");
       }
       if (oldFactory) {
         registrar.unregisterFactory(oldClassID, oldFactory);
       }
       registrar.registerFactory(newClassID, "", CONTRACT_ID, newFactory);
     }
   },
-  
-  reset: function() {
+
+  reset() {
   },
-  
-  cleanup: function() {
+
+  cleanup() {
     this.reset();
     if (oldFactory) {
       registrar.unregisterFactory(newClassID, newFactory);
       registrar.registerFactory(oldClassID, "", CONTRACT_ID, oldFactory);
     }
   },
 };
 
-function MockPermissionPromptInstance() { };
+function MockPermissionPromptInstance() { }
 MockPermissionPromptInstance.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]),
 
   promptResult: Ci.nsIPermissionManager.UNKNOWN_ACTION,
 
-  prompt: function(request) {
+  prompt(request) {
 
     let perms = request.types.QueryInterface(Ci.nsIArray);
     for (let idx = 0; idx < perms.length; idx++) {
       let perm = perms.queryElementAt(idx, Ci.nsIContentPermissionType);
       if (Services.perms.testExactPermissionFromPrincipal(
            request.principal, perm.type) != Ci.nsIPermissionManager.ALLOW_ACTION) {
         request.cancel();
         return;
@@ -85,13 +85,13 @@ MockPermissionPromptInstance.prototype =
 };
 
 // Expose everything to content. We call reset() here so that all of the relevant
 // lazy expandos get added.
 MockPermissionPrompt.reset();
 function exposeAll(obj) {
   var props = {};
   for (var prop in obj)
-    props[prop] = 'rw';
+    props[prop] = "rw";
   obj.__exposedProps__ = props;
 }
 exposeAll(MockPermissionPrompt);
 exposeAll(MockPermissionPromptInstance.prototype);
--- a/testing/specialpowers/content/MozillaLogger.js
+++ b/testing/specialpowers/content/MozillaLogger.js
@@ -1,71 +1,73 @@
 /**
  * MozillaLogger, a base class logger that just logs to stdout.
  */
 
 "use strict";
 
+/* import-globals-from specialpowers.js */
+
 function MozillaLogger(aPath) {
 }
 
 function formatLogMessage(msg) {
-    return msg.info.join(' ') + "\n";
+    return msg.info.join(" ") + "\n";
 }
 
 MozillaLogger.prototype = {
-  init : function(path) {},
+  init(path) {},
 
-  getLogCallback : function() {
-    return function (msg) {
+  getLogCallback() {
+    return function(msg) {
       var data = formatLogMessage(msg);
       dump(data);
     };
   },
 
-  log : function(msg) {
+  log(msg) {
     dump(msg);
   },
 
-  close : function() {}
+  close() {}
 };
 
 
 /**
  * SpecialPowersLogger, inherits from MozillaLogger and utilizes SpecialPowers.
  * intented to be used in content scripts to write to a file
  */
 function SpecialPowersLogger(aPath) {
   // Call the base constructor
   MozillaLogger.call(this);
   this.prototype = new MozillaLogger(aPath);
   this.init(aPath);
 }
 
 SpecialPowersLogger.prototype = {
-  init : function (path) {
+  init(path) {
     SpecialPowers.setLogFile(path);
   },
 
-  getLogCallback : function () {
-    return function (msg) {
+  getLogCallback() {
+    return function(msg) {
       var data = formatLogMessage(msg);
       SpecialPowers.log(data);
 
       if (data.indexOf("SimpleTest FINISH") >= 0) {
         SpecialPowers.closeLogFile();
       }
     };
   },
 
-  log : function (msg) {
+  log(msg) {
     SpecialPowers.log(msg);
   },
 
-  close : function () {
+  close() {
     SpecialPowers.closeLogFile();
   }
 };
 
 
 /**
  * MozillaFileLogger, a log listener that can write to a local file.
  * intended to be run from chrome space
@@ -77,52 +79,52 @@ function MozillaFileLogger(aPath) {
   // Call the base constructor
   MozillaLogger.call(this);
   this.prototype = new MozillaLogger(aPath);
   this.init(aPath);
 }
 
 MozillaFileLogger.prototype = {
 
-  init : function (path) {
+  init(path) {
     var PR_WRITE_ONLY   = 0x02; // Open for writing only.
     var PR_CREATE_FILE  = 0x08;
     var PR_APPEND       = 0x10;
     this._file = Components.classes["@mozilla.org/file/local;1"].
                             createInstance(Components.interfaces.nsILocalFile);
     this._file.initWithPath(path);
     this._foStream = Components.classes["@mozilla.org/network/file-output-stream;1"].
                                      createInstance(Components.interfaces.nsIFileOutputStream);
     this._foStream.init(this._file, PR_WRITE_ONLY | PR_CREATE_FILE | PR_APPEND,
                                      436 /* 0664 */, 0);
 
     this._converter = Components.classes["@mozilla.org/intl/converter-output-stream;1"].
                     createInstance(Components.interfaces.nsIConverterOutputStream);
     this._converter.init(this._foStream, "UTF-8", 0, 0);
   },
 
-  getLogCallback : function() {
-    return function (msg) {
+  getLogCallback() {
+    return function(msg) {
       var data = formatLogMessage(msg);
       if (MozillaFileLogger._converter) {
         this._converter.writeString(data);
       }
 
       if (data.indexOf("SimpleTest FINISH") >= 0) {
         MozillaFileLogger.close();
       }
     };
   },
 
-  log : function(msg) {
+  log(msg) {
     if (this._converter) {
       this._converter.writeString(msg);
     }
   },
-  close : function() {
+  close() {
     if (this._converter) {
       this._converter.flush();
       this._converter.close();
     }
 
     this._foStream = null;
     this._converter = null;
     this._file = null;
--- a/testing/specialpowers/content/SpecialPowersObserver.jsm
+++ b/testing/specialpowers/content/SpecialPowersObserver.jsm
@@ -3,21 +3,23 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Based on:
 // https://bugzilla.mozilla.org/show_bug.cgi?id=549539
 // https://bug549539.bugzilla.mozilla.org/attachment.cgi?id=429661
 // https://developer.mozilla.org/en/XPCOM/XPCOM_changes_in_Gecko_1.9.3
 // https://developer.mozilla.org/en/how_to_build_an_xpcom_component_in_javascript
 
+/* import-globals-from SpecialPowersObserverAPI.js */
+
 var EXPORTED_SYMBOLS = ["SpecialPowersObserver", "SpecialPowersObserverFactory"];
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
-Components.utils.importGlobalProperties(['File']);
+Components.utils.importGlobalProperties(["File"]);
 
 if (typeof(Cc) == "undefined") {
   const Cc = Components.classes;
   const Ci = Components.interfaces;
 }
 
 const CHILD_SCRIPT = "chrome://specialpowers/content/specialpowers.js"
 const CHILD_SCRIPT_API = "chrome://specialpowers/content/specialpowersAPI.js"
@@ -39,38 +41,36 @@ this.SpecialPowersObserver = function Sp
 
 SpecialPowersObserver.prototype = new SpecialPowersObserverAPI();
 
 SpecialPowersObserver.prototype.classDescription = "Special powers Observer for use in testing.";
 SpecialPowersObserver.prototype.classID = Components.ID("{59a52458-13e0-4d93-9d85-a637344f29a1}");
 SpecialPowersObserver.prototype.contractID = "@mozilla.org/special-powers-observer;1";
 SpecialPowersObserver.prototype.QueryInterface = XPCOMUtils.generateQI([Components.interfaces.nsIObserver]);
 
-SpecialPowersObserver.prototype.observe = function(aSubject, aTopic, aData)
-{
+SpecialPowersObserver.prototype.observe = function(aSubject, aTopic, aData) {
   switch (aTopic) {
     case "chrome-document-global-created":
       this._loadFrameScript();
       break;
 
     case "http-on-modify-request":
       if (aSubject instanceof Ci.nsIChannel) {
         let uri = aSubject.URI.spec;
-        this._sendAsyncMessage("specialpowers-http-notify-request", { uri: uri });
+        this._sendAsyncMessage("specialpowers-http-notify-request", { uri });
       }
       break;
 
     default:
       this._observe(aSubject, aTopic, aData);
       break;
   }
 };
 
-SpecialPowersObserver.prototype._loadFrameScript = function()
-{
+SpecialPowersObserver.prototype._loadFrameScript = function() {
   if (!this._isFrameScriptLoaded) {
     // Register for any messages our API needs us to handle
     this._messageManager.addMessageListener("SPPrefService", this);
     this._messageManager.addMessageListener("SPProcessCrashService", this);
     this._messageManager.addMessageListener("SPPingService", this);
     this._messageManager.addMessageListener("SpecialPowers.Quit", this);
     this._messageManager.addMessageListener("SpecialPowers.Focus", this);
     this._messageManager.addMessageListener("SpecialPowers.CreateFiles", this);
@@ -91,27 +91,25 @@ SpecialPowersObserver.prototype._loadFra
     this._messageManager.loadFrameScript(CHILD_LOGGER_SCRIPT, true);
     this._messageManager.loadFrameScript(CHILD_SCRIPT_API, true);
     this._messageManager.loadFrameScript(CHILD_SCRIPT, true);
     this._isFrameScriptLoaded = true;
     this._createdFiles = null;
   }
 };
 
-SpecialPowersObserver.prototype._sendAsyncMessage = function(msgname, msg)
-{
+SpecialPowersObserver.prototype._sendAsyncMessage = function(msgname, msg) {
   this._messageManager.broadcastAsyncMessage(msgname, msg);
 };
 
 SpecialPowersObserver.prototype._receiveMessage = function(aMessage) {
   return this._receiveMessageAPI(aMessage);
 };
 
-SpecialPowersObserver.prototype.init = function()
-{
+SpecialPowersObserver.prototype.init = function() {
   var obs = Services.obs;
   obs.addObserver(this, "chrome-document-global-created");
 
   // Register special testing modules.
   var testsURI = Cc["@mozilla.org/file/directory_service;1"].
                    getService(Ci.nsIProperties).
                    get("ProfD", Ci.nsILocalFile);
   testsURI.append("tests.manifest");
@@ -123,18 +121,17 @@ SpecialPowersObserver.prototype.init = f
   Components.manager.QueryInterface(Ci.nsIComponentRegistrar).
                  autoRegister(manifestFile);
 
   obs.addObserver(this, "http-on-modify-request");
 
   this._loadFrameScript();
 };
 
-SpecialPowersObserver.prototype.uninit = function()
-{
+SpecialPowersObserver.prototype.uninit = function() {
   var obs = Services.obs;
   obs.removeObserver(this, "chrome-document-global-created");
   obs.removeObserver(this, "http-on-modify-request");
   this._registerObservers._topics.forEach(function(element) {
     obs.removeObserver(this._registerObservers, element);
   });
   this._removeProcessCrashObservers();
 
@@ -190,24 +187,24 @@ SpecialPowersObserver.prototype._removeP
   obs.removeObserver(this, "plugin-crashed");
   obs.removeObserver(this, "ipc:content-shutdown");
   this._processCrashObserversRegistered = false;
 };
 
 SpecialPowersObserver.prototype._registerObservers = {
   _self: null,
   _topics: [],
-  _add: function(topic) {
+  _add(topic) {
     if (this._topics.indexOf(topic) < 0) {
       this._topics.push(topic);
       Services.obs.addObserver(this, topic);
     }
   },
-  observe: function (aSubject, aTopic, aData) {
-    var msg = { aData: aData };
+  observe(aSubject, aTopic, aData) {
+    var msg = { aData };
     switch (aTopic) {
       case "perm-changed":
         var permission = aSubject.QueryInterface(Ci.nsIPermission);
 
         // specialPowersAPI will consume this value, and it is used as a
         // fake permission, but only type and principal.appId will be used.
         //
         // We need to ensure that it looks the same as a real permission,
@@ -224,17 +221,17 @@ SpecialPowersObserver.prototype._registe
   }
 };
 
 /**
  * messageManager callback function
  * This will get requests from our API in the window and process them in chrome for it
  **/
 SpecialPowersObserver.prototype.receiveMessage = function(aMessage) {
-  switch(aMessage.name) {
+  switch (aMessage.name) {
     case "SPPingService":
       if (aMessage.json.op == "ping") {
         aMessage.target
                 .QueryInterface(Ci.nsIFrameLoaderOwner)
                 .frameLoader
                 .messageManager
                 .sendAsyncMessage("SPPingService", { op: "pong" });
       }
@@ -242,25 +239,25 @@ SpecialPowersObserver.prototype.receiveM
     case "SpecialPowers.Quit":
       let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
       appStartup.quit(Ci.nsIAppStartup.eForceQuit);
       break;
     case "SpecialPowers.Focus":
       aMessage.target.focus();
       break;
     case "SpecialPowers.CreateFiles":
-      let filePaths = new Array;
+      let filePaths = [];
       if (!this._createdFiles) {
-        this._createdFiles = new Array;
+        this._createdFiles = [];
       }
       let createdFiles = this._createdFiles;
       try {
         let promises = [];
         aMessage.data.forEach(function(request) {
-          const filePerms = 0666;
+          const filePerms = 0666; // eslint-disable-line no-octal
           let testFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
           if (request.name) {
             testFile.appendRelativePath(request.name);
           } else {
             testFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, filePerms);
           }
           let outStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
           outStream.init(testFile, 0x02 | 0x08 | 0x20, // PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE
@@ -294,30 +291,31 @@ SpecialPowersObserver.prototype.receiveM
                   .frameLoader
                   .messageManager
                   .sendAsyncMessage("SpecialPowers.FilesError", e.toString());
       }
 
       break;
     case "SpecialPowers.RemoveFiles":
       if (this._createdFiles) {
-        this._createdFiles.forEach(function (testFile) {
+        this._createdFiles.forEach(function(testFile) {
           try {
             testFile.remove(false);
           } catch (e) {}
         });
         this._createdFiles = null;
       }
       break;
     default:
       return this._receiveMessage(aMessage);
   }
+  return undefined;
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SpecialPowersObserver]);
 this.SpecialPowersObserverFactory = Object.freeze({
-  createInstance: function(outer, id) {
-    if (outer) { throw Components.results.NS_ERROR_NO_AGGREGATION };
+  createInstance(outer, id) {
+    if (outer) { throw Components.results.NS_ERROR_NO_AGGREGATION }
     return new SpecialPowersObserver();
   },
-  loadFactory: function(lock){},
+  loadFactory(lock) {},
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory])
 });
--- a/testing/specialpowers/content/SpecialPowersObserverAPI.js
+++ b/testing/specialpowers/content/SpecialPowersObserverAPI.js
@@ -2,27 +2,27 @@
  * 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";
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource://gre/modules/NetUtil.jsm");
 
-if (typeof(Ci) == 'undefined') {
+if (typeof(Ci) == "undefined") {
   var Ci = Components.interfaces;
 }
 
-if (typeof(Cc) == 'undefined') {
+if (typeof(Cc) == "undefined") {
   var Cc = Components.classes;
 }
 
 this.SpecialPowersError = function(aMsg) {
   Error.call(this);
-  let {stack} = new Error();
+  // let {stack} = new Error();
   this.message = aMsg;
   this.name = "SpecialPowersError";
 }
 SpecialPowersError.prototype = Object.create(Error.prototype);
 
 SpecialPowersError.prototype.toString = function() {
   return `${this.name}: ${this.message}`;
 };
@@ -30,24 +30,24 @@ SpecialPowersError.prototype.toString = 
 this.SpecialPowersObserverAPI = function SpecialPowersObserverAPI() {
   this._crashDumpDir = null;
   this._processCrashObserversRegistered = false;
   this._chromeScriptListeners = [];
   this._extensions = new Map();
 }
 
 function parseKeyValuePairs(text) {
-  var lines = text.split('\n');
+  var lines = text.split("\n");
   var data = {};
   for (let i = 0; i < lines.length; i++) {
-    if (lines[i] == '')
+    if (lines[i] == "")
       continue;
 
     // can't just .split() because the value might contain = characters
-    let eq = lines[i].indexOf('=');
+    let eq = lines[i].indexOf("=");
     if (eq != -1) {
       let [key, value] = [lines[i].substring(0, eq),
                           lines[i].substring(eq + 1)];
       if (key && value)
         data[key] = value.replace(/\\n/g, "\n").replace(/\\\\/g, "\\");
     }
   }
   return data;
@@ -56,17 +56,17 @@ function parseKeyValuePairs(text) {
 function parseKeyValuePairsFromFile(file) {
   var fstream = Cc["@mozilla.org/network/file-input-stream;1"].
                 createInstance(Ci.nsIFileInputStream);
   fstream.init(file, -1, 0, 0);
   var is = Cc["@mozilla.org/intl/converter-input-stream;1"].
            createInstance(Ci.nsIConverterInputStream);
   is.init(fstream, "UTF-8", 1024, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
   var str = {};
-  var contents = '';
+  var contents = "";
   while (is.readString(4096, str) != 0) {
     contents += str.value;
   }
   is.close();
   fstream.close();
   return parseKeyValuePairs(contents);
 }
 
@@ -81,81 +81,81 @@ function getTestPlugin(pluginName) {
     }
   }
 
   return null;
 }
 
 SpecialPowersObserverAPI.prototype = {
 
-  _observe: function(aSubject, aTopic, aData) {
+  _observe(aSubject, aTopic, aData) {
     function addDumpIDToMessage(propertyName) {
       try {
         var id = aSubject.getPropertyAsAString(propertyName);
-      } catch(ex) {
-        var id = null;
+      } catch (ex) {
+        id = null;
       }
       if (id) {
-        message.dumpIDs.push({id: id, extension: "dmp"});
-        message.dumpIDs.push({id: id, extension: "extra"});
+        message.dumpIDs.push({id, extension: "dmp"});
+        message.dumpIDs.push({id, extension: "extra"});
       }
     }
 
-    switch(aTopic) {
+    switch (aTopic) {
       case "plugin-crashed":
       case "ipc:content-shutdown":
         var message = { type: "crash-observed", dumpIDs: [] };
         aSubject = aSubject.QueryInterface(Ci.nsIPropertyBag2);
         if (aTopic == "plugin-crashed") {
           addDumpIDToMessage("pluginDumpID");
           addDumpIDToMessage("browserDumpID");
 
           let pluginID = aSubject.getPropertyAsAString("pluginDumpID");
           let extra = this._getExtraData(pluginID);
           if (extra && ("additional_minidumps" in extra)) {
-            let dumpNames = extra.additional_minidumps.split(',');
+            let dumpNames = extra.additional_minidumps.split(",");
             for (let name of dumpNames) {
               message.dumpIDs.push({id: pluginID + "-" + name, extension: "dmp"});
             }
           }
         } else { // ipc:content-shutdown
           addDumpIDToMessage("dumpID");
         }
         this._sendAsyncMessage("SPProcessCrashService", message);
         break;
     }
   },
 
-  _getCrashDumpDir: function() {
+  _getCrashDumpDir() {
     if (!this._crashDumpDir) {
       this._crashDumpDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
       this._crashDumpDir.append("minidumps");
     }
     return this._crashDumpDir;
   },
 
-  _getPendingCrashDumpDir: function() {
+  _getPendingCrashDumpDir() {
     if (!this._pendingCrashDumpDir) {
       this._pendingCrashDumpDir = Services.dirsvc.get("UAppData", Ci.nsIFile);
       this._pendingCrashDumpDir.append("Crash Reports");
       this._pendingCrashDumpDir.append("pending");
     }
     return this._pendingCrashDumpDir;
   },
 
-  _getExtraData: function(dumpId) {
+  _getExtraData(dumpId) {
     let extraFile = this._getCrashDumpDir().clone();
     extraFile.append(dumpId + ".extra");
     if (!extraFile.exists()) {
       return null;
     }
     return parseKeyValuePairsFromFile(extraFile);
   },
 
-  _deleteCrashDumpFiles: function(aFilenames) {
+  _deleteCrashDumpFiles(aFilenames) {
     var crashDumpDir = this._getCrashDumpDir();
     if (!crashDumpDir.exists()) {
       return false;
     }
 
     var success = aFilenames.length != 0;
     aFilenames.forEach(function(crashFilename) {
       var file = crashDumpDir.clone();
@@ -164,17 +164,17 @@ SpecialPowersObserverAPI.prototype = {
         file.remove(false);
       } else {
         success = false;
       }
     });
     return success;
   },
 
-  _findCrashDumpFiles: function(aToIgnore) {
+  _findCrashDumpFiles(aToIgnore) {
     var crashDumpDir = this._getCrashDumpDir();
     var entries = crashDumpDir.exists() && crashDumpDir.directoryEntries;
     if (!entries) {
       return [];
     }
 
     var crashDumpFiles = [];
     while (entries.hasMoreElements()) {
@@ -182,37 +182,37 @@ SpecialPowersObserverAPI.prototype = {
       var path = String(file.path);
       if (path.match(/\.(dmp|extra)$/) && !aToIgnore[path]) {
         crashDumpFiles.push(path);
       }
     }
     return crashDumpFiles.concat();
   },
 
-  _deletePendingCrashDumpFiles: function() {
+  _deletePendingCrashDumpFiles() {
     var crashDumpDir = this._getPendingCrashDumpDir();
     var removed = false;
     if (crashDumpDir.exists()) {
       let entries = crashDumpDir.directoryEntries;
       while (entries.hasMoreElements()) {
         let file = entries.getNext().QueryInterface(Ci.nsIFile);
         if (file.isFile()) {
           file.remove(false);
           removed = true;
         }
       }
     }
     return removed;
   },
 
-  _getURI: function (url) {
+  _getURI(url) {
     return Services.io.newURI(url);
   },
 
-  _readUrlAsString: function(aUrl) {
+  _readUrlAsString(aUrl) {
     // Fetch script content as we can't use scriptloader's loadSubScript
     // to evaluate http:// urls...
     var scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
                              .getService(Ci.nsIScriptableInputStream);
 
     var channel = NetUtil.newChannel({
       uri: aUrl,
       loadUsingSystemPrincipal: true
@@ -242,25 +242,25 @@ SpecialPowersObserverAPI.prototype = {
         "Error while executing chrome script '" + aUrl + "':\n" +
         "The script doesn't exists. Ensure you have registered it in " +
         "'support-files' in your mochitest.ini.");
     }
 
     return output;
   },
 
-  _sendReply: function(aMessage, aReplyName, aReplyMsg) {
+  _sendReply(aMessage, aReplyName, aReplyMsg) {
     let mm = aMessage.target
                      .QueryInterface(Ci.nsIFrameLoaderOwner)
                      .frameLoader
                      .messageManager;
     mm.sendAsyncMessage(aReplyName, aReplyMsg);
   },
 
-  _notifyCategoryAndObservers: function(subject, topic, data) {
+  _notifyCategoryAndObservers(subject, topic, data) {
     const serviceMarker = "service,";
 
     // First create observers from the category manager.
     let cm =
       Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
     let enumerator = cm.enumerateCategory(topic);
 
     let observers = [];
@@ -269,102 +269,97 @@ SpecialPowersObserverAPI.prototype = {
       let entry =
         enumerator.getNext().QueryInterface(Ci.nsISupportsCString).data;
       let contractID = cm.getCategoryEntry(topic, entry);
 
       let factoryFunction;
       if (contractID.substring(0, serviceMarker.length) == serviceMarker) {
         contractID = contractID.substring(serviceMarker.length);
         factoryFunction = "getService";
-      }
-      else {
+      } else {
         factoryFunction = "createInstance";
       }
 
       try {
         let handler = Cc[contractID][factoryFunction]();
         if (handler) {
           let observer = handler.QueryInterface(Ci.nsIObserver);
           observers.push(observer);
         }
-      } catch(e) { }
+      } catch (e) { }
     }
 
     // Next enumerate the registered observers.
     enumerator = Services.obs.enumerateObservers(topic);
     while (enumerator.hasMoreElements()) {
       try {
         let observer = enumerator.getNext().QueryInterface(Ci.nsIObserver);
         if (observers.indexOf(observer) == -1) {
           observers.push(observer);
         }
       } catch (e) { }
     }
 
-    observers.forEach(function (observer) {
+    observers.forEach(function(observer) {
       try {
         observer.observe(subject, topic, data);
-      } catch(e) { }
+      } catch (e) { }
     });
   },
 
   /**
    * messageManager callback function
    * This will get requests from our API in the window and process them in chrome for it
    **/
-  _receiveMessageAPI: function(aMessage) {
+  _receiveMessageAPI(aMessage) { // eslint-disable-line complexity
     // We explicitly return values in the below code so that this function
     // doesn't trigger a flurry of warnings about "does not always return
     // a value".
-    switch(aMessage.name) {
+    switch (aMessage.name) {
       case "SPPrefService": {
         let prefs = Services.prefs;
         let prefType = aMessage.json.prefType.toUpperCase();
         let prefName = aMessage.json.prefName;
         let prefValue = "prefValue" in aMessage.json ? aMessage.json.prefValue : null;
 
         if (aMessage.json.op == "get") {
           if (!prefName || !prefType)
             throw new SpecialPowersError("Invalid parameters for get in SPPrefService");
 
           // return null if the pref doesn't exist
           if (prefs.getPrefType(prefName) == prefs.PREF_INVALID)
             return null;
         } else if (aMessage.json.op == "set") {
-          if (!prefName || !prefType  || prefValue === null)
+          if (!prefName || !prefType || prefValue === null)
             throw new SpecialPowersError("Invalid parameters for set in SPPrefService");
         } else if (aMessage.json.op == "clear") {
           if (!prefName)
             throw new SpecialPowersError("Invalid parameters for clear in SPPrefService");
         } else {
           throw new SpecialPowersError("Invalid operation for SPPrefService");
         }
 
         // Now we make the call
-        switch(prefType) {
+        switch (prefType) {
           case "BOOL":
             if (aMessage.json.op == "get")
-              return(prefs.getBoolPref(prefName));
-            else
-              return(prefs.setBoolPref(prefName, prefValue));
+              return (prefs.getBoolPref(prefName));
+            return (prefs.setBoolPref(prefName, prefValue));
           case "INT":
             if (aMessage.json.op == "get")
-              return(prefs.getIntPref(prefName));
-            else
-              return(prefs.setIntPref(prefName, prefValue));
+              return (prefs.getIntPref(prefName));
+            return (prefs.setIntPref(prefName, prefValue));
           case "CHAR":
             if (aMessage.json.op == "get")
-              return(prefs.getCharPref(prefName));
-            else
-              return(prefs.setCharPref(prefName, prefValue));
+              return (prefs.getCharPref(prefName));
+            return (prefs.setCharPref(prefName, prefValue));
           case "COMPLEX":
             if (aMessage.json.op == "get")
-              return(prefs.getComplexValue(prefName, prefValue[0]));
-            else
-              return(prefs.setComplexValue(prefName, prefValue[0], prefValue[1]));
+              return (prefs.getComplexValue(prefName, prefValue[0]));
+            return (prefs.setComplexValue(prefName, prefValue[0], prefValue[1]));
           case "":
             if (aMessage.json.op == "clear") {
               prefs.clearUserPref(prefName);
               return undefined;
             }
         }
         return undefined;	// See comment at the beginning of this function.
       }
@@ -462,47 +457,47 @@ SpecialPowersObserverAPI.prototype = {
         let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
         let sb = Components.utils.Sandbox(systemPrincipal);
         let mm = aMessage.target
                          .QueryInterface(Ci.nsIFrameLoaderOwner)
                          .frameLoader
                          .messageManager;
         sb.sendAsyncMessage = (name, message) => {
           mm.sendAsyncMessage("SPChromeScriptMessage",
-                              { id: id, name: name, message: message });
+                              { id, name, message });
         };
         sb.addMessageListener = (name, listener) => {
-          this._chromeScriptListeners.push({ id: id, name: name, listener: listener });
+          this._chromeScriptListeners.push({ id, name, listener });
         };
         sb.browserElement = aMessage.target;
 
         // Also expose assertion functions
-        let reporter = function (err, message, stack) {
+        let reporter = function(err, message, stack) {
           // Pipe assertions back to parent process
           mm.sendAsyncMessage("SPChromeScriptAssert",
                               { id, name: scriptName, err, message,
                                 stack });
         };
         Object.defineProperty(sb, "assert", {
-          get: function () {
+          get() {
             let scope = Components.utils.createObjectIn(sb);
             Services.scriptloader.loadSubScript("chrome://specialpowers/content/Assert.jsm",
                                                 scope);
 
             let assert = new scope.Assert(reporter);
             delete sb.assert;
             return sb.assert = assert;
           },
           configurable: true
         });
 
         // Evaluate the chrome script
         try {
           Components.utils.evalInSandbox(jsScript, sb, "1.8", scriptName, 1);
-        } catch(e) {
+        } catch (e) {
           throw new SpecialPowersError(
             "Error while executing chrome script '" + scriptName + "':\n" +
             e + "\n" +
             e.fileName + ":" + e.lineNumber);
         }
         return undefined;	// See comment at the beginning of this function.
       }
 
@@ -578,16 +573,17 @@ SpecialPowersObserverAPI.prototype = {
         // as the add-on manager runs them.
         let extensionData = new ExtensionData(extension.rootURI);
         extensionData.loadManifest().then(
           () => {
             return extensionData.initAllLocales().then(() => {
               if (extensionData.errors.length) {
                 return Promise.reject("Extension contains packaging errors");
               }
+              return undefined;
             });
           },
           () => {
             // loadManifest() will throw if we're loading an embedded
             // extension, so don't worry about locale errors in that
             // case.
           }
         ).then(() => {
@@ -618,12 +614,12 @@ SpecialPowersObserverAPI.prototype = {
       }
 
       default:
         throw new SpecialPowersError("Unrecognized Special Powers API");
     }
 
     // We throw an exception before reaching this explicit return because
     // we should never be arriving here anyway.
-    throw new SpecialPowersError("Unreached code");
+    throw new SpecialPowersError("Unreached code"); // eslint-disable-line no-unreachable
     return undefined;
   }
 };
--- a/testing/specialpowers/content/specialpowers.js
+++ b/testing/specialpowers/content/specialpowers.js
@@ -1,28 +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/. */
 /* This code is loaded in every child process that is started by mochitest in
  * order to be used as a replacement for UniversalXPConnect
  */
 
+/* import-globals-from specialpowersAPI.js */
+/* globals addMessageListener, removeMessageListener, sendSyncMessage, sendAsyncMessage */
+
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 function SpecialPowers(window) {
   this.window = Components.utils.getWeakReference(window);
   this._windowID = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                          .getInterface(Components.interfaces.nsIDOMWindowUtils)
                          .currentInnerWindowID;
   this._encounteredCrashDumpFiles = [];
   this._unexpectedCrashDumpFiles = { };
   this._crashDumpDir = null;
   this.DOMWindowUtils = bindDOMWindowUtils(window);
-  Object.defineProperty(this, 'Components', {
-      configurable: true, enumerable: true, get: function() {
+  Object.defineProperty(this, "Components", {
+      configurable: true, enumerable: true, get() {
           var win = this.window.get();
           if (!win)
               return null;
           return getRawComponents(win);
       }});
   this._pongHandlers = [];
   this._messageListener = this._messageReceived.bind(this);
   this._grandChildFrameMM = null;
@@ -54,19 +57,21 @@ function SpecialPowers(window) {
   Services.obs.addObserver(function onInnerWindowDestroyed(subject, topic, data) {
     var id = subject.QueryInterface(Components.interfaces.nsISupportsPRUint64).data;
     if (self._windowID === id) {
       Services.obs.removeObserver(onInnerWindowDestroyed, "inner-window-destroyed");
       try {
         removeMessageListener("SPPingService", self._messageListener);
         removeMessageListener("SpecialPowers.FilesCreated", self._messageListener);
         removeMessageListener("SpecialPowers.FilesError", self._messageListener);
-      } catch (e if e.result == Components.results.NS_ERROR_ILLEGAL_VALUE) {
+      } catch (e) {
         // Ignore the exception which the message manager has been destroyed.
-        ;
+        if (e.result != Components.results.NS_ERROR_ILLEGAL_VALUE) {
+          throw e;
+        }
       }
     }
   }, "inner-window-destroyed");
 }
 
 SpecialPowers.prototype = new SpecialPowersAPI();
 
 SpecialPowers.prototype.toString = function() { return "[SpecialPowers]"; };
@@ -128,30 +133,30 @@ SpecialPowers.prototype._messageReceived
         }
         if (this._grandChildFrameMM) {
           this._grandChildFrameMM.sendAsyncMessage("SPPingService", { op: "pong" });
         }
       }
       break;
 
     case "SpecialPowers.FilesCreated":
-      var handler = this._createFilesOnSuccess;
+      var createdHandler = this._createFilesOnSuccess;
       this._createFilesOnSuccess = null;
       this._createFilesOnError = null;
-      if (handler) {
-        handler(aMessage.data);
+      if (createdHandler) {
+        createdHandler(aMessage.data);
       }
       break;
 
     case "SpecialPowers.FilesError":
-      var handler = this._createFilesOnError;
+      var errorHandler = this._createFilesOnError;
       this._createFilesOnSuccess = null;
       this._createFilesOnError = null;
-      if (handler) {
-        handler(aMessage.data);
+      if (errorHandler) {
+        errorHandler(aMessage.data);
       }
       break;
   }
 
   return true;
 };
 
 SpecialPowers.prototype.quit = function() {
@@ -186,30 +191,30 @@ SpecialPowers.prototype.executeAfterFlus
 
 SpecialPowers.prototype.nestedFrameSetup = function() {
   let self = this;
   Services.obs.addObserver(function onRemoteBrowserShown(subject, topic, data) {
     let frameLoader = subject;
     // get a ref to the app <iframe>
     frameLoader.QueryInterface(Components.interfaces.nsIFrameLoader);
     let frame = frameLoader.ownerElement;
-    let frameId = frame.getAttribute('id');
+    let frameId = frame.getAttribute("id");
     if (frameId === "nested-parent-frame") {
       Services.obs.removeObserver(onRemoteBrowserShown, "remote-browser-shown");
 
       let mm = frame.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader.messageManager;
       self._grandChildFrameMM = mm;
 
-      self.SP_SYNC_MESSAGES.forEach(function (msgname) {
-        mm.addMessageListener(msgname, function (msg) {
+      self.SP_SYNC_MESSAGES.forEach(function(msgname) {
+        mm.addMessageListener(msgname, function(msg) {
           return self._sendSyncMessage(msgname, msg.data)[0];
         });
       });
-      self.SP_ASYNC_MESSAGES.forEach(function (msgname) {
-        mm.addMessageListener(msgname, function (msg) {
+      self.SP_ASYNC_MESSAGES.forEach(function(msgname) {
+        mm.addMessageListener(msgname, function(msg) {
           self._sendAsyncMessage(msgname, msg.data);
         });
       });
       mm.addMessageListener("SPPAddNestedMessageListener", function(msg) {
         self._addMessageListener(msg.json.name, function(aMsg) {
           mm.sendAsyncMessage(aMsg.name, aMsg.data);
           });
       });
@@ -238,17 +243,17 @@ function attachSpecialPowersToWindow(aWi
         (aWindow.wrappedJSObject) &&
         !(aWindow.wrappedJSObject.SpecialPowers)) {
       let sp = new SpecialPowers(aWindow);
       aWindow.wrappedJSObject.SpecialPowers = sp;
       if (sp.IsInNestedFrame) {
         sp.addPermission("allowXULXBL", true, aWindow.document);
       }
     }
-  } catch(ex) {
+  } catch (ex) {
     dump("TEST-INFO | specialpowers.js |  Failed to attach specialpowers to window exception: " + ex + "\n");
   }
 }
 
 // This is a frame script, so it may be running in a content process.
 // In any event, it is targeted at a specific "tab", so we listen for
 // the DOMWindowCreated event to be notified about content windows
 // being created in this context.
@@ -267,13 +272,13 @@ SpecialPowersManager.prototype = {
 
 var specialpowersmanager = new SpecialPowersManager();
 
 this.SpecialPowers = SpecialPowers;
 this.attachSpecialPowersToWindow = attachSpecialPowersToWindow;
 
 // In the case of Chrome mochitests that inject specialpowers.js as
 // a regular content script
-if (typeof window != 'undefined') {
+if (typeof window != "undefined") {
   window.addMessageListener = function() {}
   window.removeMessageListener = function() {}
   window.wrappedJSObject.SpecialPowers = new SpecialPowers(window);
 }
--- a/testing/specialpowers/content/specialpowersAPI.js
+++ b/testing/specialpowers/content/specialpowersAPI.js
@@ -2,16 +2,19 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 /* This code is loaded in every child process that is started by mochitest in
  * order to be used as a replacement for UniversalXPConnect
  */
 
 "use strict";
 
+/* import-globals-from MozillaLogger.js */
+/* globals XPCNativeWrapper, content */
+
 var global = this;
 
 var Ci = Components.interfaces;
 var Cc = Components.classes;
 var Cu = Components.utils;
 
 Cu.import("chrome://specialpowers/content/MockFilePicker.jsm");
 Cu.import("chrome://specialpowers/content/MockColorPicker.jsm");
@@ -21,17 +24,17 @@ Cu.import("resource://gre/modules/Privat
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 
 // We're loaded with "this" not set to the global in some cases, so we
 // have to play some games to get at the global object here.  Normally
 // we'd try "this" from a function called with undefined this value,
 // but this whole file is in strict mode.  So instead fall back on
 // returning "this" from indirect eval, which returns the global.
-if (!(function() { var e = eval; return e("this"); })().File) {
+if (!(function() { var e = eval; return e("this"); })().File) { // eslint-disable-line no-eval
     Cu.importGlobalProperties(["File"]);
 }
 
 // Allow stuff from this scope to be accessed from non-privileged scopes. This
 // would crash if used outside of automation.
 Cu.forcePermissiveCOWs();
 
 function SpecialPowersAPI() {
@@ -48,59 +51,59 @@ function SpecialPowersAPI() {
   this._applyingPermissions = false;
   this._observingPermissions = false;
   this._fm = null;
   this._cb = null;
 }
 
 function bindDOMWindowUtils(aWindow) {
   if (!aWindow)
-    return
+    return undefined;
 
-   var util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                     .getInterface(Ci.nsIDOMWindowUtils);
-   return wrapPrivileged(util);
+  var util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                    .getInterface(Ci.nsIDOMWindowUtils);
+  return wrapPrivileged(util);
 }
 
 function getRawComponents(aWindow) {
   // If we're running in automation that supports enablePrivilege, then we also
   // provided access to the privileged Components.
   try {
     let win = Cu.waiveXrays(aWindow);
-    if (typeof win.netscape.security.PrivilegeManager == 'object')
+    if (typeof win.netscape.security.PrivilegeManager == "object")
       Cu.forcePrivilegedComponentsForScope(aWindow);
   } catch (e) {}
   return Cu.getComponentsForScope(aWindow);
 }
 
 function isWrappable(x) {
   if (typeof x === "object")
     return x !== null;
   return typeof x === "function";
-};
+}
 
 function isWrapper(x) {
   return isWrappable(x) && (typeof x.SpecialPowers_wrappedObject !== "undefined");
-};
+}
 
 function unwrapIfWrapped(x) {
   return isWrapper(x) ? unwrapPrivileged(x) : x;
-};
+}
 
 function wrapIfUnwrapped(x) {
   return isWrapper(x) ? x : wrapPrivileged(x);
 }
 
 function isObjectOrArray(obj) {
   if (Object(obj) !== obj)
     return false;
-  let arrayClasses = ['Object', 'Array', 'Int8Array', 'Uint8Array',
-                      'Int16Array', 'Uint16Array', 'Int32Array',
-                      'Uint32Array', 'Float32Array', 'Float64Array',
-                      'Uint8ClampedArray'];
+  let arrayClasses = ["Object", "Array", "Int8Array", "Uint8Array",
+                      "Int16Array", "Uint16Array", "Int32Array",
+                      "Uint32Array", "Float32Array", "Float64Array",
+                      "Uint8ClampedArray"];
   let className = Cu.getClassName(obj, true);
   return arrayClasses.indexOf(className) != -1;
 }
 
 // In general, we want Xray wrappers for content DOM objects, because waiving
 // Xray gives us Xray waiver wrappers that clamp the principal when we cross
 // compartment boundaries. However, there are some exceptions where we want
 // to use a waiver:
@@ -115,19 +118,18 @@ function isObjectOrArray(obj) {
 //   security in general, but tends to break tests that try to pass object
 //   literals into SpecialPowers. So we waive [[Object]] and [[Array]]
 //   instances before inspecting properties.
 //
 // * When we don't have meaningful Xray semantics, we create an Opaque
 //   XrayWrapper for security reasons. For test code, we generally want to see
 //   through that sort of thing.
 function waiveXraysIfAppropriate(obj, propName) {
-  if (propName == 'toString' || isObjectOrArray(obj) ||
-      /Opaque/.test(Object.prototype.toString.call(obj)))
-{
+  if (propName == "toString" || isObjectOrArray(obj) ||
+      /Opaque/.test(Object.prototype.toString.call(obj))) {
     return XPCNativeWrapper.unwrap(obj);
 }
   return obj;
 }
 
 // We can't call apply() directy on Xray-wrapped functions, so we have to be
 // clever.
 function doApply(fun, invocant, args) {
@@ -158,17 +160,17 @@ function wrapPrivileged(obj) {
 
   let dummy;
   if (typeof obj === "function")
     dummy = function() {};
   else
     dummy = Object.create(null);
 
   return new Proxy(dummy, new SpecialPowersHandler(obj));
-};
+}
 
 function unwrapPrivileged(x) {
 
   // We don't wrap primitives, so sometimes we have a primitive where we'd
   // expect to have a wrapper. The proxy pretends to be the type that it's
   // emulating, so we can just as easily check isWrappable() on a proxy as
   // we can on an unwrapped object.
   if (!isWrappable(x))
@@ -176,17 +178,17 @@ function unwrapPrivileged(x) {
 
   // If we have a wrappable type, make sure it's wrapped.
   if (!isWrapper(x))
     throw "Trying to unwrap a non-wrapped object!";
 
   var obj = x.SpecialPowers_wrappedObject;
   // unwrapped.
   return obj;
-};
+}
 
 function SpecialPowersHandler(wrappedObject) {
   this.wrappedObject = wrappedObject;
 }
 
 SpecialPowersHandler.prototype = {
   construct(target, args) {
     // The arguments may or may not be wrappers. Unwrap them if necessary.
@@ -261,40 +263,40 @@ SpecialPowersHandler.prototype = {
 
     if (desc === undefined)
       return undefined;
 
     // Transitively maintain the wrapper membrane.
     function wrapIfExists(key) {
       if (key in desc)
         desc[key] = wrapIfUnwrapped(desc[key]);
-    };
+    }
 
-    wrapIfExists('value');
-    wrapIfExists('get');
-    wrapIfExists('set');
+    wrapIfExists("value");
+    wrapIfExists("get");
+    wrapIfExists("set");
 
     // A trapping proxy's properties must always be configurable, but sometimes
     // we come across non-configurable properties. Tell a white lie.
     desc.configurable = true;
 
     return desc;
   },
 
   ownKeys(target) {
     // Insert our special API. It's not enumerable, but ownKeys()
     // includes non-enumerable properties.
-    let props = ['SpecialPowers_wrappedObject'];
+    let props = ["SpecialPowers_wrappedObject"];
 
     // Do the normal thing.
     let flt = (a) => !props.includes(a);
     props = props.concat(Reflect.ownKeys(this.wrappedObject).filter(flt));
 
     // If we've got an Xray wrapper, include the expandos as well.
-    if ('wrappedJSObject' in this.wrappedObject) {
+    if ("wrappedJSObject" in this.wrappedObject) {
       props = props.concat(Reflect.ownKeys(this.wrappedObject.wrappedJSObject)
                            .filter(flt));
     }
 
     return props;
   },
 
   preventExtensions(target) {
@@ -306,17 +308,17 @@ SpecialPowersHandler.prototype = {
 // tidy, XPCOM-hiding way.  Messages that are nsIScriptError objects
 // have their properties exposed in detail.  It also auto-unregisters
 // itself when it receives a "sentinel" message.
 function SPConsoleListener(callback) {
   this.callback = callback;
 }
 
 SPConsoleListener.prototype = {
-  observe: function(msg) {
+  observe(msg) {
     let m = { message: msg.message,
               errorMessage: null,
               sourceName: null,
               sourceLine: null,
               lineNumber: null,
               columnNumber: null,
               category: null,
               windowID: null,
@@ -360,17 +362,17 @@ function wrapCallback(cb) {
     return cb.apply(this, args);
   }
 }
 
 function wrapCallbackObject(obj) {
   obj = Cu.waiveXrays(obj);
   var wrapper = {};
   for (var i in obj) {
-    if (typeof obj[i] == 'function')
+    if (typeof obj[i] == "function")
       wrapper[i] = wrapCallback(obj[i]);
     else
       wrapper[i] = obj[i];
   }
   return wrapper;
 }
 
 function setWrapped(obj, prop, val) {
@@ -408,49 +410,49 @@ SpecialPowersAPI.prototype = {
    *    That is to say, the wrapper uses Xray delegation.
    *
    *  - The wrapper sometimes guesses certain ES5 attributes for returned
    *    properties. This is explained in a comment in the wrapper code above,
    *    and shouldn't be a problem.
    */
   wrap: wrapIfUnwrapped,
   unwrap: unwrapIfWrapped,
-  isWrapper: isWrapper,
+  isWrapper,
 
   /*
    * When content needs to pass a callback or a callback object to an API
    * accessed over SpecialPowers, that API may sometimes receive arguments for
    * whom it is forbidden to create a wrapper in content scopes. As such, we
    * need a layer to wrap the values in SpecialPowers wrappers before they ever
    * reach content.
    */
-  wrapCallback: wrapCallback,
-  wrapCallbackObject: wrapCallbackObject,
+  wrapCallback,
+  wrapCallbackObject,
 
   /*
    * Used for assigning a property to a SpecialPowers wrapper, without unwrapping
    * the value that is assigned.
    */
-  setWrapped: setWrapped,
+  setWrapped,
 
   /*
    * Create blank privileged objects to use as out-params for privileged functions.
    */
-  createBlankObject: function () {
-    return new Object;
+  createBlankObject() {
+    return {};
   },
 
   /*
    * Because SpecialPowers wrappers don't preserve identity, comparing with ==
    * can be hazardous. Sometimes we can just unwrap to compare, but sometimes
    * wrapping the underlying object into a content scope is forbidden. This
    * function strips any wrappers if they exist and compare the underlying
    * values.
    */
-  compare: function(a, b) {
+  compare(a, b) {
     return unwrapIfWrapped(a) === unwrapIfWrapped(b);
   },
 
   get MockFilePicker() {
     return MockFilePicker;
   },
 
   get MockColorPicker() {
@@ -460,33 +462,33 @@ SpecialPowersAPI.prototype = {
   get MockPermissionPrompt() {
     return MockPermissionPrompt;
   },
 
   /*
    * Load a privileged script that runs same-process. This is different from
    * |loadChromeScript|, which will run in the parent process in e10s mode.
    */
-  loadPrivilegedScript: function (aFunction) {
+  loadPrivilegedScript(aFunction) {
     var str = "(" + aFunction.toString() + ")();";
     var systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
     var sb = Cu.Sandbox(systemPrincipal);
     var window = this.window.get();
     var mc = new window.MessageChannel();
     sb.port = mc.port1;
     try {
       sb.eval(str);
     } catch (e) {
       throw wrapIfUnwrapped(e);
     }
 
     return mc.port2;
   },
 
-  loadChromeScript: function (urlOrFunction) {
+  loadChromeScript(urlOrFunction) {
     // Create a unique id for this chrome script
     let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"]
                           .getService(Ci.nsIUUIDGenerator);
     let id = uuidGenerator.generateUUID().toString();
 
     // Tells chrome code to evaluate this chrome script
     let scriptArgs = { id };
     if (typeof(urlOrFunction) == "function") {
@@ -500,17 +502,17 @@ SpecialPowersAPI.prototype = {
     this._sendSyncMessage("SPLoadChromeScript",
                           scriptArgs);
 
     // Returns a MessageManager like API in order to be
     // able to communicate with this chrome script
     let listeners = [];
     let chromeScript = {
       addMessageListener: (name, listener) => {
-        listeners.push({ name: name, listener: listener });
+        listeners.push({ name, listener });
       },
 
       promiseOneMessage: name => new Promise(resolve => {
         chromeScript.addMessageListener(name, function listener(message) {
           chromeScript.removeMessageListener(name, listener);
           resolve(message);
         });
       }),
@@ -518,17 +520,17 @@ SpecialPowersAPI.prototype = {
       removeMessageListener: (name, listener) => {
         listeners = listeners.filter(
           o => (o.name != name || o.listener != listener)
         );
       },
 
       sendAsyncMessage: (name, message) => {
         this._sendSyncMessage("SPChromeScriptMessage",
-                              { id: id, name: name, message: message });
+                              { id, name, message });
       },
 
       sendSyncMessage: (name, message) => {
         return this._sendSyncMessage("SPChromeScriptMessage",
                                      { id, name, message });
       },
 
       destroy: () => {
@@ -596,22 +598,22 @@ SpecialPowersAPI.prototype = {
         // When we are running only a single mochitest, there is no test runner
         dump(msg + "\n");
       }
     };
 
     return this.wrap(chromeScript);
   },
 
-  importInMainProcess: function (importString) {
+  importInMainProcess(importString) {
     var message = this._sendSyncMessage("SPImportInMainProcess", importString)[0];
     if (message.hadError) {
       throw "SpecialPowers.importInMainProcess failed with error " + message.errorMessage;
     }
-    return;
+
   },
 
   get Services() {
     return wrapPrivileged(Services);
   },
 
   /*
    * In general, any Components object created for unprivileged scopes is
@@ -626,104 +628,106 @@ SpecialPowersAPI.prototype = {
    * the same way (in particular, SpecialPowers.Cc[foo].createInstance() will
    * create an instance in the SpecialPowers scope), but SpecialPowers wrapping
    * is already a YMMV / Whatever-It-Takes-To-Get-TBPL-Green sort of thing.
    *
    * It probably wouldn't be too much work to just make SpecialPowers.Components
    * unconditionally point to the Components object in the SpecialPowers scope.
    * Try will tell what needs to be fixed up.
    */
-  getFullComponents: function() {
-    if (this.Components && typeof this.Components.classes == 'object') {
+  getFullComponents() {
+    if (this.Components && typeof this.Components.classes == "object") {
       return this.Components;
     }
     return Components;
   },
 
   /*
    * Convenient shortcuts to the standard Components abbreviations. Note that
    * we don't SpecialPowers-wrap Components.interfaces, because it's available
    * to untrusted content, and wrapping it confuses QI and identity checks.
    */
   get Cc() { return wrapPrivileged(this.getFullComponents().classes); },
-  get Ci() { return this.Components ? this.Components.interfaces
-                                    : Components.interfaces; },
+  get Ci() {
+ return this.Components ? this.Components.interfaces
+                                    : Components.interfaces;
+},
   get Cu() { return wrapPrivileged(this.getFullComponents().utils); },
   get Cr() { return wrapPrivileged(this.Components.results); },
 
   /*
    * SpecialPowers.getRawComponents() allows content to get a reference to a
    * naked (and, in certain automation configurations, privileged) Components
    * object for its scope.
    *
    * SpecialPowers.getRawComponents(window) is defined as the global property
    * window.SpecialPowers.Components for convenience.
    */
-  getRawComponents: getRawComponents,
+  getRawComponents,
 
-  getDOMWindowUtils: function(aWindow) {
+  getDOMWindowUtils(aWindow) {
     if (aWindow == this.window.get() && this.DOMWindowUtils != null)
       return this.DOMWindowUtils;
 
     return bindDOMWindowUtils(aWindow);
   },
 
-  removeExpectedCrashDumpFiles: function(aExpectingProcessCrash) {
+  removeExpectedCrashDumpFiles(aExpectingProcessCrash) {
     var success = true;
     if (aExpectingProcessCrash) {
       var message = {
         op: "delete-crash-dump-files",
         filenames: this._encounteredCrashDumpFiles
       };
       if (!this._sendSyncMessage("SPProcessCrashService", message)[0]) {
         success = false;
       }
     }
     this._encounteredCrashDumpFiles.length = 0;
     return success;
   },
 
-  findUnexpectedCrashDumpFiles: function() {
+  findUnexpectedCrashDumpFiles() {
     var self = this;
     var message = {
       op: "find-crash-dump-files",
       crashDumpFilesToIgnore: this._unexpectedCrashDumpFiles
     };
     var crashDumpFiles = this._sendSyncMessage("SPProcessCrashService", message)[0];
     crashDumpFiles.forEach(function(aFilename) {
       self._unexpectedCrashDumpFiles[aFilename] = true;
     });
     return crashDumpFiles;
   },
 
-  removePendingCrashDumpFiles: function() {
+  removePendingCrashDumpFiles() {
     var message = {
       op: "delete-pending-crash-dump-files"
     };
     var removed = this._sendSyncMessage("SPProcessCrashService", message)[0];
     return removed;
   },
 
-  _setTimeout: function(callback) {
+  _setTimeout(callback) {
     // for mochitest-browser
-    if (typeof window != 'undefined')
+    if (typeof window != "undefined")
       setTimeout(callback, 0);
     // for mochitest-plain
     else
       content.window.setTimeout(callback, 0);
   },
 
-  _delayCallbackTwice: function(callback) {
+  _delayCallbackTwice(callback) {
      function delayedCallback() {
        function delayAgain(aCallback) {
          // Using this._setTimeout doesn't work here
          // It causes failures in mochtests that use
          // multiple pushPrefEnv calls
          // For chrome/browser-chrome mochitests
-         if (typeof window != 'undefined')
+         if (typeof window != "undefined")
            setTimeout(aCallback, 0);
          // For mochitest-plain
          else
            content.window.setTimeout(aCallback, 0);
        }
        delayAgain(delayAgain(callback));
      }
      return delayedCallback;
@@ -733,17 +737,17 @@ SpecialPowersAPI.prototype = {
      we will revert the permission back to the original.
 
      inPermissions is an array of objects where each object has a type, action, context, ex:
      [{'type': 'SystemXHR', 'allow': 1, 'context': document},
       {'type': 'SystemXHR', 'allow': Ci.nsIPermissionManager.PROMPT_ACTION, 'context': document}]
 
      Allow can be a boolean value of true/false or ALLOW_ACTION/DENY_ACTION/PROMPT_ACTION/UNKNOWN_ACTION
   */
-  pushPermissions: function(inPermissions, callback) {
+  pushPermissions(inPermissions, callback) {
     inPermissions = Cu.waiveXrays(inPermissions);
     var pendingPermissions = [];
     var cleanupPermissions = [];
 
     for (var p in inPermissions) {
         var permission = inPermissions[p];
         var originalValue = Ci.nsIPermissionManager.UNKNOWN_ACTION;
         var context = Cu.unwaiveXrays(permission.context); // Sometimes |context| is a DOM object on which we expect
@@ -763,50 +767,50 @@ SpecialPowersAPI.prototype = {
         }
 
         let principal = this._getPrincipalFromArg(context);
         if (principal.isSystemPrincipal) {
           continue;
         }
 
         let perm;
-        if (typeof permission.allow !== 'boolean') {
+        if (typeof permission.allow !== "boolean") {
           perm = permission.allow;
         } else {
           perm = permission.allow ? Ci.nsIPermissionManager.ALLOW_ACTION
                              : Ci.nsIPermissionManager.DENY_ACTION;
         }
 
         if (permission.remove == true)
           perm = Ci.nsIPermissionManager.UNKNOWN_ACTION;
 
         if (originalValue == perm) {
           continue;
         }
 
-        var todo = {'op': 'add',
-                    'type': permission.type,
-                    'permission': perm,
-                    'value': perm,
-                    'principal': principal,
-                    'expireType': (typeof permission.expireType === "number") ?
+        var todo = {"op": "add",
+                    "type": permission.type,
+                    "permission": perm,
+                    "value": perm,
+                    "principal": principal,
+                    "expireType": (typeof permission.expireType === "number") ?
                       permission.expireType : 0, // default: EXPIRE_NEVER
-                    'expireTime': (typeof permission.expireTime === "number") ?
+                    "expireTime": (typeof permission.expireTime === "number") ?
                       permission.expireTime : 0};
 
         var cleanupTodo = Object.assign({}, todo);
 
         if (permission.remove == true)
-          todo.op = 'remove';
+          todo.op = "remove";
 
         pendingPermissions.push(todo);
 
         /* Push original permissions value or clear into cleanup array */
         if (originalValue == Ci.nsIPermissionManager.UNKNOWN_ACTION) {
-          cleanupTodo.op = 'remove';
+          cleanupTodo.op = "remove";
         } else {
           cleanupTodo.value = originalValue;
           cleanupTodo.permission = originalValue;
         }
         cleanupPermissions.push(cleanupTodo);
     }
 
     if (pendingPermissions.length > 0) {
@@ -853,85 +857,83 @@ SpecialPowersAPI.prototype = {
    *
    * To get the expected data, you should modify
    * SpecialPowersObserver.prototype._registerObservers.observe. Or the message
    * you received from messageManager will only contain 'aData' from Service.obs.
    *
    * NOTICE: there is no implementation of _addMessageListener in
    * ChromePowers.js
    */
-  registerObservers: function(topic) {
+  registerObservers(topic) {
     var msg = {
-      'op': 'add',
-      'observerTopic': topic,
+      "op": "add",
+      "observerTopic": topic,
     };
     this._sendSyncMessage("SPObserverService", msg);
   },
 
-  permChangedProxy: function(aMessage) {
+  permChangedProxy(aMessage) {
     let permission = aMessage.json.permission;
     let aData = aMessage.json.aData;
     this._permissionObserver.observe(permission, aData);
   },
 
   permissionObserverProxy: {
     // 'this' in permChangedObserverProxy is the permChangedObserverProxy
     // object itself. The '_specialPowersAPI' will be set to the 'SpecialPowersAPI'
     // object to call the member function in SpecialPowersAPI.
     _specialPowersAPI: null,
-    observe: function (aSubject, aTopic, aData)
-    {
+    observe(aSubject, aTopic, aData) {
       if (aTopic == "perm-changed") {
         var permission = aSubject.QueryInterface(Ci.nsIPermission);
         this._specialPowersAPI._permissionObserver.observe(permission, aData);
       }
     }
   },
 
-  popPermissions: function(callback) {
+  popPermissions(callback) {
     if (this._permissionsUndoStack.length > 0) {
       // See pushPermissions comment regarding delay.
       let cb = callback ? this._delayCallbackTwice(callback) : null;
       /* Each pop from the stack will yield an object {op/type/permission/value/url/appid/isInIsolatedMozBrowserElement} or null */
       this._pendingPermissions.push([this._permissionsUndoStack.pop(), cb]);
       this._applyPermissions();
     } else {
       if (this._observingPermissions) {
         this._observingPermissions = false;
         this._removeMessageListener("specialpowers-perm-changed", this.permChangedProxy.bind(this));
       }
       this._setTimeout(callback);
     }
   },
 
-  flushPermissions: function(callback) {
+  flushPermissions(callback) {
     while (this._permissionsUndoStack.length > 1)
       this.popPermissions(null);
 
     this.popPermissions(callback);
   },
 
 
-  setTestPluginEnabledState: function(newEnabledState, pluginName) {
+  setTestPluginEnabledState(newEnabledState, pluginName) {
     return this._sendSyncMessage("SPSetTestPluginEnabledState",
-                                 { newEnabledState: newEnabledState, pluginName: pluginName })[0];
+                                 { newEnabledState, pluginName })[0];
   },
 
 
   _permissionObserver: {
     _self: null,
     _lastPermission: {},
     _callBack: null,
     _nextCallback: null,
     _obsDataMap: {
-      'deleted':'remove',
-      'added':'add'
+      "deleted": "remove",
+      "added": "add"
     },
-    observe: function (permission, aData)
-    {
+    observe(permission, aData) {
       if (this._self._applyingPermissions) {
         if (permission.type == this._lastPermission.type) {
           this._self._setTimeout(this._callback);
           this._self._setTimeout(this._nextCallback);
           this._callback = null;
           this._nextCallback = null;
         }
       } else {
@@ -940,17 +942,17 @@ SpecialPowersAPI.prototype = {
           var undos = this._self._permissionsUndoStack[i];
           for (var j = 0; j < undos.length; j++) {
             var undo = undos[j];
             if (undo.op == this._obsDataMap[aData] &&
                 undo.principal.originAttributes.appId == permission.principal.originAttributes.appId &&
                 undo.type == permission.type) {
               // Remove this undo item if it has been done by others(not
               // specialpowers itself.)
-              undos.splice(j,1);
+              undos.splice(j, 1);
               found = true;
               break;
             }
           }
           if (!undos.length) {
             // Remove the empty row in permissionsUndoStack
             this._self._permissionsUndoStack.splice(i, 1);
           }
@@ -958,41 +960,41 @@ SpecialPowersAPI.prototype = {
       }
     }
   },
 
   /*
     Iterate through one atomic set of permissions actions and perform allow/deny as appropriate.
     All actions performed must modify the relevant permission.
   */
-  _applyPermissions: function() {
+  _applyPermissions() {
     if (this._applyingPermissions || this._pendingPermissions.length <= 0) {
       return;
     }
 
     /* Set lock and get prefs from the _pendingPrefs queue */
     this._applyingPermissions = true;
     var transaction = this._pendingPermissions.shift();
     var pendingActions = transaction[0];
     var callback = transaction[1];
-    var lastPermission = pendingActions[pendingActions.length-1];
+    var lastPermission = pendingActions[pendingActions.length - 1];
 
     var self = this;
     this._permissionObserver._self = self;
     this._permissionObserver._lastPermission = lastPermission;
     this._permissionObserver._callback = callback;
-    this._permissionObserver._nextCallback = function () {
+    this._permissionObserver._nextCallback = function() {
         self._applyingPermissions = false;
         // Now apply any permissions that may have been queued while we were applying
         self._applyPermissions();
     }
 
     for (var idx in pendingActions) {
       var perm = pendingActions[idx];
-      this._sendSyncMessage('SPPermissionManager', perm)[0];
+      this._sendSyncMessage("SPPermissionManager", perm)[0];
     }
   },
 
   /**
    * Helper to resolve a promise by calling the resolve function and call an
    * optional callback.
    */
   _resolveAndCallOptionalCallback(resolveFn, callback = null) {
@@ -1027,17 +1029,17 @@ SpecialPowersAPI.prototype = {
    * the behavior of this method is undefined.
    *
    * (Implementation note: _prefEnvUndoStack is a stack of values to revert to,
    * not values which have been set!)
    *
    * TODO: complex values for original cleanup?
    *
    */
-  pushPrefEnv: function(inPrefs, callback = null) {
+  pushPrefEnv(inPrefs, callback = null) {
     var prefs = Services.prefs;
 
     var pref_string = [];
     pref_string[prefs.PREF_INT] = "INT";
     pref_string[prefs.PREF_BOOL] = "BOOL";
     pref_string[prefs.PREF_STRING] = "CHAR";
 
     var pendingActions = [];
@@ -1057,20 +1059,20 @@ SpecialPowersAPI.prototype = {
           prefIid = aPref[2];
         } else if (aPref.length == 2) {
           prefValue = aPref[1];
         }
 
         /* If pref is not found or invalid it doesn't exist. */
         if (prefs.getPrefType(prefName) != prefs.PREF_INVALID) {
           prefType = pref_string[prefs.getPrefType(prefName)];
-          if ((prefs.prefHasUserValue(prefName) && action == 'clear') ||
-              (action == 'set'))
+          if ((prefs.prefHasUserValue(prefName) && action == "clear") ||
+              (action == "set"))
             originalValue = this._getPref(prefName, prefType);
-        } else if (action == 'set') {
+        } else if (action == "set") {
           /* prefName doesn't exist, so 'clear' is pointless */
           if (aPref.length == 3) {
             prefType = "COMPLEX";
           } else if (aPref.length == 2) {
             if (typeof(prefValue) == "boolean")
               prefType = "BOOL";
             else if (typeof(prefValue) == "number")
               prefType = "INT";
@@ -1082,24 +1084,24 @@ SpecialPowersAPI.prototype = {
         /* PREF_INVALID: A non existing pref which we are clearing or invalid values for a set */
         if (prefType == prefs.PREF_INVALID)
           continue;
 
         /* We are not going to set a pref if the value is the same */
         if (originalValue == prefValue)
           continue;
 
-        pendingActions.push({'action': action, 'type': prefType, 'name': prefName, 'value': prefValue, 'Iid': prefIid});
+        pendingActions.push({"action": action, "type": prefType, "name": prefName, "value": prefValue, "Iid": prefIid});
 
         /* Push original preference value or clear into cleanup array */
-        var cleanupTodo = {'action': action, 'type': prefType, 'name': prefName, 'value': originalValue, 'Iid': prefIid};
+        var cleanupTodo = {"action": action, "type": prefType, "name": prefName, "value": originalValue, "Iid": prefIid};
         if (originalValue == null) {
-          cleanupTodo.action = 'clear';
+          cleanupTodo.action = "clear";
         } else {
-          cleanupTodo.action = 'set';
+          cleanupTodo.action = "set";
         }
         cleanupActions.push(cleanupTodo);
       }
     }
 
     return new Promise(resolve => {
       let done = this._resolveAndCallOptionalCallback.bind(this, resolve, callback);
       if (pendingActions.length > 0) {
@@ -1115,336 +1117,336 @@ SpecialPowersAPI.prototype = {
                                  this._delayCallbackTwice(done)]);
         this._applyPrefs();
       } else {
         this._setTimeout(done);
       }
     });
   },
 
-  popPrefEnv: function(callback = null) {
+  popPrefEnv(callback = null) {
     return new Promise(resolve => {
       let done = this._resolveAndCallOptionalCallback.bind(this, resolve, callback);
       if (this._prefEnvUndoStack.length > 0) {
         // See pushPrefEnv comment regarding delay.
         let cb = this._delayCallbackTwice(done);
         /* Each pop will have a valid block of preferences */
         this._pendingPrefs.push([this._prefEnvUndoStack.pop(), cb]);
         this._applyPrefs();
       } else {
         this._setTimeout(done);
       }
     });
   },
 
-  flushPrefEnv: function(callback = null) {
+  flushPrefEnv(callback = null) {
     while (this._prefEnvUndoStack.length > 1)
       this.popPrefEnv(null);
 
     return new Promise(resolve => {
       let done = this._resolveAndCallOptionalCallback.bind(this, resolve, callback);
       this.popPrefEnv(done);
     });
   },
 
   /*
     Iterate through one atomic set of pref actions and perform sets/clears as appropriate.
     All actions performed must modify the relevant pref.
   */
-  _applyPrefs: function() {
+  _applyPrefs() {
     if (this._applyingPrefs || this._pendingPrefs.length <= 0) {
       return;
     }
 
     /* Set lock and get prefs from the _pendingPrefs queue */
     this._applyingPrefs = true;
     var transaction = this._pendingPrefs.shift();
     var pendingActions = transaction[0];
     var callback = transaction[1];
 
-    var lastPref = pendingActions[pendingActions.length-1];
+    var lastPref = pendingActions[pendingActions.length - 1];
 
     var pb = Services.prefs;
     var self = this;
     pb.addObserver(lastPref.name, function prefObs(subject, topic, data) {
       pb.removeObserver(lastPref.name, prefObs);
 
       self._setTimeout(callback);
-      self._setTimeout(function () {
+      self._setTimeout(function() {
         self._applyingPrefs = false;
         // Now apply any prefs that may have been queued while we were applying
         self._applyPrefs();
       });
     });
 
     for (var idx in pendingActions) {
       var pref = pendingActions[idx];
-      if (pref.action == 'set') {
+      if (pref.action == "set") {
         this._setPref(pref.name, pref.type, pref.value, pref.Iid);
-      } else if (pref.action == 'clear') {
+      } else if (pref.action == "clear") {
         this.clearUserPref(pref.name);
       }
     }
   },
 
   _proxiedObservers: {
     "specialpowers-http-notify-request": function(aMessage) {
       let uri = aMessage.json.uri;
       Services.obs.notifyObservers(null, "specialpowers-http-notify-request", uri);
     },
   },
 
-  _addObserverProxy: function(notification) {
+  _addObserverProxy(notification) {
     if (notification in this._proxiedObservers) {
       this._addMessageListener(notification, this._proxiedObservers[notification]);
     }
   },
-  _removeObserverProxy: function(notification) {
+  _removeObserverProxy(notification) {
     if (notification in this._proxiedObservers) {
       this._removeMessageListener(notification, this._proxiedObservers[notification]);
     }
   },
 
-  addObserver: function(obs, notification, weak) {
+  addObserver(obs, notification, weak) {
     this._addObserverProxy(notification);
     obs = Cu.waiveXrays(obs);
-    if (typeof obs == 'object' && obs.observe.name != 'SpecialPowersCallbackWrapper')
+    if (typeof obs == "object" && obs.observe.name != "SpecialPowersCallbackWrapper")
       obs.observe = wrapCallback(obs.observe);
     Services.obs.addObserver(obs, notification, weak);
   },
-  removeObserver: function(obs, notification) {
+  removeObserver(obs, notification) {
     this._removeObserverProxy(notification);
     Services.obs.removeObserver(Cu.waiveXrays(obs), notification);
   },
-  notifyObservers: function(subject, topic, data) {
+  notifyObservers(subject, topic, data) {
     Services.obs.notifyObservers(subject, topic, data);
   },
 
   /**
    * An async observer is useful if you're listening for a
    * notification that normally is only used by C++ code or chrome
    * code (so it runs in the SystemGroup), but we need to know about
    * it for a test (which runs as web content). If we used
    * addObserver, we would assert when trying to enter web content
    * from a runnabled labeled by the SystemGroup. An async observer
    * avoids this problem.
    */
   _asyncObservers: new WeakMap(),
-  addAsyncObserver: function(obs, notification, weak) {
+  addAsyncObserver(obs, notification, weak) {
     obs = Cu.waiveXrays(obs);
-    if (typeof obs == 'object' && obs.observe.name != 'SpecialPowersCallbackWrapper') {
+    if (typeof obs == "object" && obs.observe.name != "SpecialPowersCallbackWrapper") {
       obs.observe = wrapCallback(obs.observe);
     }
     let asyncObs = (...args) => {
       Services.tm.dispatchToMainThread(() => {
-        if (typeof obs == 'function') {
-          obs.call(undefined, ...args);
+        if (typeof obs == "function") {
+          obs(...args);
         } else {
           obs.observe.call(undefined, ...args);
         }
       });
     };
     this._asyncObservers.set(obs, asyncObs);
     Services.obs.addObserver(asyncObs, notification, weak);
   },
-  removeAsyncObserver: function(obs, notification) {
+  removeAsyncObserver(obs, notification) {
     let asyncObs = this._asyncObservers.get(Cu.waiveXrays(obs));
     Services.obs.removeObserver(asyncObs, notification);
   },
 
-  can_QI: function(obj) {
+  can_QI(obj) {
     return obj.QueryInterface !== undefined;
   },
-  do_QueryInterface: function(obj, iface) {
+  do_QueryInterface(obj, iface) {
     return obj.QueryInterface(Ci[iface]);
   },
 
-  call_Instanceof: function (obj1, obj2) {
-     obj1=unwrapIfWrapped(obj1);
-     obj2=unwrapIfWrapped(obj2);
+  call_Instanceof(obj1, obj2) {
+     obj1 = unwrapIfWrapped(obj1);
+     obj2 = unwrapIfWrapped(obj2);
      return obj1 instanceof obj2;
   },
 
   // Returns a privileged getter from an object. GetOwnPropertyDescriptor does
   // not work here because xray wrappers don't properly implement it.
   //
   // This terribleness is used by dom/base/test/test_object.html because
   // <object> and <embed> tags will spawn plugins if their prototype is touched,
   // so we need to get and cache the getter of |hasRunningPlugin| if we want to
   // call it without paradoxically spawning the plugin.
-  do_lookupGetter: function(obj, name) {
+  do_lookupGetter(obj, name) {
     return Object.prototype.__lookupGetter__.call(obj, name);
   },
 
   // Mimic the get*Pref API
-  getBoolPref: function(aPrefName) {
-    return (this._getPref(aPrefName, 'BOOL'));
+  getBoolPref(aPrefName) {
+    return (this._getPref(aPrefName, "BOOL"));
   },
-  getIntPref: function(aPrefName) {
-    return (this._getPref(aPrefName, 'INT'));
+  getIntPref(aPrefName) {
+    return (this._getPref(aPrefName, "INT"));
   },
-  getCharPref: function(aPrefName) {
-    return (this._getPref(aPrefName, 'CHAR'));
+  getCharPref(aPrefName) {
+    return (this._getPref(aPrefName, "CHAR"));
   },
-  getComplexValue: function(aPrefName, aIid) {
-    return (this._getPref(aPrefName, 'COMPLEX', aIid));
+  getComplexValue(aPrefName, aIid) {
+    return (this._getPref(aPrefName, "COMPLEX", aIid));
   },
 
   // Mimic the set*Pref API
-  setBoolPref: function(aPrefName, aValue) {
-    return (this._setPref(aPrefName, 'BOOL', aValue));
+  setBoolPref(aPrefName, aValue) {
+    return (this._setPref(aPrefName, "BOOL", aValue));
   },
-  setIntPref: function(aPrefName, aValue) {
-    return (this._setPref(aPrefName, 'INT', aValue));
+  setIntPref(aPrefName, aValue) {
+    return (this._setPref(aPrefName, "INT", aValue));
   },
-  setCharPref: function(aPrefName, aValue) {
-    return (this._setPref(aPrefName, 'CHAR', aValue));
+  setCharPref(aPrefName, aValue) {
+    return (this._setPref(aPrefName, "CHAR", aValue));
   },
-  setComplexValue: function(aPrefName, aIid, aValue) {
-    return (this._setPref(aPrefName, 'COMPLEX', aValue, aIid));
+  setComplexValue(aPrefName, aIid, aValue) {
+    return (this._setPref(aPrefName, "COMPLEX", aValue, aIid));
   },
 
   // Mimic the clearUserPref API
-  clearUserPref: function(aPrefName) {
-    var msg = {'op':'clear', 'prefName': aPrefName, 'prefType': ""};
-    this._sendSyncMessage('SPPrefService', msg);
+  clearUserPref(aPrefName) {
+    var msg = {"op": "clear", "prefName": aPrefName, "prefType": ""};
+    this._sendSyncMessage("SPPrefService", msg);
   },
 
   // Private pref functions to communicate to chrome
-  _getPref: function(aPrefName, aPrefType, aIid) {
+  _getPref(aPrefName, aPrefType, aIid) {
     var msg = {};
     if (aIid) {
       // Overloading prefValue to handle complex prefs
-      msg = {'op':'get', 'prefName': aPrefName, 'prefType':aPrefType, 'prefValue':[aIid]};
+      msg = {"op": "get", "prefName": aPrefName, "prefType": aPrefType, "prefValue": [aIid]};
     } else {
-      msg = {'op':'get', 'prefName': aPrefName,'prefType': aPrefType};
+      msg = {"op": "get", "prefName": aPrefName, "prefType": aPrefType};
     }
-    var val = this._sendSyncMessage('SPPrefService', msg);
+    var val = this._sendSyncMessage("SPPrefService", msg);
 
     if (val == null || val[0] == null)
       throw "Error getting pref '" + aPrefName + "'";
     return val[0];
   },
-  _setPref: function(aPrefName, aPrefType, aValue, aIid) {
+  _setPref(aPrefName, aPrefType, aValue, aIid) {
     var msg = {};
     if (aIid) {
-      msg = {'op':'set','prefName':aPrefName, 'prefType': aPrefType, 'prefValue': [aIid,aValue]};
+      msg = {"op": "set", "prefName": aPrefName, "prefType": aPrefType, "prefValue": [aIid, aValue]};
     } else {
-      msg = {'op':'set', 'prefName': aPrefName, 'prefType': aPrefType, 'prefValue': aValue};
+      msg = {"op": "set", "prefName": aPrefName, "prefType": aPrefType, "prefValue": aValue};
     }
-    return(this._sendSyncMessage('SPPrefService', msg)[0]);
+    return (this._sendSyncMessage("SPPrefService", msg)[0]);
   },
 
-  _getDocShell: function(window) {
+  _getDocShell(window) {
     return window.QueryInterface(Ci.nsIInterfaceRequestor)
                  .getInterface(Ci.nsIWebNavigation)
                  .QueryInterface(Ci.nsIDocShell);
   },
-  _getMUDV: function(window) {
+  _getMUDV(window) {
     return this._getDocShell(window).contentViewer;
   },
-  //XXX: these APIs really ought to be removed, they're not e10s-safe.
+  // XXX: these APIs really ought to be removed, they're not e10s-safe.
   // (also they're pretty Firefox-specific)
-  _getTopChromeWindow: function(window) {
+  _getTopChromeWindow(window) {
     return window.QueryInterface(Ci.nsIInterfaceRequestor)
                  .getInterface(Ci.nsIWebNavigation)
                  .QueryInterface(Ci.nsIDocShellTreeItem)
                  .rootTreeItem
                  .QueryInterface(Ci.nsIInterfaceRequestor)
                  .getInterface(Ci.nsIDOMWindow)
                  .QueryInterface(Ci.nsIDOMChromeWindow);
   },
-  _getAutoCompletePopup: function(window) {
+  _getAutoCompletePopup(window) {
     return this._getTopChromeWindow(window).document
                                            .getElementById("PopupAutoComplete");
   },
-  addAutoCompletePopupEventListener: function(window, eventname, listener) {
+  addAutoCompletePopupEventListener(window, eventname, listener) {
     this._getAutoCompletePopup(window).addEventListener(eventname,
                                                         listener);
   },
-  removeAutoCompletePopupEventListener: function(window, eventname, listener) {
+  removeAutoCompletePopupEventListener(window, eventname, listener) {
     this._getAutoCompletePopup(window).removeEventListener(eventname,
                                                            listener);
   },
   get formHistory() {
     let tmp = {};
     Cu.import("resource://gre/modules/FormHistory.jsm", tmp);
     return wrapPrivileged(tmp.FormHistory);
   },
-  getFormFillController: function(window) {
+  getFormFillController(window) {
     return Components.classes["@mozilla.org/satchel/form-fill-controller;1"]
                      .getService(Components.interfaces.nsIFormFillController);
   },
-  attachFormFillControllerTo: function(window) {
+  attachFormFillControllerTo(window) {
     this.getFormFillController()
         .attachToBrowser(this._getDocShell(window),
                          this._getAutoCompletePopup(window));
   },
-  detachFormFillControllerFrom: function(window) {
+  detachFormFillControllerFrom(window) {
     this.getFormFillController().detachFromBrowser(this._getDocShell(window));
   },
-  isBackButtonEnabled: function(window) {
+  isBackButtonEnabled(window) {
     return !this._getTopChromeWindow(window).document
                                       .getElementById("Browser:Back")
                                       .hasAttribute("disabled");
   },
-  //XXX end of problematic APIs
+  // XXX end of problematic APIs
 
-  addChromeEventListener: function(type, listener, capture, allowUntrusted) {
+  addChromeEventListener(type, listener, capture, allowUntrusted) {
     addEventListener(type, listener, capture, allowUntrusted);
   },
-  removeChromeEventListener: function(type, listener, capture) {
+  removeChromeEventListener(type, listener, capture) {
     removeEventListener(type, listener, capture);
   },
 
   // Note: each call to registerConsoleListener MUST be paired with a
   // call to postConsoleSentinel; when the callback receives the
   // sentinel it will unregister itself (_after_ calling the
   // callback).  SimpleTest.expectConsoleMessages does this for you.
   // If you register more than one console listener, a call to
   // postConsoleSentinel will zap all of them.
-  registerConsoleListener: function(callback) {
+  registerConsoleListener(callback) {
     let listener = new SPConsoleListener(callback);
     Services.console.registerListener(listener);
   },
-  postConsoleSentinel: function() {
+  postConsoleSentinel() {
     Services.console.logStringMessage("SENTINEL");
   },
-  resetConsole: function() {
+  resetConsole() {
     Services.console.reset();
   },
 
-  getFullZoom: function(window) {
+  getFullZoom(window) {
     return this._getMUDV(window).fullZoom;
   },
-  setFullZoom: function(window, zoom) {
+  setFullZoom(window, zoom) {
     this._getMUDV(window).fullZoom = zoom;
   },
-  getTextZoom: function(window) {
+  getTextZoom(window) {
     return this._getMUDV(window).textZoom;
   },
-  setTextZoom: function(window, zoom) {
+  setTextZoom(window, zoom) {
     this._getMUDV(window).textZoom = zoom;
   },
 
-  getOverrideDPPX: function(window) {
+  getOverrideDPPX(window) {
     return this._getMUDV(window).overrideDPPX;
   },
-  setOverrideDPPX: function(window, dppx) {
+  setOverrideDPPX(window, dppx) {
     this._getMUDV(window).overrideDPPX = dppx;
   },
 
-  emulateMedium: function(window, mediaType) {
+  emulateMedium(window, mediaType) {
     this._getMUDV(window).emulateMedium(mediaType);
   },
-  stopEmulatingMedium: function(window) {
+  stopEmulatingMedium(window) {
     this._getMUDV(window).stopEmulatingMedium();
   },
 
-  snapshotWindowWithOptions: function (win, rect, bgcolor, options) {
+  snapshotWindowWithOptions(win, rect, bgcolor, options) {
     var el = this.window.get().document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
     if (rect === undefined) {
       rect = { top: win.scrollY, left: win.scrollX,
                width: win.innerWidth, height: win.innerHeight };
     }
     if (bgcolor === undefined) {
       bgcolor = "rgb(255,255,255)";
     }
@@ -1463,72 +1465,72 @@ SpecialPowersAPI.prototype = {
 
     ctx.drawWindow(win,
                    rect.left, rect.top, rect.width, rect.height,
                    bgcolor,
                    flags);
     return el;
   },
 
-  snapshotWindow: function (win, withCaret, rect, bgcolor) {
+  snapshotWindow(win, withCaret, rect, bgcolor) {
     return this.snapshotWindowWithOptions(win, rect, bgcolor,
                                           { DRAWWINDOW_DRAW_CARET: withCaret });
   },
 
-  snapshotRect: function (win, rect, bgcolor) {
+  snapshotRect(win, rect, bgcolor) {
     return this.snapshotWindowWithOptions(win, rect, bgcolor);
   },
 
-  gc: function() {
+  gc() {
     this.DOMWindowUtils.garbageCollect();
   },
 
-  forceGC: function() {
+  forceGC() {
     Cu.forceGC();
   },
 
-  forceCC: function() {
+  forceCC() {
     Cu.forceCC();
   },
 
-  finishCC: function() {
+  finishCC() {
     Cu.finishCC();
   },
 
-  ccSlice: function(budget) {
+  ccSlice(budget) {
     Cu.ccSlice(budget);
   },
 
   // Due to various dependencies between JS objects and C++ objects, an ordinary
   // forceGC doesn't necessarily clear all unused objects, thus the GC and CC
   // needs to run several times and when no other JS is running.
   // The current number of iterations has been determined according to massive
   // cross platform testing.
-  exactGC: function(callback) {
+  exactGC(callback) {
     let count = 0;
 
     function genGCCallback(cb) {
       return function() {
         Cu.forceCC();
         if (++count < 2) {
           Cu.schedulePreciseGC(genGCCallback(cb));
         } else if (cb) {
           cb();
         }
       }
     }
 
     Cu.schedulePreciseGC(genGCCallback(callback));
   },
 
-  setGCZeal: function(zeal) {
+  setGCZeal(zeal) {
     Cu.setGCZeal(zeal);
   },
 
-  isMainProcess: function() {
+  isMainProcess() {
     try {
       return Cc["@mozilla.org/xre/app-info;1"].
                getService(Ci.nsIXULRuntime).
                processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
     } catch (e) { }
     return true;
   },
 
@@ -1544,17 +1546,17 @@ SpecialPowersAPI.prototype = {
 
     this._xpcomabi = xulRuntime.XPCOMABI;
     return this._xpcomabi;
   },
 
   // The optional aWin parameter allows the caller to specify a given window in
   // whose scope the runnable should be dispatched. If aFun throws, the
   // exception will be reported to aWin.
-  executeSoon: function(aFun, aWin) {
+  executeSoon(aFun, aWin) {
     // Create the runnable in the scope of aWin to avoid running into COWs.
     var runnable = {};
     if (aWin)
         runnable = Cu.createObjectIn(aWin);
     runnable.run = aFun;
     Cu.dispatch(runnable, aWin);
   },
 
@@ -1567,92 +1569,84 @@ SpecialPowersAPI.prototype = {
     var xulRuntime = Cc["@mozilla.org/xre/app-info;1"]
                         .getService(Components.interfaces.nsIXULAppInfo)
                         .QueryInterface(Components.interfaces.nsIXULRuntime);
 
     this._os = xulRuntime.OS;
     return this._os;
   },
 
-  get isB2G() {
-#ifdef MOZ_B2G
-    return true;
-#else
-    return false;
-#endif
-  },
-
-  addSystemEventListener: function(target, type, listener, useCapture) {
+  addSystemEventListener(target, type, listener, useCapture) {
     Cc["@mozilla.org/eventlistenerservice;1"].
       getService(Ci.nsIEventListenerService).
       addSystemEventListener(target, type, listener, useCapture);
   },
-  removeSystemEventListener: function(target, type, listener, useCapture) {
+  removeSystemEventListener(target, type, listener, useCapture) {
     Cc["@mozilla.org/eventlistenerservice;1"].
       getService(Ci.nsIEventListenerService).
       removeSystemEventListener(target, type, listener, useCapture);
   },
 
   // helper method to check if the event is consumed by either default group's
   // event listener or system group's event listener.
-  defaultPreventedInAnyGroup: function(event) {
+  defaultPreventedInAnyGroup(event) {
     // FYI: Event.defaultPrevented returns false in content context if the
     //      event is consumed only by system group's event listeners.
     return event.defaultPrevented;
   },
 
-  getDOMRequestService: function() {
+  getDOMRequestService() {
     var serv = Services.DOMRequest;
     var res = {};
     var props = ["createRequest", "createCursor", "fireError", "fireSuccess",
                  "fireDone", "fireDetailedError"];
     for (var i in props) {
       let prop = props[i];
       res[prop] = function() { return serv[prop].apply(serv, arguments) };
     }
     return res;
   },
 
-  setLogFile: function(path) {
+  setLogFile(path) {
     this._mfl = new MozillaFileLogger(path);
   },
 
-  log: function(data) {
+  log(data) {
     this._mfl.log(data);
   },
 
-  closeLogFile: function() {
+  closeLogFile() {
     this._mfl.close();
   },
 
-  addCategoryEntry: function(category, entry, value, persists, replace) {
+  addCategoryEntry(category, entry, value, persists, replace) {
     Components.classes["@mozilla.org/categorymanager;1"].
       getService(Components.interfaces.nsICategoryManager).
       addCategoryEntry(category, entry, value, persists, replace);
   },
 
-  deleteCategoryEntry: function(category, entry, persists) {
+  deleteCategoryEntry(category, entry, persists) {
     Components.classes["@mozilla.org/categorymanager;1"].
       getService(Components.interfaces.nsICategoryManager).
       deleteCategoryEntry(category, entry, persists);
   },
-  openDialog: function(win, args) {
+  openDialog(win, args) {
     return win.openDialog.apply(win, args);
   },
   // This is a blocking call which creates and spins a native event loop
-  spinEventLoop: function(win) {
+  spinEventLoop(win) {
     // simply do a sync XHR back to our windows location.
     var syncXHR = new win.XMLHttpRequest();
-    syncXHR.open('GET', win.location, false);
+    syncXHR.open("GET", win.location, false);
     syncXHR.send();
   },
 
   // :jdm gets credit for this.  ex: getPrivilegedProps(window, 'location.href');
-  getPrivilegedProps: function(obj, props) {
-    var parts = props.split('.');
+  getPrivilegedProps(obj, props) {
+    var parts = props.split(".");
     for (var i = 0; i < parts.length; i++) {
       var p = parts[i];
       if (obj[p]) {
         obj = obj[p];
       } else {
         return null;
       }
     }
@@ -1664,31 +1658,31 @@ SpecialPowersAPI.prototype = {
       return this._fm;
 
     this._fm = Components.classes["@mozilla.org/focus-manager;1"].
                         getService(Components.interfaces.nsIFocusManager);
 
     return this._fm;
   },
 
-  getFocusedElementForWindow: function(targetWindow, aDeep) {
+  getFocusedElementForWindow(targetWindow, aDeep) {
     var outParam = {};
     this.focusManager.getFocusedElementForWindow(targetWindow, aDeep, outParam);
     return outParam.value;
   },
 
-  activeWindow: function() {
+  activeWindow() {
     return this.focusManager.activeWindow;
   },
 
-  focusedWindow: function() {
+  focusedWindow() {
     return this.focusManager.focusedWindow;
   },
 
-  focus: function(aWindow) {
+  focus(aWindow) {
     // This is called inside TestRunner._makeIframe without aWindow, because of assertions in oop mochitests
     // With aWindow, it is called in SimpleTest.waitForFocus to allow popup window opener focus switching
     if (aWindow)
       aWindow.focus();
     var mm = global;
     if (aWindow) {
       try {
         mm = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
@@ -1698,17 +1692,17 @@ SpecialPowersAPI.prototype = {
       } catch (ex) {
         /* Ignore exceptions for e.g. XUL chrome windows from mochitest-chrome
          * which won't have a message manager */
       }
     }
     mm.sendAsyncMessage("SpecialPowers.Focus", {});
   },
 
-  getClipboardData: function(flavor, whichClipboard) {
+  getClipboardData(flavor, whichClipboard) {
     if (this._cb == null)
       this._cb = Components.classes["@mozilla.org/widget/clipboard;1"].
                             getService(Components.interfaces.nsIClipboard);
     if (whichClipboard === undefined)
       whichClipboard = this._cb.kGlobalClipboard;
 
     var xferable = Components.classes["@mozilla.org/widget/transferable;1"].
                    createInstance(Components.interfaces.nsITransferable);
@@ -1725,93 +1719,93 @@ SpecialPowersAPI.prototype = {
     } catch (e) {}
     data = data.value || null;
     if (data == null)
       return "";
 
     return data.QueryInterface(Components.interfaces.nsISupportsString).data;
   },
 
-  clipboardCopyString: function(str) {
+  clipboardCopyString(str) {
     Cc["@mozilla.org/widget/clipboardhelper;1"].
       getService(Ci.nsIClipboardHelper).
       copyString(str);
   },
 
-  supportsSelectionClipboard: function() {
+  supportsSelectionClipboard() {
     if (this._cb == null) {
       this._cb = Components.classes["@mozilla.org/widget/clipboard;1"].
                             getService(Components.interfaces.nsIClipboard);
     }
     return this._cb.supportsSelectionClipboard();
   },
 
-  swapFactoryRegistration: function(cid, contractID, newFactory, oldFactory) {
+  swapFactoryRegistration(cid, contractID, newFactory, oldFactory) {
     newFactory = Cu.waiveXrays(newFactory);
     oldFactory = Cu.waiveXrays(oldFactory);
 
     var componentRegistrar = Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
 
     var unregisterFactory = newFactory;
     var registerFactory = oldFactory;
 
     if (cid == null) {
       if (contractID != null) {
         cid = componentRegistrar.contractIDToCID(contractID);
         oldFactory = Components.manager.getClassObject(Components.classes[contractID],
                                                             Components.interfaces.nsIFactory);
       } else {
-        return {'error': "trying to register a new contract ID: Missing contractID"};
+        return {"error": "trying to register a new contract ID: Missing contractID"};
       }
 
       unregisterFactory = oldFactory;
       registerFactory = newFactory;
     }
     componentRegistrar.unregisterFactory(cid,
                                          unregisterFactory);
 
     // Restore the original factory.
     componentRegistrar.registerFactory(cid,
                                        "",
                                        contractID,
                                        registerFactory);
-    return {'cid':cid, 'originalFactory':oldFactory};
+    return {"cid": cid, "originalFactory": oldFactory};
   },
 
-  _getElement: function(aWindow, id) {
+  _getElement(aWindow, id) {
     return ((typeof(id) == "string") ?
         aWindow.document.getElementById(id) : id);
   },
 
-  dispatchEvent: function(aWindow, target, event) {
+  dispatchEvent(aWindow, target, event) {
     var el = this._getElement(aWindow, target);
     return el.dispatchEvent(event);
   },
 
   get isDebugBuild() {
     delete SpecialPowersAPI.prototype.isDebugBuild;
 
     var debug = Cc["@mozilla.org/xpcom/debug;1"].getService(Ci.nsIDebug2);
     return SpecialPowersAPI.prototype.isDebugBuild = debug.isDebugBuild;
   },
-  assertionCount: function() {
-    var debugsvc = Cc['@mozilla.org/xpcom/debug;1'].getService(Ci.nsIDebug2);
+  assertionCount() {
+    var debugsvc = Cc["@mozilla.org/xpcom/debug;1"].getService(Ci.nsIDebug2);
     return debugsvc.assertionCount;
   },
 
   /**
    * Get the message manager associated with an <iframe mozbrowser>.
    */
-  getBrowserFrameMessageManager: function(aFrameElement) {
+  getBrowserFrameMessageManager(aFrameElement) {
     return this.wrap(aFrameElement.QueryInterface(Ci.nsIFrameLoaderOwner)
                                   .frameLoader
                                   .messageManager);
   },
 
-  _getPrincipalFromArg: function(arg) {
+  _getPrincipalFromArg(arg) {
     let principal;
     let secMan = Services.scriptSecurityManager;
 
     if (typeof(arg) == "string") {
       // It's an URL.
       let uri = Services.io.newURI(arg);
       principal = secMan.createCodebasePrincipal(uri, {});
     } else if (arg.nodePrincipal) {
@@ -1822,123 +1816,123 @@ SpecialPowersAPI.prototype = {
       let uri = Services.io.newURI(arg.url);
       let attrs = arg.originAttributes || {};
       principal = secMan.createCodebasePrincipal(uri, attrs);
     }
 
     return principal;
   },
 
-  addPermission: function(type, allow, arg, expireType, expireTime) {
+  addPermission(type, allow, arg, expireType, expireTime) {
     let principal = this._getPrincipalFromArg(arg);
     if (principal.isSystemPrincipal) {
       return; // nothing to do
     }
 
     let permission;
-    if (typeof allow !== 'boolean') {
+    if (typeof allow !== "boolean") {
       permission = allow;
     } else {
       permission = allow ? Ci.nsIPermissionManager.ALLOW_ACTION
                          : Ci.nsIPermissionManager.DENY_ACTION;
     }
 
     var msg = {
-      'op': 'add',
-      'type': type,
-      'permission': permission,
-      'principal': principal,
-      'expireType': (typeof expireType === "number") ? expireType : 0,
-      'expireTime': (typeof expireTime === "number") ? expireTime : 0
+      "op": "add",
+      "type": type,
+      "permission": permission,
+      "principal": principal,
+      "expireType": (typeof expireType === "number") ? expireType : 0,
+      "expireTime": (typeof expireTime === "number") ? expireTime : 0
     };
 
-    this._sendSyncMessage('SPPermissionManager', msg);
+    this._sendSyncMessage("SPPermissionManager", msg);
   },
 
-  removePermission: function(type, arg) {
+  removePermission(type, arg) {
     let principal = this._getPrincipalFromArg(arg);
     if (principal.isSystemPrincipal) {
       return; // nothing to do
     }
 
     var msg = {
-      'op': 'remove',
-      'type': type,
-      'principal': principal
+      "op": "remove",
+      "type": type,
+      "principal": principal
     };
 
-    this._sendSyncMessage('SPPermissionManager', msg);
+    this._sendSyncMessage("SPPermissionManager", msg);
   },
 
-  hasPermission: function (type, arg) {
+  hasPermission(type, arg) {
     let principal = this._getPrincipalFromArg(arg);
     if (principal.isSystemPrincipal) {
       return true; // system principals have all permissions
     }
 
     var msg = {
-      'op': 'has',
-      'type': type,
-      'principal': principal
+      "op": "has",
+      "type": type,
+      "principal": principal
     };
 
-    return this._sendSyncMessage('SPPermissionManager', msg)[0];
+    return this._sendSyncMessage("SPPermissionManager", msg)[0];
   },
 
-  testPermission: function (type, value, arg) {
+  testPermission(type, value, arg) {
     let principal = this._getPrincipalFromArg(arg);
     if (principal.isSystemPrincipal) {
       return true; // system principals have all permissions
     }
 
     var msg = {
-      'op': 'test',
-      'type': type,
-      'value': value,
-      'principal': principal
+      "op": "test",
+      "type": type,
+      "value": value,
+      "principal": principal
     };
-    return this._sendSyncMessage('SPPermissionManager', msg)[0];
+    return this._sendSyncMessage("SPPermissionManager", msg)[0];
   },
 
-  isContentWindowPrivate: function(win) {
+  isContentWindowPrivate(win) {
     return PrivateBrowsingUtils.isContentWindowPrivate(win);
   },
 
-  notifyObserversInParentProcess: function(subject, topic, data) {
+  notifyObserversInParentProcess(subject, topic, data) {
     if (subject) {
       throw new Error("Can't send subject to another process!");
     }
     if (this.isMainProcess()) {
       this.notifyObservers(subject, topic, data);
       return;
     }
     var msg = {
-      'op': 'notify',
-      'observerTopic': topic,
-      'observerData': data
+      "op": "notify",
+      "observerTopic": topic,
+      "observerData": data
     };
-    this._sendSyncMessage('SPObserverService', msg);
+    this._sendSyncMessage("SPObserverService", msg);
   },
 
-  removeAllServiceWorkerData: function() {
+  removeAllServiceWorkerData() {
     this.notifyObserversInParentProcess(null, "browser:purge-session-history", "");
   },
 
-  removeServiceWorkerDataForExampleDomain: function() {
+  removeServiceWorkerDataForExampleDomain() {
     this.notifyObserversInParentProcess(null, "browser:purge-domain-data", "example.com");
   },
 
-  cleanUpSTSData: function(origin, flags) {
-    return this._sendSyncMessage('SPCleanUpSTSData', {origin: origin, flags: flags || 0});
+  cleanUpSTSData(origin, flags) {
+    return this._sendSyncMessage("SPCleanUpSTSData", {origin, flags: flags || 0});
   },
 
   _nextExtensionID: 0,
   _extensionListeners: null,
 
-  loadExtension: function(ext, handler) {
+  loadExtension(ext, handler) {
     if (this._extensionListeners == null) {
       this._extensionListeners = new Set();
 
       this._addMessageListener("SPExtensionMessage", msg => {
         for (let listener of this._extensionListeners) {
           try {
             listener(msg);
           } catch (e) {
@@ -2013,49 +2007,49 @@ SpecialPowersAPI.prototype = {
         }
       }
     };
 
     this._extensionListeners.add(listener);
     return extension;
   },
 
-  invalidateExtensionStorageCache: function() {
+  invalidateExtensionStorageCache() {
     this.notifyObserversInParentProcess(null, "extension-invalidate-storage-cache", "");
   },
 
-  allowMedia: function(window, enable) {
+  allowMedia(window, enable) {
     this._getDocShell(window).allowMedia = enable;
   },
 
-  createChromeCache: function(name, url) {
+  createChromeCache(name, url) {
     let principal = this._getPrincipalFromArg(url);
     return wrapIfUnwrapped(new content.window.CacheStorage(name, principal));
   },
 
-  loadChannelAndReturnStatus: function(url, loadUsingSystemPrincipal) {
+  loadChannelAndReturnStatus(url, loadUsingSystemPrincipal) {
     const BinaryInputStream =
         Components.Constructor("@mozilla.org/binaryinputstream;1",
                                "nsIBinaryInputStream",
                                "setInputStream");
 
     return new Promise(function(resolve) {
       let listener = {
-        httpStatus : 0,
+        httpStatus: 0,
 
-        onStartRequest: function(request, context) {
+        onStartRequest(request, context) {
           request.QueryInterface(Ci.nsIHttpChannel);
           this.httpStatus = request.responseStatus;
         },
 
-        onDataAvailable: function(request, context, stream, offset, count) {
+        onDataAvailable(request, context, stream, offset, count) {
           new BinaryInputStream(stream).readByteArray(count);
         },
 
-        onStopRequest: function(request, context, status) {
+        onStopRequest(request, context, status) {
          /* testing here that the redirect was not followed. If it was followed
             we would see a http status of 200 and status of NS_OK */
 
           let httpStatus = this.httpStatus;
           resolve({status, httpStatus});
         }
       };
       let uri = NetUtil.newURI(url);
@@ -2072,50 +2066,50 @@ SpecialPowersAPI.prototype = {
 
   get ParserUtils() {
     if (this._pu != null)
       return this._pu;
 
     let pu = Cc["@mozilla.org/parserutils;1"].getService(Ci.nsIParserUtils);
     // We need to create and return our own wrapper.
     this._pu = {
-      sanitize: function(src, flags) {
+      sanitize(src, flags) {
         return pu.sanitize(src, flags);
       },
-      convertToPlainText: function(src, flags, wrapCol) {
+      convertToPlainText(src, flags, wrapCol) {
         return pu.convertToPlainText(src, flags, wrapCol);
       },
-      parseFragment: function(fragment, flags, isXML, baseURL, element) {
+      parseFragment(fragment, flags, isXML, baseURL, element) {
         let baseURI = baseURL ? NetUtil.newURI(baseURL) : null;
         return pu.parseFragment(unwrapIfWrapped(fragment),
                                 flags, isXML, baseURI,
                                 unwrapIfWrapped(element));
       },
     };
     return this._pu;
   },
 
-  createDOMWalker: function(node, showAnonymousContent) {
+  createDOMWalker(node, showAnonymousContent) {
     node = unwrapIfWrapped(node);
     let walker = Cc["@mozilla.org/inspector/deep-tree-walker;1"].
                  createInstance(Ci.inIDeepTreeWalker);
     walker.showAnonymousContent = showAnonymousContent;
     walker.init(node.ownerDocument, Ci.nsIDOMNodeFilter.SHOW_ALL);
     walker.currentNode = node;
     return {
       get firstChild() {
         return wrapIfUnwrapped(walker.firstChild());
       },
       get lastChild() {
         return wrapIfUnwrapped(walker.lastChild());
       },
     };
   },
 
-  observeMutationEvents: function(mo, node, nativeAnonymousChildList, subtree) {
+  observeMutationEvents(mo, node, nativeAnonymousChildList, subtree) {
     unwrapIfWrapped(mo).observe(unwrapIfWrapped(node),
                                 {nativeAnonymousChildList, subtree});
   },
 
   doCommand(window, cmd) {
     return this._getDocShell(window).doCommand(cmd);
   },
 
@@ -2133,18 +2127,18 @@ SpecialPowersAPI.prototype = {
    */
 
   doUrlClassify(principal, eventTarget, tpEnabled, callback) {
     let classifierService =
       Cc["@mozilla.org/url-classifier/dbservice;1"].getService(Ci.nsIURIClassifier);
 
     let wrapCallback = (...args) => {
       Services.tm.dispatchToMainThread(() => {
-        if (typeof callback == 'function') {
-          callback.call(undefined, ...args);
+        if (typeof callback == "function") {
+          callback(...args);
         } else {
           callback.onClassifyComplete.call(undefined, ...args);
         }
       });
     };
 
     return classifierService.classify(unwrapIfWrapped(principal), eventTarget,
                                       tpEnabled, wrapCallback);
@@ -2152,18 +2146,18 @@ SpecialPowersAPI.prototype = {
 
   // TODO: Bug 1353701 - Supports custom event target for labelling.
   doUrlClassifyLocal(uri, tables, callback) {
     let classifierService =
       Cc["@mozilla.org/url-classifier/dbservice;1"].getService(Ci.nsIURIClassifier);
 
     let wrapCallback = (...args) => {
       Services.tm.dispatchToMainThread(() => {
-        if (typeof callback == 'function') {
-          callback.call(undefined, ...args);
+        if (typeof callback == "function") {
+          callback(...args);
         } else {
           callback.onClassifyComplete.call(undefined, ...args);
         }
       });
     };
 
     return classifierService.asyncClassifyLocalWithTables(unwrapIfWrapped(uri),
                                                           tables,
--- a/testing/specialpowers/jar.mn
+++ b/testing/specialpowers/jar.mn
@@ -1,11 +1,11 @@
 specialpowers.jar:
 % content specialpowers %content/ contentaccessible=true
   content/specialpowers.js (content/specialpowers.js)
-* content/specialpowersAPI.js (content/specialpowersAPI.js)
+  content/specialpowersAPI.js (content/specialpowersAPI.js)
   content/SpecialPowersObserverAPI.js (content/SpecialPowersObserverAPI.js)
   content/SpecialPowersObserver.jsm (content/SpecialPowersObserver.jsm)
   content/MozillaLogger.js (content/MozillaLogger.js)
   content/MockFilePicker.jsm (content/MockFilePicker.jsm)
   content/MockColorPicker.jsm (content/MockColorPicker.jsm)
   content/MockPermissionPrompt.jsm (content/MockPermissionPrompt.jsm)
   content/Assert.jsm (../modules/Assert.jsm)
--- a/testing/web-platform/meta/intersection-observer/display-none.html.ini
+++ b/testing/web-platform/meta/intersection-observer/display-none.html.ini
@@ -1,5 +1,3 @@
 [display-none.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1358668
-  [Intersecting notification after first rAF.]
-    disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
+  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1359318
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -522,20 +522,16 @@ this.ExtensionData = class {
         this.id = this.manifest.applications.gecko.id;
       }
     } catch (e) {
       // Errors are handled by the type checks above.
     }
 
     let whitelist = [];
     for (let perm of this.manifest.permissions) {
-      if (perm == "contextualIdentities" && !Preferences.get("privacy.userContext.enabled")) {
-        continue;
-      }
-
       if (perm === "geckoProfiler") {
         const acceptedExtensions = Preferences.get("extensions.geckoProfiler.acceptedExtensionIds");
         if (!acceptedExtensions.split(",").includes(this.id)) {
           this.manifestError("Only whitelisted extensions are allowed to access the geckoProfiler.");
           continue;
         }
       }
 
--- a/toolkit/components/extensions/ext-contextualIdentities.js
+++ b/toolkit/components/extensions/ext-contextualIdentities.js
@@ -1,12 +1,14 @@
 "use strict";
 
 XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
                                   "resource://gre/modules/ContextualIdentityService.jsm");
+XPCOMUtils.defineLazyPreferenceGetter(this, "containersEnabled",
+                                      "privacy.userContext.enabled");
 
 function convert(identity) {
   let result = {
     name: ContextualIdentityService.getUserContextLabel(identity.userContextId),
     icon: identity.icon,
     color: identity.color,
     cookieStoreId: getCookieStoreIdForContainer(identity.userContextId),
   };
@@ -14,47 +16,63 @@ function convert(identity) {
   return result;
 }
 
 this.contextualIdentities = class extends ExtensionAPI {
   getAPI(context) {
     let self = {
       contextualIdentities: {
         get(cookieStoreId) {
+          if (!containersEnabled) {
+            return Promise.resolve(false);
+          }
+
           let containerId = getContainerForCookieStoreId(cookieStoreId);
           if (!containerId) {
             return Promise.resolve(null);
           }
 
           let identity = ContextualIdentityService.getPublicIdentityFromId(containerId);
           return Promise.resolve(convert(identity));
         },
 
         query(details) {
+          if (!containersEnabled) {
+            return Promise.resolve(false);
+          }
+
           let identities = [];
           ContextualIdentityService.getPublicIdentities().forEach(identity => {
             if (details.name &&
                 ContextualIdentityService.getUserContextLabel(identity.userContextId) != details.name) {
               return;
             }
 
             identities.push(convert(identity));
           });
 
           return Promise.resolve(identities);
         },
 
         create(details) {
+          if (!containersEnabled) {
+            return Promise.resolve(false);
+          }
+
           let identity = ContextualIdentityService.create(details.name,
                                                           details.icon,
                                                           details.color);
           return Promise.resolve(convert(identity));
         },
 
         update(cookieStoreId, details) {
+          if (!containersEnabled) {
+            return Promise.resolve(false);
+          }
+
           let containerId = getContainerForCookieStoreId(cookieStoreId);
           if (!containerId) {
             return Promise.resolve(null);
           }
 
           let identity = ContextualIdentityService.getPublicIdentityFromId(containerId);
           if (!identity) {
             return Promise.resolve(null);
@@ -77,16 +95,20 @@ this.contextualIdentities = class extend
                                                 identity.color)) {
             return Promise.resolve(null);
           }
 
           return Promise.resolve(convert(identity));
         },
 
         remove(cookieStoreId) {
+          if (!containersEnabled) {
+            return Promise.resolve(false);
+          }
+
           let containerId = getContainerForCookieStoreId(cookieStoreId);
           if (!containerId) {
             return Promise.resolve(null);
           }
 
           let identity = ContextualIdentityService.getPublicIdentityFromId(containerId);
           if (!identity) {
             return Promise.resolve(null);
--- a/toolkit/components/extensions/test/xpcshell/test_ext_contextual_identities.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_contextual_identities.js
@@ -18,33 +18,49 @@ add_task(async function test_contextualI
 
   await extension.startup();
   await extension.awaitFinish("contextualIdentities_permission");
   await extension.unload();
 });
 
 
 add_task(async function test_contextualIdentity_no_containers() {
-  function backgroundScript() {
-    browser.test.assertTrue(!browser.contextualIdentities,
-                            "contextualIdentities API is not available when the containers are disabled");
-    browser.test.notifyPass("contextualIdentities_pref");
+  async function backgroundScript() {
+    let ci = await browser.contextualIdentities.get("foobar");
+    browser.test.assertEq(false, ci, "No identity should be returned here");
+
+    ci = await browser.contextualIdentities.get("firefox-container-1");
+    browser.test.assertEq(false, ci, "We don't have any identity");
+
+    let cis = await browser.contextualIdentities.query({});
+    browser.test.assertEq(false, cis, "no containers, 0 containers");
+
+    ci = await browser.contextualIdentities.create({name: "foobar", color: "red", icon: "icon"});
+    browser.test.assertEq(false, ci, "We don't have any identity");
+
+    ci = await browser.contextualIdentities.update("firefox-container-1", {name: "barfoo", color: "blue", icon: "icon icon"});
+    browser.test.assertEq(false, ci, "We don't have any identity");
+
+    ci = await browser.contextualIdentities.remove("firefox-container-1");
+    browser.test.assertEq(false, ci, "We have an identity");
+
+    browser.test.notifyPass("contextualIdentities");
   }
 
   let extension = ExtensionTestUtils.loadExtension({
     background: `(${backgroundScript})()`,
     manifest: {
       permissions: ["contextualIdentities"],
     },
   });
 
   Services.prefs.setBoolPref("privacy.userContext.enabled", false);
 
   await extension.startup();
-  await extension.awaitFinish("contextualIdentities_pref");
+  await extension.awaitFinish("contextualIdentities");
   await extension.unload();
 
   Services.prefs.clearUserPref("privacy.userContext.enabled");
 });
 
 add_task(async function test_contextualIdentity_with_permissions() {
   async function backgroundScript() {
     let ci = await browser.contextualIdentities.get("foobar");
--- a/toolkit/xre/nsXREDirProvider.cpp
+++ b/toolkit/xre/nsXREDirProvider.cpp
@@ -1129,18 +1129,18 @@ GetShellFolderPath(KNOWNFOLDERID folder,
   wchar_t* buf;
   uint32_t bufLength = _retval.GetMutableData(&buf, MAXPATHLEN + 3);
   NS_ENSURE_TRUE(bufLength >= (MAXPATHLEN + 3), NS_ERROR_OUT_OF_MEMORY);
 
   nsresult rv = NS_OK;
 
   LPITEMIDLIST pItemIDList = nullptr;
 
-  if (SUCCEEDED(SHGetKnownFolderIDList(folder, KF_FLAG_SIMPLE_IDLIST | KF_FLAG_DONT_VERIFY,
-                                       NULL, &pItemIDList)) &&
+  DWORD flags = KF_FLAG_SIMPLE_IDLIST | KF_FLAG_DONT_VERIFY | KF_FLAG_NO_ALIAS;
+  if (SUCCEEDED(SHGetKnownFolderIDList(folder, flags, NULL, &pItemIDList)) &&
       SHGetPathFromIDListW(pItemIDList, buf)) {
     // We're going to use wcslen (wcsnlen not available in msvc7.1) so make
     // sure to null terminate.
     buf[bufLength - 1] = L'\0';
     _retval.SetLength(wcslen(buf));
   } else {
     _retval.SetLength(0);
     rv = NS_ERROR_NOT_AVAILABLE;