Merge inbound to mozilla-central a=merge
authorCoroiu Cristina <ccoroiu@mozilla.com>
Fri, 26 Oct 2018 00:56:50 +0300
changeset 499474 1e44ac3b69a4
parent 499446 4e1ac8b657be (current diff)
parent 499473 25132db0240f (diff)
child 499500 4fa606785c71
child 499509 d197f134bdc0
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone65.0a1
first release with
nightly linux32
1e44ac3b69a4 / 65.0a1 / 20181025220044 / files
nightly linux64
1e44ac3b69a4 / 65.0a1 / 20181025220044 / files
nightly mac
1e44ac3b69a4 / 65.0a1 / 20181025220044 / files
nightly win32
1e44ac3b69a4 / 65.0a1 / 20181025220044 / files
nightly win64
1e44ac3b69a4 / 65.0a1 / 20181025220044 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central a=merge
devtools/client/debugger/test/mochitest/addon4.xpi
devtools/client/debugger/test/mochitest/browser_dbg_addon-console.js
devtools/client/debugger/test/mochitest/browser_dbg_debugger-statement.js
devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-01.js
devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-02.js
devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-03.js
devtools/client/debugger/test/mochitest/browser_dbg_listworkers.js
devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js
devtools/client/debugger/test/mochitest/code_WorkerTargetActor.attachThread-worker.js
devtools/client/debugger/test/mochitest/code_listworkers-worker1.js
devtools/client/debugger/test/mochitest/code_listworkers-worker2.js
devtools/client/debugger/test/mochitest/doc_WorkerTargetActor.attachThread-tab.html
devtools/client/debugger/test/mochitest/doc_event-listeners-01.html
devtools/client/debugger/test/mochitest/doc_event-listeners-03.html
devtools/client/debugger/test/mochitest/doc_inline-debugger-statement.html
devtools/client/debugger/test/mochitest/doc_listworkers-tab.html
devtools/client/debugger/test/mochitest/doc_native-event-handler.html
testing/web-platform/meta/css/css-text/text-indent/text-indent-percentage-001.xht.ini
testing/web-platform/meta/css/css-text/text-indent/text-indent-percentage-002.html.ini
testing/web-platform/meta/css/css-text/text-indent/text-indent-percentage-003.html.ini
testing/web-platform/meta/css/css-text/text-indent/text-indent-percentage-004.html.ini
--- a/.eslintignore
+++ b/.eslintignore
@@ -93,16 +93,18 @@ browser/extensions/mortar/**
 # Generated data files
 browser/extensions/formautofill/phonenumberutils/PhoneNumberMetaData.jsm
 
 # devtools/ exclusions
 devtools/client/inspector/markup/test/doc_markup_events_*.html
 devtools/client/inspector/rules/test/doc_media_queries.html
 devtools/client/performance/components/test/test_jit_optimizations_01.html
 devtools/client/responsive.html/test/browser/touch.html
+devtools/client/shared/test/*.html
+devtools/client/shared/test/code_*.js
 devtools/client/shared/components/test/mochitest/*.html
 !devtools/client/shared/components/test/mochitest/test_stack-trace.html
 devtools/client/storage/test/*.html
 !devtools/client/storage/test/storage-cookies.html
 !devtools/client/storage/test/storage-overflow.html
 !devtools/client/storage/test/storage-search.html
 !devtools/client/storage/test/storage-unsecured-iframe.html
 !devtools/client/storage/test/storage-unsecured-iframe-usercontextid.html
--- a/browser/base/content/test/general/browser_findbarClose.js
+++ b/browser/base/content/test/general/browser_findbarClose.js
@@ -22,12 +22,15 @@ add_task(async function findbar_test() {
     let awaitLoad = ContentTaskUtils.waitForEvent(iframe, "load", false);
     iframe.src = "https://example.org/";
     await awaitLoad;
   });
 
   ok(!gFindBar.hidden, "the Find bar isn't hidden after the location of a " +
      "subdocument changes");
 
+  let findBarClosePromise = promiseWaitForEvent(gBrowser, "findbarclose");
   gFindBar.close();
+  await findBarClosePromise;
+
   gBrowser.removeTab(newTab);
 });
 
--- a/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm
+++ b/browser/extensions/pdfjs/content/PdfjsChromeUtils.jsm
@@ -187,50 +187,56 @@ var PdfjsChromeUtils = {
     };
     if (result.total > result.limit) {
       result.total = -1;
     }
     return result;
   },
 
   handleEvent(aEvent) {
+    const type = aEvent.type;
     // Handle the tab find initialized event specially:
-    if (aEvent.type == "TabFindInitialized") {
+    if (type == "TabFindInitialized") {
       let browser = aEvent.target.linkedBrowser;
       this._hookupEventListeners(browser);
-      aEvent.target.removeEventListener(aEvent.type, this);
+      aEvent.target.removeEventListener(type, this);
       return;
     }
 
     // To avoid forwarding the message as a CPOW, create a structured cloneable
     // version of the event for both performance, and ease of usage, reasons.
-    let type = aEvent.type;
-    let detail = {
-      query: aEvent.detail.query,
-      caseSensitive: aEvent.detail.caseSensitive,
-      entireWord: aEvent.detail.entireWord,
-      highlightAll: aEvent.detail.highlightAll,
-      findPrevious: aEvent.detail.findPrevious,
-    };
+    let detail = null;
+    if (type !== "findbarclose") {
+      detail = {
+        query: aEvent.detail.query,
+        caseSensitive: aEvent.detail.caseSensitive,
+        entireWord: aEvent.detail.entireWord,
+        highlightAll: aEvent.detail.highlightAll,
+        findPrevious: aEvent.detail.findPrevious,
+      };
+    }
 
     let browser = aEvent.currentTarget.browser;
     if (!this._browsers.has(browser)) {
       throw new Error("FindEventManager was not bound " +
                       "for the current browser.");
     }
     // Only forward the events if the current browser is a registered browser.
     let mm = browser.messageManager;
     mm.sendAsyncMessage("PDFJS:Child:handleEvent", { type, detail });
     aEvent.preventDefault();
   },
 
-  _types: ["find",
-           "findagain",
-           "findhighlightallchange",
-           "findcasesensitivitychange"],
+  _types: [
+    "find",
+    "findagain",
+    "findhighlightallchange",
+    "findcasesensitivitychange",
+    "findbarclose",
+  ],
 
   _addEventListener(aMsg) {
     let browser = aMsg.target;
     if (this._browsers.has(browser)) {
       throw new Error("FindEventManager was bound 2nd time " +
                       "without unbinding it first.");
     }
 
--- a/devtools/client/debugger/test/mochitest/browser.ini
+++ b/devtools/client/debugger/test/mochitest/browser.ini
@@ -1,54 +1,27 @@
-# Tests in this directory are split into two manifests (this and browser2.ini)
-# to facilitate better chunking; see bug 1294489.
-
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 skip-if = (os == 'linux' && debug && bits == 32)
 support-files =
-  addon4.xpi
-  code_listworkers-worker1.js
-  code_listworkers-worker2.js
-  code_WorkerTargetActor.attachThread-worker.js
-  doc_event-listeners-01.html
-  doc_event-listeners-03.html
-  doc_inline-debugger-statement.html
-  doc_listworkers-tab.html
-  doc_native-event-handler.html
   doc_promise-get-allocation-stack.html
   doc_promise-get-fulfillment-stack.html
   doc_promise-get-rejection-stack.html
   doc_terminate-on-tab-close.html
-  doc_WorkerTargetActor.attachThread-tab.html
   head.js
   !/devtools/client/shared/test/shared-head.js
   !/devtools/client/shared/test/telemetry-test-helpers.js
-[browser_dbg_addon-console.js]
-skip-if = (e10s && debug || os == 'win' || verify) || true # bug 1005274
-tags = addons
-[browser_dbg_debugger-statement.js]
-skip-if = e10s && debug
-[browser_dbg_event-listeners-01.js]
-skip-if = e10s && debug
-[browser_dbg_event-listeners-02.js]
-skip-if = e10s && debug
-[browser_dbg_event-listeners-03.js]
-skip-if = e10s && debug
-[browser_dbg_listworkers.js]
 [browser_dbg_promises-allocation-stack.js]
 uses-unsafe-cpows = true
 skip-if = true
 [browser_dbg_promises-chrome-allocation-stack.js]
 uses-unsafe-cpows = true
 skip-if = true # Bug 1177730
 [browser_dbg_promises-fulfillment-stack.js]
 uses-unsafe-cpows = true
 skip-if = true
 [browser_dbg_promises-rejection-stack.js]
 uses-unsafe-cpows = true
 skip-if = true
 [browser_dbg_terminate-on-tab-close.js]
 uses-unsafe-cpows = true
-skip-if = true
-[browser_dbg_worker-window.js]
-skip-if = (e10s && debug) || true # Bug 1486974
\ No newline at end of file
+skip-if = true
\ No newline at end of file
deleted file mode 100644
--- a/devtools/client/debugger/test/mochitest/code_WorkerTargetActor.attachThread-worker.js
+++ /dev/null
@@ -1,16 +0,0 @@
-"use strict";
-
-function f() {
-  var a = 1;
-  var b = 2;
-  var c = 3;
-}
-
-self.onmessage = function (event) {
-  if (event.data == "ping") {
-    f();
-    postMessage("pong");
-  }
-};
-
-postMessage("load");
deleted file mode 100644
--- a/devtools/client/debugger/test/mochitest/doc_WorkerTargetActor.attachThread-tab.html
+++ /dev/null
@@ -1,8 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <meta charset="utf-8"/>
-  </head>
-  <body>
-  </body>
-</html>
--- a/devtools/client/debugger/test/mochitest/head.js
+++ b/devtools/client/debugger/test/mochitest/head.js
@@ -586,167 +586,16 @@ let initDebugger = Task.async(function*(
       }
     }
     yield onCaretUpdated;
   }
 
   return [tab, debuggerPanel, window];
 });
 
-// Creates an add-on debugger for a given add-on. The returned AddonDebugger
-// object must be destroyed before finishing the test
-function initAddonDebugger(aAddonId) {
-  let addonDebugger = new AddonDebugger();
-  return addonDebugger.init(aAddonId).then(() => addonDebugger);
-}
-
-function AddonDebugger() {
-  this._onMessage = this._onMessage.bind(this);
-  this._onConsoleAPICall = this._onConsoleAPICall.bind(this);
-  EventEmitter.decorate(this);
-}
-
-AddonDebugger.prototype = {
-  init: Task.async(function* (aAddonId) {
-    info("Initializing an addon debugger panel.");
-
-    DebuggerServer.init();
-    DebuggerServer.registerAllActors();
-    DebuggerServer.allowChromeProcess = true;
-
-    this.frame = document.createElement("iframe");
-    this.frame.setAttribute("height", 400);
-    document.documentElement.appendChild(this.frame);
-    window.addEventListener("message", this._onMessage);
-
-    let transport = DebuggerServer.connectPipe();
-    this.client = new DebuggerClient(transport);
-
-    yield this.client.connect();
-
-    let addonTargetActor = yield getAddonActorForId(this.client, aAddonId);
-
-    let targetOptions = {
-      form: addonTargetActor,
-      client: this.client,
-      chrome: true,
-    };
-
-    let toolboxOptions = {
-      customIframe: this.frame
-    };
-
-    this.target = yield TargetFactory.forRemoteTab(targetOptions);
-    let toolbox = yield gDevTools.showToolbox(this.target, "jsdebugger", Toolbox.HostType.CUSTOM, toolboxOptions);
-
-    info("Addon debugger panel shown successfully.");
-
-    this.debuggerPanel = toolbox.getCurrentPanel();
-    yield waitForSourceShown(this.debuggerPanel, "");
-
-    prepareDebugger(this.debuggerPanel);
-    yield this._attachConsole();
-  }),
-
-  destroy: Task.async(function* () {
-    yield this.client.close();
-    yield this.debuggerPanel._toolbox.destroy();
-    this.frame.remove();
-    window.removeEventListener("message", this._onMessage);
-  }),
-
-  _attachConsole: function () {
-    let deferred = promise.defer();
-    this.client.attachConsole(this.target.form.consoleActor, ["ConsoleAPI"])
-      .then(([aResponse, aWebConsoleClient]) => {
-        this.webConsole = aWebConsoleClient;
-        this.client.addListener("consoleAPICall", this._onConsoleAPICall);
-        deferred.resolve();
-      }, e => {
-        deferred.reject(e);
-      });
-    return deferred.promise;
-  },
-
-  _onConsoleAPICall: function (aType, aPacket) {
-    if (aPacket.from != this.webConsole.actor)
-      return;
-    this.emit("console", aPacket.message);
-  },
-
-  /**
-   * Returns a list of the groups and sources in the UI. The returned array
-   * contains objects for each group with properties name and sources. The
-   * sources property contains an array with objects for each source for that
-   * group with properties label and url.
-   */
-  getSourceGroups: Task.async(function* () {
-    let debuggerWin = this.debuggerPanel.panelWin;
-    let sources = yield getSources(debuggerWin.gThreadClient);
-    ok(sources.length, "retrieved sources");
-
-    // groups will be the return value, groupmap and the maps we put in it will
-    // be used as quick lookups to add the url information in below
-    let groups = [];
-    let groupmap = new Map();
-
-    let uigroups = this.debuggerPanel.panelWin.document.querySelectorAll(".side-menu-widget-group");
-    for (let g of uigroups) {
-      let name = g.querySelector(".side-menu-widget-group-title .name").value;
-      let group = {
-        name: name,
-        sources: []
-      };
-      groups.push(group);
-      let labelmap = new Map();
-      groupmap.set(name, labelmap);
-
-      for (let l of g.querySelectorAll(".dbg-source-item")) {
-        let source = {
-          label: l.value,
-          url: null
-        };
-
-        labelmap.set(l.value, source);
-        group.sources.push(source);
-      }
-    }
-
-    for (let source of sources) {
-      let { label, group } = debuggerWin.DebuggerView.Sources.getItemByValue(source.actor).attachment;
-
-      if (!groupmap.has(group)) {
-        ok(false, "Saw a source group not in the UI: " + group);
-        continue;
-      }
-
-      if (!groupmap.get(group).has(label)) {
-        ok(false, "Saw a source label not in the UI: " + label);
-        continue;
-      }
-
-      groupmap.get(group).get(label).url = source.url.split(" -> ").pop();
-    }
-
-    return groups;
-  }),
-
-  _onMessage: function(event) {
-    if (!event.data) {
-      return;
-    }
-    const msg = event.data;
-    switch (msg.name) {
-      case "toolbox-title":
-        this.title = msg.data.value;
-        break;
-    }
-  }
-};
-
 function initChromeDebugger(aOnClose) {
   info("Initializing a chrome debugger process.");
 
   let deferred = promise.defer();
 
   // Wait for the toolbox process to start...
   BrowserToolboxProcess.init(aOnClose, aProcess => {
     info("Browser toolbox process started successfully.");
rename from devtools/client/debugger/test/mochitest/addon4.xpi
rename to devtools/client/shared/test/addon4.xpi
--- a/devtools/client/shared/test/browser.ini
+++ b/devtools/client/shared/test/browser.ini
@@ -1,52 +1,61 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 support-files =
   addon1.xpi
   addon2.xpi
+  addon4.xpi
   browser_devices.json
+  code_listworkers-worker1.js
+  code_listworkers-worker2.js
   code_WorkerTargetActor.attach-worker1.js
   code_WorkerTargetActor.attach-worker2.js
   code_WorkerTargetActor.attachThread-worker.js
   code_frame-script.js
   doc_cubic-bezier-01.html
   doc_cubic-bezier-02.html
   doc_empty-tab-01.html
   doc_empty-tab-02.html
+  doc_event-listeners-01.html
+  doc_event-listeners-03.html
   doc_filter-editor-01.html
   doc_html_tooltip-02.xul
   doc_html_tooltip-03.xul
   doc_html_tooltip-04.xul
   doc_html_tooltip-05.xul
   doc_html_tooltip.xul
   doc_html_tooltip_arrow-01.xul
   doc_html_tooltip_arrow-02.xul
   doc_html_tooltip_doorhanger-01.xul
   doc_html_tooltip_doorhanger-02.xul
   doc_html_tooltip_hover.xul
   doc_html_tooltip_rtl.xul
+  doc_inline-debugger-statement.html
   doc_inplace-editor_autocomplete_offset.xul
   doc_layoutHelpers-getBoxQuads.html
   doc_layoutHelpers.html
+  doc_listworkers-tab.html
+  doc_native-event-handler.html
   doc_options-view.xul
   doc_script-switching-01.html
   doc_script-switching-02.html
   doc_spectrum.html
   doc_tableWidget_basic.html
   doc_tableWidget_keyboard_interaction.xul
   doc_tableWidget_mouse_interaction.xul
   doc_templater_basic.html
   doc_WorkerTargetActor.attach-tab1.html
   doc_WorkerTargetActor.attach-tab2.html
   doc_WorkerTargetActor.attachThread-tab.html
   dummy.html
   frame-script-utils.js
   head.js
+  helper_addons.js
   helper_color_data.js
   helper_html_tooltip.js
   helper_inplace_editor.js
   helper_workers.js
   leakhunt.js
   shared-head.js
   shared-redux-head.js
   telemetry-test-helpers.js
@@ -62,18 +71,30 @@ support-files =
 [browser_css_color.js]
 [browser_cubic-bezier-01.js]
 [browser_cubic-bezier-02.js]
 [browser_cubic-bezier-03.js]
 [browser_cubic-bezier-04.js]
 [browser_cubic-bezier-05.js]
 [browser_cubic-bezier-06.js]
 [browser_cubic-bezier-07.js]
+[browser_dbg_addon-console.js]
+skip-if = (e10s && debug || os == 'win' || verify)
+tags = addons
+[browser_dbg_debugger-statement.js]
+skip-if = e10s && debug
+[browser_dbg_event-listeners-01.js]
+skip-if = e10s && debug
+[browser_dbg_event-listeners-02.js]
+skip-if = e10s && debug
+[browser_dbg_event-listeners-03.js]
+skip-if = e10s && debug
 [browser_dbg_globalactor.js]
 skip-if = e10s
+[browser_dbg_listworkers.js]
 [browser_filter-editor-01.js]
 [browser_filter-editor-02.js]
 [browser_filter-editor-03.js]
 [browser_filter-editor-04.js]
 [browser_filter-editor-05.js]
 [browser_filter-editor-06.js]
 [browser_filter-editor-07.js]
 [browser_filter-editor-08.js]
@@ -241,8 +262,10 @@ skip-if = true # bug 1368569
 [browser_dbg_worker-console-02.js]
 skip-if = e10s && debug
 [browser_dbg_worker-console-03.js]
 skip-if = debug # bug 1334683
 [browser_dbg_worker-console-04.js]
 skip-if = e10s && debug
 [browser_dbg_WorkerTargetActor.attach.js]
 skip-if = e10s && debug
+[browser_dbg_worker-window.js]
+skip-if = (e10s && debug) || true # Bug 1486974
\ No newline at end of file
rename from devtools/client/debugger/test/mochitest/browser_dbg_addon-console.js
rename to devtools/client/shared/test/browser_dbg_addon-console.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_addon-console.js
+++ b/devtools/client/shared/test/browser_dbg_addon-console.js
@@ -1,47 +1,151 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
+/* import-globals-from helper_addons.js */
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/shared/test/helper_addons.js",
+  this);
+
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+var { Toolbox } = require("devtools/client/framework/toolbox");
+var { Task } = require("devtools/shared/task");
+
 // Test that the we can see console messages from the add-on
 
 const ADDON_ID = "browser_dbg_addon4@tests.mozilla.org";
 const ADDON_PATH = "addon4.xpi";
 
 function getCachedMessages(webConsole) {
-  let deferred = promise.defer();
-  webConsole.getCachedMessages(["ConsoleAPI"], (aResponse) => {
-    if (aResponse.error) {
-      deferred.reject(aResponse.error);
+  const deferred = getDeferredPromise().defer();
+  webConsole.getCachedMessages(["ConsoleAPI"], response => {
+    if (response.error) {
+      deferred.reject(response.error);
       return;
     }
-    deferred.resolve(aResponse.messages);
+    deferred.resolve(response.messages);
   });
   return deferred.promise;
 }
 
-function test() {
-  Task.spawn(function* () {
-    let addon = yield addTemporaryAddon(ADDON_PATH);
-    let addonDebugger = yield initAddonDebugger(ADDON_ID);
+// Creates an add-on debugger for a given add-on. The returned AddonDebugger
+// object must be destroyed before finishing the test
+function initAddonDebugger(addonId) {
+  const addonDebugger = new AddonDebugger();
+  return addonDebugger.init(addonId).then(() => addonDebugger);
+}
+
+function AddonDebugger() {
+  this._onMessage = this._onMessage.bind(this);
+  this._onConsoleAPICall = this._onConsoleAPICall.bind(this);
+  EventEmitter.decorate(this);
+}
+
+AddonDebugger.prototype = {
+  init: Task.async(function* (addonId) {
+    info("Initializing an addon debugger panel.");
+
+    DebuggerServer.init();
+    DebuggerServer.registerAllActors();
+    DebuggerServer.allowChromeProcess = true;
+
+    this.frame = document.createElement("iframe");
+    this.frame.setAttribute("height", 400);
+    document.documentElement.appendChild(this.frame);
+    window.addEventListener("message", this._onMessage);
+
+    const transport = DebuggerServer.connectPipe();
+    this.client = new DebuggerClient(transport);
 
-    let webConsole = addonDebugger.webConsole;
-    let messages = yield getCachedMessages(webConsole);
-    is(messages.length, 1, "Should be one cached message");
-    is(messages[0].arguments[0].type, "object", "Should have logged an object");
-    is(messages[0].arguments[0].preview.ownProperties.msg.value, "Hello from the test add-on", "Should have got the right message");
+    yield this.client.connect();
+
+    const addonTargetActor = yield getAddonActorForId(this.client, addonId);
+
+    const targetOptions = {
+      form: addonTargetActor,
+      client: this.client,
+      chrome: true,
+    };
+
+    const toolboxOptions = {
+      customIframe: this.frame,
+    };
 
-    let consolePromise = addonDebugger.once("console");
+    this.target = yield TargetFactory.forRemoteTab(targetOptions);
+    const toolbox = yield gDevTools.showToolbox(
+      this.target, "jsdebugger", Toolbox.HostType.CUSTOM, toolboxOptions);
+
+    info("Addon debugger panel shown successfully.");
+
+    this.debuggerPanel = toolbox.getCurrentPanel();
+    yield this._attachConsole();
+  }),
+
+  destroy: Task.async(function* () {
+    yield this.client.close();
+    this.frame.remove();
+    window.removeEventListener("message", this._onMessage);
+  }),
 
-    console.log("Bad message");
-    Services.obs.notifyObservers(null, "addon-test-ping");
+  _attachConsole: function() {
+    const deferred = getDeferredPromise().defer();
+    this.client.attachConsole(this.target.form.consoleActor, ["ConsoleAPI"])
+      .then(([aResponse, aWebConsoleClient]) => {
+        this.webConsole = aWebConsoleClient;
+        this.client.addListener("consoleAPICall", this._onConsoleAPICall);
+        deferred.resolve();
+      }, e => {
+        deferred.reject(e);
+      });
+    return deferred.promise;
+  },
+
+  _onConsoleAPICall: function(type, packet) {
+    if (packet.from != this.webConsole.actor) {
+      return;
+    }
+    this.emit("console", packet.message);
+  },
 
-    let messageGrip = yield consolePromise;
-    is(messageGrip.arguments[0].type, "object", "Should have logged an object");
-    is(messageGrip.arguments[0].preview.ownProperties.msg.value, "Hello again", "Should have got the right message");
+  _onMessage: function(event) {
+    if (!event.data) {
+      return;
+    }
+    const msg = event.data;
+    switch (msg.name) {
+      case "toolbox-title":
+        this.title = msg.data.value;
+        break;
+    }
+  },
+};
+
+add_task(async function test() {
+  const addon = await addTemporaryAddon(ADDON_PATH);
+  const addonDebugger = await initAddonDebugger(ADDON_ID);
 
-    yield addonDebugger.destroy();
-    yield removeAddon(addon);
-    finish();
-  });
-}
+  const webConsole = addonDebugger.webConsole;
+  const messages = await getCachedMessages(webConsole);
+  is(messages.length, 1, "Should be one cached message");
+  is(messages[0].arguments[0].type, "object", "Should have logged an object");
+  is(messages[0].arguments[0].preview.ownProperties.msg.value,
+    "Hello from the test add-on", "Should have got the right message");
+
+  const consolePromise = addonDebugger.once("console");
+
+  console.log("Bad message");
+  Services.obs.notifyObservers(null, "addon-test-ping");
+
+  const messageGrip = await consolePromise;
+  is(messageGrip.arguments[0].type, "object", "Should have logged an object");
+  is(messageGrip.arguments[0].preview.ownProperties.msg.value,
+    "Hello again", "Should have got the right message");
+
+  await addonDebugger.destroy();
+  await removeAddon(addon);
+  finish();
+});
rename from devtools/client/debugger/test/mochitest/browser_dbg_debugger-statement.js
rename to devtools/client/shared/test/browser_dbg_debugger-statement.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_debugger-statement.js
+++ b/devtools/client/shared/test/browser_dbg_debugger-statement.js
@@ -1,51 +1,62 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 /**
  * Tests the behavior of the debugger statement.
  */
 
-const TAB_URL = EXAMPLE_URL + "doc_inline-debugger-statement.html";
+// Import helpers for the workers
+/* import-globals-from helper_workers.js */
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/shared/test/helper_workers.js",
+  this);
+
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+
+const TAB_URL = TEST_URI_ROOT + "doc_inline-debugger-statement.html";
 
 var gClient;
 var gTab;
 
 function test() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
-  let transport = DebuggerServer.connectPipe();
+  const transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
 
     addTab(TAB_URL)
-      .then((aTab) => {
-        gTab = aTab;
+      .then(tab => {
+        gTab = tab;
         return attachTargetActorForUrl(gClient, TAB_URL);
       })
       .then(testEarlyDebuggerStatement)
       .then(testDebuggerStatement)
       .then(() => gClient.close())
       .then(finish)
-      .catch(aError => {
-        ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
+      .catch(error => {
+        ok(false, "Got an error: " + error.message + "\n" + error.stack);
       });
   });
 }
 
 function testEarlyDebuggerStatement([aGrip, aResponse]) {
-  let deferred = promise.defer();
+  const deferred = getDeferredPromise().defer();
 
-  let onPaused = function (aEvent, aPacket) {
+  const onPaused = function(event, packet) {
     ok(false, "Pause shouldn't be called before we've attached!");
     deferred.reject();
   };
 
   gClient.addListener("paused", onPaused);
 
   // This should continue without nesting an event loop and calling
   // the onPaused hook, because we haven't attached yet.
@@ -60,26 +71,26 @@ function testEarlyDebuggerStatement([aGr
       deferred.resolve([aGrip, aResponse]);
     });
   });
 
   return deferred.promise;
 }
 
 function testDebuggerStatement([aGrip, aResponse]) {
-  let deferred = promise.defer();
+  const deferred = getDeferredPromise().defer();
 
-  gClient.addListener("paused", (aEvent, aPacket) => {
+  gClient.addListener("paused", (event, packet) => {
     gClient.request({ to: aResponse.threadActor, type: "resume" }, () => {
       ok(true, "The pause handler was triggered on a debugger statement.");
       deferred.resolve();
     });
   });
 
   // Reach around the debugging protocol and execute the debugger statement.
   callInTab(gTab, "runDebuggerStatement");
 
   return deferred.promise;
 }
 
-registerCleanupFunction(function () {
+registerCleanupFunction(function() {
   gClient = null;
 });
rename from devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-01.js
rename to devtools/client/shared/test/browser_dbg_event-listeners-01.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-01.js
+++ b/devtools/client/shared/test/browser_dbg_event-listeners-01.js
@@ -1,99 +1,111 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 /**
  * Tests that the eventListeners request works.
  */
 
-const TAB_URL = EXAMPLE_URL + "doc_event-listeners-01.html";
+// Import helpers for the workers
+/* import-globals-from helper_workers.js */
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/shared/test/helper_workers.js",
+  this);
+
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+
+const TAB_URL = TEST_URI_ROOT + "doc_event-listeners-01.html";
 
 add_task(async function() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
-  let transport = DebuggerServer.connectPipe();
-  let client = new DebuggerClient(transport);
-  let [type, traits] = await client.connect();
+  const transport = DebuggerServer.connectPipe();
+  const client = new DebuggerClient(transport);
+  const [type] = await client.connect();
 
   Assert.equal(type, "browser",
     "Root actor should identify itself as a browser.");
 
-  let tab = await addTab(TAB_URL);
-  let threadClient = await attachThreadActorForUrl(client, TAB_URL);
+  const tab = await addTab(TAB_URL);
+  const threadClient = await attachThreadActorForUrl(client, TAB_URL);
   await pauseDebuggee(tab, client, threadClient);
   await testEventListeners(client, threadClient);
 
   await client.close();
 });
 
-function pauseDebuggee(aTab, aClient, aThreadClient) {
-  let deferred = promise.defer();
+function pauseDebuggee(tab, client, threadClient) {
+  const deferred = getDeferredPromise().defer();
 
-  aClient.addOneTimeListener("paused", (aEvent, aPacket) => {
-    is(aPacket.type, "paused",
+  client.addOneTimeListener("paused", (event, packet) => {
+    is(packet.type, "paused",
       "We should now be paused.");
-    is(aPacket.why.type, "debuggerStatement",
+    is(packet.why.type, "debuggerStatement",
       "The debugger statement was hit.");
 
-    deferred.resolve(aThreadClient);
+    deferred.resolve(threadClient);
   });
 
-  generateMouseClickInTab(aTab, "content.document.querySelector('button')");
+  generateMouseClickInTab(tab, "content.document.querySelector('button')");
 
   return deferred.promise;
 }
 
-async function testEventListeners(aClient, aThreadClient) {
-  let packet = await aThreadClient.eventListeners();
+async function testEventListeners(client, threadClient) {
+  const packet = await threadClient.eventListeners();
 
   if (packet.error) {
-    let msg = "Error getting event listeners: " + packet.message;
+    const msg = "Error getting event listeners: " + packet.message;
     Assert.ok(false, msg);
     return;
   }
 
   is(packet.listeners.length, 3, "Found all event listeners.");
 
-  let listeners = await promise.all(packet.listeners.map(listener => {
-    const lDeferred = promise.defer();
-    aThreadClient.pauseGrip(listener.function).getDefinitionSite(aResponse => {
-      if (aResponse.error) {
-        const msg = "Error getting function definition site: " + aResponse.message;
+  const listeners = await getDeferredPromise().all(packet.listeners.map(listener => {
+    const lDeferred = getDeferredPromise().defer();
+    threadClient.pauseGrip(listener.function).getDefinitionSite(response => {
+      if (response.error) {
+        const msg = "Error getting function definition site: " + response.message;
         ok(false, msg);
         lDeferred.reject(msg);
         return;
       }
-      listener.function.url = aResponse.source.url;
+      listener.function.url = response.source.url;
       lDeferred.resolve(listener);
     });
     return lDeferred.promise;
   }));
 
-  let types = [];
+  const types = [];
 
-  for (let l of listeners) {
+  for (const l of listeners) {
     info("Listener for the " + l.type + " event.");
-    let node = l.node;
+    const node = l.node;
     ok(node, "There is a node property.");
     ok(node.object, "There is a node object property.");
     if (node.selector != "window") {
-      let nodeCount =
-        await ContentTask.spawn(gBrowser.selectedBrowser, node.selector, async (selector) => {
-          return content.document.querySelectorAll(selector).length;
-        });
+      const nodeCount =
+        await ContentTask.spawn(gBrowser.selectedBrowser, node.selector,
+          async (selector) => {
+            return content.document.querySelectorAll(selector).length;
+          });
       Assert.equal(nodeCount, 1, "The node property is a unique CSS selector.");
     } else {
       Assert.ok(true, "The node property is a unique CSS selector.");
     }
 
-    let func = l.function;
+    const func = l.function;
     ok(func, "There is a function property.");
     is(func.type, "object", "The function form is of type 'object'.");
     is(func.class, "Function", "The function form is of class 'Function'.");
 
     // The onchange handler is an inline string that doesn't have
     // a URL because it's basically eval'ed
     if (l.type !== "change") {
       is(func.url, TAB_URL, "The function url is correct.");
@@ -123,10 +135,10 @@ async function testEventListeners(aClien
         "'isEventHandler' property has the right value.");
     }
   }
 
   Assert.ok(types.includes("click"), "Found the click handler.");
   Assert.ok(types.includes("change"), "Found the change handler.");
   Assert.ok(types.includes("keyup"), "Found the keyup handler.");
 
-  await aThreadClient.resume();
+  await threadClient.resume();
 }
rename from devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-02.js
rename to devtools/client/shared/test/browser_dbg_event-listeners-02.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-02.js
+++ b/devtools/client/shared/test/browser_dbg_event-listeners-02.js
@@ -1,111 +1,122 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 /**
  * Tests that the eventListeners request works when bound functions are used as
  * event listeners.
  */
 
-const TAB_URL = EXAMPLE_URL + "doc_event-listeners-03.html";
+// Import helpers for the workers
+/* import-globals-from helper_workers.js */
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/shared/test/helper_workers.js",
+  this);
 
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+
+const TAB_URL = TEST_URI_ROOT + "doc_event-listeners-03.html";
 
 add_task(async function() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
-  let transport = DebuggerServer.connectPipe();
-  let client = new DebuggerClient(transport);
-  let [type, traits] = await client.connect();
+  const transport = DebuggerServer.connectPipe();
+  const client = new DebuggerClient(transport);
+  const [type] = await client.connect();
 
   Assert.equal(type, "browser",
     "Root actor should identify itself as a browser.");
 
-  let tab = await addTab(TAB_URL);
-  let threadClient = await attachThreadActorForUrl(client, TAB_URL);
+  const tab = await addTab(TAB_URL);
+  const threadClient = await attachThreadActorForUrl(client, TAB_URL);
   await pauseDebuggee(tab, client, threadClient);
   await testEventListeners(client, threadClient);
   await client.close();
 });
 
-function pauseDebuggee(aTab, aClient, aThreadClient) {
-  let deferred = promise.defer();
+function pauseDebuggee(tab, client, threadClient) {
+  const deferred = getDeferredPromise().defer();
 
-  aClient.addOneTimeListener("paused", (aEvent, aPacket) => {
-    is(aPacket.type, "paused",
+  client.addOneTimeListener("paused", (event, packet) => {
+    is(packet.type, "paused",
       "We should now be paused.");
-    is(aPacket.why.type, "debuggerStatement",
+    is(packet.why.type, "debuggerStatement",
       "The debugger statement was hit.");
 
-    deferred.resolve(aThreadClient);
+    deferred.resolve(threadClient);
   });
 
-  generateMouseClickInTab(aTab, "content.document.querySelector('button')");
+  generateMouseClickInTab(tab, "content.document.querySelector('button')");
 
   return deferred.promise;
 }
 
-async function testEventListeners(aClient, aThreadClient) {
-  let packet = await aThreadClient.eventListeners();
+async function testEventListeners(client, threadClient) {
+  const packet = await threadClient.eventListeners();
 
   if (packet.error) {
-    let msg = "Error getting event listeners: " + aPacket.message;
+    const msg = "Error getting event listeners: " + packet.message;
     ok(false, msg);
     return;
   }
 
   is(packet.listeners.length, 3,
     "Found all event listeners.");
 
-  let listeners = await promise.all(packet.listeners.map(listener => {
-    const lDeferred = promise.defer();
-    aThreadClient.pauseGrip(listener.function).getDefinitionSite(aResponse => {
-      if (aResponse.error) {
-        const msg = "Error getting function definition site: " + aResponse.message;
+  const listeners = await getDeferredPromise().all(packet.listeners.map(listener => {
+    const lDeferred = getDeferredPromise().defer();
+    threadClient.pauseGrip(listener.function).getDefinitionSite(response => {
+      if (response.error) {
+        const msg = "Error getting function definition site: " + response.message;
         ok(false, msg);
         lDeferred.reject(msg);
         return;
       }
-      listener.function.url = aResponse.source.url;
+      listener.function.url = response.source.url;
       lDeferred.resolve(listener);
     });
     return lDeferred.promise;
   }));
 
   Assert.equal(listeners.length, 3, "Found three event listeners.");
 
-  for (let l of listeners) {
-    let node = l.node;
+  for (const l of listeners) {
+    const node = l.node;
     ok(node, "There is a node property.");
     ok(node.object, "There is a node object property.");
 
     if (node.selector != "window") {
-      let nodeCount =
-        await ContentTask.spawn(gBrowser.selectedBrowser, node.selector, async (selector) => {
-          return content.document.querySelectorAll(selector).length;
-        });
+      const nodeCount =
+        await ContentTask.spawn(gBrowser.selectedBrowser, node.selector,
+          async (selector) => {
+            return content.document.querySelectorAll(selector).length;
+          });
       Assert.equal(nodeCount, 1, "The node property is a unique CSS selector.");
     } else {
       Assert.ok(true, "The node property is a unique CSS selector.");
     }
 
-    let func = l.function;
+    const func = l.function;
     ok(func, "There is a function property.");
     is(func.type, "object", "The function form is of type 'object'.");
     is(func.class, "Function", "The function form is of class 'Function'.");
     is(func.url, TAB_URL, "The function url is correct.");
 
     is(l.type, "click", "This is a click event listener.");
     is(l.allowsUntrusted, true,
       "'allowsUntrusted' property has the right value.");
     is(l.inSystemEventGroup, false,
       "'inSystemEventGroup' property has the right value.");
     is(l.isEventHandler, false,
       "'isEventHandler' property has the right value.");
     is(l.capturing, false,
       "Capturing property has the right value.");
   }
 
-  await aThreadClient.resume();
+  await threadClient.resume();
 }
rename from devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-03.js
rename to devtools/client/shared/test/browser_dbg_event-listeners-03.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_event-listeners-03.js
+++ b/devtools/client/shared/test/browser_dbg_event-listeners-03.js
@@ -1,81 +1,92 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 /**
  * Tests that the eventListeners request works when there are event handlers
  * that the debugger cannot unwrap.
  */
 
-const TAB_URL = EXAMPLE_URL + "doc_native-event-handler.html";
+// Import helpers for the workers
+/* import-globals-from helper_workers.js */
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/shared/test/helper_workers.js",
+  this);
+
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+
+const TAB_URL = TEST_URI_ROOT + "doc_native-event-handler.html";
 
 var gClient;
 var gTab;
 
 function test() {
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
-  let transport = DebuggerServer.connectPipe();
+  const transport = DebuggerServer.connectPipe();
   gClient = new DebuggerClient(transport);
   gClient.connect().then(([aType, aTraits]) => {
     is(aType, "browser",
       "Root actor should identify itself as a browser.");
 
     addTab(TAB_URL)
-      .then((aTab) => {
-        gTab = aTab;
+      .then((tab) => {
+        gTab = tab;
         return attachThreadActorForUrl(gClient, TAB_URL);
       })
       .then(pauseDebuggee)
       .then(testEventListeners)
       .then(() => gClient.close())
       .then(finish)
-      .catch(aError => {
-        ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
+      .catch(error => {
+        ok(false, "Got an error: " + error.message + "\n" + error.stack);
       });
   });
 }
 
-function pauseDebuggee(aThreadClient) {
-  let deferred = promise.defer();
+function pauseDebuggee(threadClient) {
+  const deferred = getDeferredPromise().defer();
 
-  gClient.addOneTimeListener("paused", (aEvent, aPacket) => {
-    is(aPacket.type, "paused",
+  gClient.addOneTimeListener("paused", (event, packet) => {
+    is(packet.type, "paused",
       "We should now be paused.");
-    is(aPacket.why.type, "debuggerStatement",
+    is(packet.why.type, "debuggerStatement",
       "The debugger statement was hit.");
 
-    deferred.resolve(aThreadClient);
+    deferred.resolve(threadClient);
   });
 
   generateMouseClickInTab(gTab, "content.document.querySelector('button')");
 
   return deferred.promise;
 }
 
-function testEventListeners(aThreadClient) {
-  let deferred = promise.defer();
+function testEventListeners(threadClient) {
+  const deferred = getDeferredPromise().defer();
 
-  aThreadClient.eventListeners(aPacket => {
-    if (aPacket.error) {
-      let msg = "Error getting event listeners: " + aPacket.message;
+  threadClient.eventListeners(packet => {
+    if (packet.error) {
+      const msg = "Error getting event listeners: " + packet.message;
       ok(false, msg);
       deferred.reject(msg);
       return;
     }
 
     // There are 2 event listeners in the page: button.onclick, window.onload.
     // The video element controls listeners are skipped — they cannot be
     // unwrapped but they shouldn't cause us to throw either.
-    is(aPacket.listeners.length, 2, "Found all event listeners.");
-    aThreadClient.resume(deferred.resolve);
+    is(packet.listeners.length, 2, "Found all event listeners.");
+    threadClient.resume(deferred.resolve);
   });
 
   return deferred.promise;
 }
 
-registerCleanupFunction(function () {
+registerCleanupFunction(function() {
   gClient = null;
 });
--- a/devtools/client/shared/test/browser_dbg_listaddons.js
+++ b/devtools/client/shared/test/browser_dbg_listaddons.js
@@ -1,25 +1,23 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+/* import-globals-from helper_addons.js */
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/shared/test/helper_addons.js",
+  this);
+
 var { DebuggerServer } = require("devtools/server/main");
 var { DebuggerClient } = require("devtools/shared/client/debugger-client");
 
-const chromeRegistry =
-  Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry);
-const DEBUGGER_CHROME_URL = "chrome://mochitests/content/browser/devtools/client/shared/test/";
-const DEBUGGER_CHROME_URI = Services.io.newURI(DEBUGGER_CHROME_URL);
-
-var { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm", {});
-
 /**
  * Make sure the listAddons request works as specified.
  */
 const ADDON1_ID = "jid1-oBAwBoE5rSecNg@jetpack";
 const ADDON1_PATH = "addon1.xpi";
 const ADDON2_ID = "jid1-qjtzNGV8xw5h2A@jetpack";
 const ADDON2_PATH = "addon2.xpi";
 
@@ -113,53 +111,8 @@ function testRemoveSecondAddon() {
 }
 
 registerCleanupFunction(function() {
   gAddon1 = null;
   gAddon1Actor = null;
   gAddon2 = null;
   gClient = null;
 });
-
-function getAddonURIFromPath(path) {
-  const chromeURI = Services.io.newURI(path, null, DEBUGGER_CHROME_URI);
-  return chromeRegistry.convertChromeURL(chromeURI).QueryInterface(Ci.nsIFileURL);
-}
-
-function addTemporaryAddon(path) {
-  const addonFile = getAddonURIFromPath(path).file;
-  info("Installing addon: " + addonFile.path);
-
-  return AddonManager.installTemporaryAddon(addonFile);
-}
-
-function getAddonActorForId(client, addonId) {
-  info("Get addon actor for ID: " + addonId);
-  const deferred = promise.defer();
-
-  client.listAddons().then(response => {
-    const addonTargetActor = response.addons.filter(grip => grip.id == addonId).pop();
-    info("got addon actor for ID: " + addonId);
-    deferred.resolve(addonTargetActor);
-  });
-
-  return deferred.promise;
-}
-
-function removeAddon(addon) {
-  info("Removing addon.");
-
-  const deferred = promise.defer();
-
-  const listener = {
-    onUninstalled: function(uninstalledAddon) {
-      if (uninstalledAddon != addon) {
-        return;
-      }
-      AddonManager.removeAddonListener(listener);
-      deferred.resolve();
-    },
-  };
-  AddonManager.addAddonListener(listener);
-  addon.uninstall();
-
-  return deferred.promise;
-}
rename from devtools/client/debugger/test/mochitest/browser_dbg_listworkers.js
rename to devtools/client/shared/test/browser_dbg_listworkers.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_listworkers.js
+++ b/devtools/client/shared/test/browser_dbg_listworkers.js
@@ -1,59 +1,73 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Import helpers for the workers
+/* import-globals-from helper_workers.js */
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/shared/test/helper_workers.js",
+  this);
+
+var { DebuggerServer } = require("devtools/server/main");
+var { DebuggerClient } = require("devtools/shared/client/debugger-client");
+
 var TAB_URL = EXAMPLE_URL + "doc_listworkers-tab.html";
 var WORKER1_URL = "code_listworkers-worker1.js";
 var WORKER2_URL = "code_listworkers-worker2.js";
 
-function test() {
-  Task.spawn(function* () {
-    DebuggerServer.init();
-    DebuggerServer.registerAllActors();
+add_task(async function test() {
+  DebuggerServer.init();
+  DebuggerServer.registerAllActors();
 
-    let client = new DebuggerClient(DebuggerServer.connectPipe());
-    yield connect(client);
+  const client = new DebuggerClient(DebuggerServer.connectPipe());
+  await connect(client);
 
-    let tab = yield addTab(TAB_URL);
-    let { tabs } = yield listTabs(client);
-    let [, targetFront] = yield attachTarget(client, findTab(tabs, TAB_URL));
+  const tab = await addTab(TAB_URL);
+  const { tabs } = await listTabs(client);
+  const [, targetFront] = await attachTarget(client, findTab(tabs, TAB_URL));
 
-    let { workers } = yield listWorkers(targetFront);
-    is(workers.length, 0);
+  let { workers } = await listWorkers(targetFront);
+  is(workers.length, 0);
 
-    executeSoon(() => {
-      evalInTab(tab, "var worker1 = new Worker('" + WORKER1_URL + "');");
-    });
-    yield waitForWorkerListChanged(targetFront);
+  executeSoon(() => {
+    evalInTab(tab, "var worker1 = new Worker('" + WORKER1_URL + "');");
+  });
+  await waitForWorkerListChanged(targetFront);
 
-    ({ workers } = yield listWorkers(targetFront));
-    is(workers.length, 1);
-    is(workers[0].url, WORKER1_URL);
+  ({ workers } = await listWorkers(targetFront));
+  is(workers.length, 1);
+  is(workers[0].url, WORKER1_URL);
 
-    executeSoon(() => {
-      evalInTab(tab, "var worker2 = new Worker('" + WORKER2_URL + "');");
-    });
-    yield waitForWorkerListChanged(targetFront);
+  executeSoon(() => {
+    evalInTab(tab, "var worker2 = new Worker('" + WORKER2_URL + "');");
+  });
+  await waitForWorkerListChanged(targetFront);
 
-    ({ workers } = yield listWorkers(targetFront));
-    is(workers.length, 2);
-    is(workers[0].url, WORKER1_URL);
-    is(workers[1].url, WORKER2_URL);
+  ({ workers } = await listWorkers(targetFront));
+  is(workers.length, 2);
+  is(workers[0].url, WORKER1_URL);
+  is(workers[1].url, WORKER2_URL);
 
-    executeSoon(() => {
-      evalInTab(tab, "worker1.terminate()");
-    });
-    yield waitForWorkerListChanged(targetFront);
+  executeSoon(() => {
+    evalInTab(tab, "worker1.terminate()");
+  });
+  await waitForWorkerListChanged(targetFront);
 
-    ({ workers } = yield listWorkers(targetFront));
-    is(workers.length, 1);
-    is(workers[0].url, WORKER2_URL);
+  ({ workers } = await listWorkers(targetFront));
+  is(workers.length, 1);
+  is(workers[0].url, WORKER2_URL);
 
-    executeSoon(() => {
-      evalInTab(tab, "worker2.terminate()");
-    });
-    yield waitForWorkerListChanged(targetFront);
+  executeSoon(() => {
+    evalInTab(tab, "worker2.terminate()");
+  });
+  await waitForWorkerListChanged(targetFront);
 
-    ({ workers } = yield listWorkers(targetFront));
-    is(workers.length, 0);
+  ({ workers } = await listWorkers(targetFront));
+  is(workers.length, 0);
 
-    yield close(client);
-    finish();
-  });
-}
+  await close(client);
+  finish();
+});
rename from devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js
rename to devtools/client/shared/test/browser_dbg_worker-window.js
--- a/devtools/client/debugger/test/mochitest/browser_dbg_worker-window.js
+++ b/devtools/client/shared/test/browser_dbg_worker-window.js
@@ -1,60 +1,66 @@
 // Check to make sure that a worker can be attached to a toolbox
 // directly, and that the toolbox has expected properties.
 
 "use strict";
 
+// Import helpers for the workers
+/* import-globals-from helper_workers.js */
+Services.scriptloader.loadSubScript(
+  "chrome://mochitests/content/browser/devtools/client/shared/test/helper_workers.js",
+  this);
+
 // The following "connectionClosed" rejection should not be left uncaught. This
 // test has been whitelisted until the issue is fixed.
 ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm", this);
 PromiseTestUtils.expectUncaughtRejection(/[object Object]/);
 
-var TAB_URL = EXAMPLE_URL + "doc_WorkerTargetActor.attachThread-tab.html";
-var WORKER_URL = "code_WorkerTargetActor.attachThread-worker.js";
+const TAB_URL = EXAMPLE_URL + "doc_WorkerTargetActor.attachThread-tab.html";
+const WORKER_URL = "code_WorkerTargetActor.attachThread-worker.js";
 
 add_task(async function() {
   await pushPrefs(["devtools.scratchpad.enabled", true]);
 
   DebuggerServer.init();
   DebuggerServer.registerAllActors();
 
-  let client = new DebuggerClient(DebuggerServer.connectPipe());
+  const client = new DebuggerClient(DebuggerServer.connectPipe());
   await connect(client);
 
-  let tab = await addTab(TAB_URL);
-  let { tabs } = await listTabs(client);
-  let [, targetFront] = await attachTarget(client, findTab(tabs, TAB_URL));
+  const tab = await addTab(TAB_URL);
+  const { tabs } = await listTabs(client);
+  const [, targetFront] = await attachTarget(client, findTab(tabs, TAB_URL));
 
   await listWorkers(targetFront);
   await createWorkerInTab(tab, WORKER_URL);
 
-  let { workers } = await listWorkers(targetFront);
-  let [, workerTargetFront] = await attachWorker(targetFront,
+  const { workers } = await listWorkers(targetFront);
+  const [, workerTargetFront] = await attachWorker(targetFront,
                                              findWorker(workers, WORKER_URL));
 
-  let toolbox = await gDevTools.showToolbox(TargetFactory.forWorker(workerTargetFront),
+  const toolbox = await gDevTools.showToolbox(TargetFactory.forWorker(workerTargetFront),
                                             "jsdebugger",
                                             Toolbox.HostType.WINDOW);
 
   is(toolbox.hostType, "window", "correct host");
 
   await new Promise(done => {
     toolbox.win.parent.addEventListener("message", function onmessage(event) {
       if (event.data.name == "set-host-title") {
         toolbox.win.parent.removeEventListener("message", onmessage);
         done();
       }
     });
   });
   ok(toolbox.win.parent.document.title.includes(WORKER_URL),
      "worker URL in host title");
 
-  let toolTabs = toolbox.doc.querySelectorAll(".devtools-tab");
-  let activeTools = [...toolTabs].map(tab=>tab.getAttribute("data-id"));
+  const toolTabs = toolbox.doc.querySelectorAll(".devtools-tab");
+  const activeTools = [...toolTabs].map(toolTab => toolTab.getAttribute("data-id"));
 
   is(activeTools.join(","), "webconsole,jsdebugger,scratchpad",
     "Correct set of tools supported by worker");
 
   terminateWorkerInTab(tab, WORKER_URL);
   await waitForWorkerClose(workerTargetFront);
   await close(client);
 
rename from devtools/client/debugger/test/mochitest/code_listworkers-worker1.js
rename to devtools/client/shared/test/code_listworkers-worker1.js
--- a/devtools/client/debugger/test/mochitest/code_listworkers-worker1.js
+++ b/devtools/client/shared/test/code_listworkers-worker1.js
@@ -1,3 +1,3 @@
 "use strict";
 
-self.onmessage = function () {};
+self.onmessage = function() {};
rename from devtools/client/debugger/test/mochitest/code_listworkers-worker2.js
rename to devtools/client/shared/test/code_listworkers-worker2.js
--- a/devtools/client/debugger/test/mochitest/code_listworkers-worker2.js
+++ b/devtools/client/shared/test/code_listworkers-worker2.js
@@ -1,3 +1,3 @@
 "use strict";
 
-self.onmessage = function () {};
+self.onmessage = function() {};
rename from devtools/client/debugger/test/mochitest/doc_event-listeners-01.html
rename to devtools/client/shared/test/doc_event-listeners-01.html
--- a/devtools/client/debugger/test/mochitest/doc_event-listeners-01.html
+++ b/devtools/client/shared/test/doc_event-listeners-01.html
@@ -7,36 +7,38 @@
     <title>Debugger test page</title>
   </head>
 
   <body>
     <button>Click me!</button>
     <input type="text" onchange="changeHandler()">
 
     <script type="text/javascript">
-      window.addEventListener("load", function () {
+      "use strict";
+
+      window.addEventListener("load", function() {
         function initialSetup(event) {
           debugger;
-          var button = document.querySelector("button");
+          const button = document.querySelector("button");
           button.onclick = clickHandler;
         }
         function clickHandler(event) {
           window.foobar = "clickHandler";
         }
         function changeHandler(event) {
           window.foobar = "changeHandler";
         }
         function keyupHandler(event) {
           window.foobar = "keyupHandler";
         }
 
-        var button = document.querySelector("button");
+        const button = document.querySelector("button");
         button.onclick = initialSetup;
 
-        var input = document.querySelector("input");
+        const input = document.querySelector("input");
         input.addEventListener("keyup", keyupHandler, true);
 
         window.changeHandler = changeHandler;
       }, {once: true});
     </script>
   </body>
 
 </html>
rename from devtools/client/debugger/test/mochitest/doc_event-listeners-03.html
rename to devtools/client/shared/test/doc_event-listeners-03.html
--- a/devtools/client/debugger/test/mochitest/doc_event-listeners-03.html
+++ b/devtools/client/shared/test/doc_event-listeners-03.html
@@ -9,54 +9,56 @@
 
   <body>
     <button id="initialSetup">initialSetup</button>
     <button id="clicker">clicker</button>
     <button id="handleEventClick">handleEventClick</button>
     <button id="boundHandleEventClick">boundHandleEventClick</button>
 
     <script type="text/javascript">
-      window.addEventListener("load", function () {
+      "use strict";
+
+      window.addEventListener("load", function() {
         function initialSetup(event) {
-          var button = document.getElementById("initialSetup");
+          const button = document.getElementById("initialSetup");
           button.removeEventListener("click", initialSetup);
           debugger;
         }
 
         function clicker(event) {
           window.foobar = "clicker";
         }
 
         function handleEventClick() {
-          var button = document.getElementById("handleEventClick");
+          const button = document.getElementById("handleEventClick");
           // Create a long prototype chain to test for weird edge cases.
           button.addEventListener("click", Object.create(Object.create(this)));
         }
 
         handleEventClick.prototype.handleEvent = function() {
           window.foobar = "handleEventClick";
         };
 
         function boundHandleEventClick() {
-          var button = document.getElementById("boundHandleEventClick");
+          const button = document.getElementById("boundHandleEventClick");
           this.handleEvent = this.handleEvent.bind(this);
           button.addEventListener("click", this);
         }
 
         boundHandleEventClick.prototype.handleEvent = function() {
           window.foobar = "boundHandleEventClick";
         };
 
-        var button = document.getElementById("clicker");
+        const button = document.getElementById("clicker");
         // Bind more than once to test for weird edge cases.
-        var boundClicker = clicker.bind(this).bind(this).bind(this);
+        const boundClicker = clicker.bind(this).bind(this).bind(this);
         button.addEventListener("click", boundClicker);
 
         new handleEventClick();
         new boundHandleEventClick();
 
-        var initButton = document.getElementById("initialSetup");
+        const initButton = document.getElementById("initialSetup");
         initButton.addEventListener("click", initialSetup);
       }, {once: true});
     </script>
   </body>
 
 </html>
rename from devtools/client/debugger/test/mochitest/doc_inline-debugger-statement.html
rename to devtools/client/shared/test/doc_inline-debugger-statement.html
--- a/devtools/client/debugger/test/mochitest/doc_inline-debugger-statement.html
+++ b/devtools/client/shared/test/doc_inline-debugger-statement.html
@@ -7,15 +7,16 @@
     <meta charset="utf-8"/>
     <title>Debugger test page</title>
   </head>
 
   <body>
     <button>Click me!</button>
 
     <script type="text/javascript">
+      "use strict";
       function runDebuggerStatement() {
         debugger;
       }
     </script>
   </body>
 
 </html>
rename from devtools/client/debugger/test/mochitest/doc_listworkers-tab.html
rename to devtools/client/shared/test/doc_listworkers-tab.html
rename from devtools/client/debugger/test/mochitest/doc_native-event-handler.html
rename to devtools/client/shared/test/doc_native-event-handler.html
--- a/devtools/client/debugger/test/mochitest/doc_native-event-handler.html
+++ b/devtools/client/shared/test/doc_native-event-handler.html
@@ -1,16 +1,18 @@
 <!-- Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/ -->
 <!doctype html>
 <html lang="en">
   <head>
     <meta charset="utf-8">
     <title>A video element with native event handlers</title>
     <script type="text/javascript">
+      "use strict";
+
       function initialSetup(event) {
         debugger;
       }
 
       window.addEventListener("load", function() {});
     </script>
   </head>
   <body>
new file mode 100644
--- /dev/null
+++ b/devtools/client/shared/test/helper_addons.js
@@ -0,0 +1,69 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+/* eslint no-unused-vars: [2, {"vars": "local", "args": "none"}] */
+
+"use strict";
+
+const chromeRegistry =
+  Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIChromeRegistry);
+const DEBUGGER_CHROME_URL = "chrome://mochitests/content/browser/devtools/client/shared/test/";
+const DEBUGGER_CHROME_URI = Services.io.newURI(DEBUGGER_CHROME_URL);
+
+const EventEmitter = require("devtools/shared/event-emitter");
+
+var { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm", {});
+
+/**
+ * Returns a thenable promise
+ * @return {Promise}
+ */
+function getDeferredPromise() {
+  // Override promise with deprecated-sync-thenables
+  const promise = require("devtools/shared/deprecated-sync-thenables");
+  return promise;
+}
+
+function getAddonURIFromPath(path) {
+  const chromeURI = Services.io.newURI(path, null, DEBUGGER_CHROME_URI);
+  return chromeRegistry.convertChromeURL(chromeURI).QueryInterface(Ci.nsIFileURL);
+}
+
+function addTemporaryAddon(path) {
+  const addonFile = getAddonURIFromPath(path).file;
+  info("Installing addon: " + addonFile.path);
+
+  return AddonManager.installTemporaryAddon(addonFile);
+}
+
+function getAddonActorForId(client, addonId) {
+  info("Get addon actor for ID: " + addonId);
+  const deferred = getDeferredPromise().defer();
+
+  client.listAddons().then(response => {
+    const addonTargetActor = response.addons.filter(grip => grip.id == addonId).pop();
+    info("got addon actor for ID: " + addonId);
+    deferred.resolve(addonTargetActor);
+  });
+
+  return deferred.promise;
+}
+
+function removeAddon(addon) {
+  info("Removing addon.");
+
+  const deferred = getDeferredPromise().defer();
+
+  const listener = {
+    onUninstalled: function(uninstalledAddon) {
+      if (uninstalledAddon != addon) {
+        return;
+      }
+      AddonManager.removeAddonListener(listener);
+      deferred.resolve();
+    },
+  };
+  AddonManager.addAddonListener(listener);
+  addon.uninstall();
+
+  return deferred.promise;
+}
--- a/devtools/client/shared/test/helper_workers.js
+++ b/devtools/client/shared/test/helper_workers.js
@@ -7,23 +7,26 @@
 /* import-globals-from ../../debugger/new/test/mochitest/helpers.js */
 /* import-globals-from ../../debugger/new/test/mochitest/helpers/context.js */
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/debugger/new/test/mochitest/helpers.js",
   this);
 
 var { DebuggerServer } = require("devtools/server/main");
 var { DebuggerClient } = require("devtools/shared/client/debugger-client");
-
 var { Toolbox } = require("devtools/client/framework/toolbox");
 
 const FRAME_SCRIPT_URL = getRootDirectory(gTestPath) + "code_frame-script.js";
 
 var nextId = 0;
 
+/**
+ * Returns a thenable promise
+ * @return {Promise}
+ */
 function getDeferredPromise() {
   // Override promise with deprecated-sync-thenables
   const promise = require("devtools/shared/deprecated-sync-thenables");
   return promise;
 }
 
 function jsonrpc(tab, method, params) {
   return new Promise(function(resolve, reject) {
@@ -63,16 +66,34 @@ function terminateWorkerInTab(tab, url) 
 }
 
 function postMessageToWorkerInTab(tab, url, message) {
   info("Posting message to worker with url '" + url + "' in tab.");
 
   return jsonrpc(tab, "postMessageToWorker", [url, message]);
 }
 
+function generateMouseClickInTab(tab, path) {
+  info("Generating mouse click in tab.");
+
+  return jsonrpc(tab, "generateMouseClick", [path]);
+}
+
+function evalInTab(tab, string) {
+  info("Evalling string in tab.");
+
+  return jsonrpc(tab, "_eval", [string]);
+}
+
+function callInTab(tab, name) {
+  info("Calling function with name '" + name + "' in tab.");
+
+  return jsonrpc(tab, "call", [name, Array.prototype.slice.call(arguments, 2)]);
+}
+
 function connect(client) {
   info("Connecting client.");
   return client.connect();
 }
 
 function close(client) {
   info("Waiting for client to close.\n");
   return client.close();
@@ -113,16 +134,21 @@ function findWorker(workers, url) {
   return null;
 }
 
 function attachWorker(targetFront, worker) {
   info("Attaching to worker with url '" + worker.url + "'.");
   return targetFront.attachWorker(worker.actor);
 }
 
+function waitForWorkerListChanged(targetFront) {
+  info("Waiting for worker list to change.");
+  return targetFront.once("workerListChanged");
+}
+
 function attachThread(workerTargetFront, options) {
   info("Attaching to thread.");
   return workerTargetFront.attachThread(options);
 }
 
 async function waitForWorkerClose(workerTargetFront) {
   info("Waiting for worker to close.");
   await workerTargetFront.once("close");
@@ -216,8 +242,44 @@ this.removeTab = function removeTab(tab,
   tabContainer.addEventListener("TabClose", function() {
     info("Tab removed and finished closing.");
     deferred.resolve();
   }, {once: true});
 
   targetBrowser.removeTab(tab);
   return deferred.promise;
 };
+
+async function attachTargetActorForUrl(client, url) {
+  const grip = await getTargetActorForUrl(client, url);
+  const [ response, front ] = await client.attachTarget(grip.actor);
+  return [grip, response, front];
+}
+
+async function attachThreadActorForUrl(client, url) {
+  const [, response] = await attachTargetActorForUrl(client, url);
+  const [, threadClient] = await client.attachThread(response.threadActor);
+  await threadClient.resume();
+  return threadClient;
+}
+
+function getTargetActorForUrl(client, url) {
+  const deferred = getDeferredPromise().defer();
+
+  client.listTabs().then(response => {
+    const targetActor = response.tabs.filter(grip => grip.url == url).pop();
+    deferred.resolve(targetActor);
+  });
+
+  return deferred.promise;
+}
+
+function pushPrefs(...aPrefs) {
+  const deferred = getDeferredPromise().defer();
+  SpecialPowers.pushPrefEnv({"set": aPrefs}, deferred.resolve);
+  return deferred.promise;
+}
+
+function popPrefs() {
+  const deferred = getDeferredPromise().defer();
+  SpecialPowers.popPrefEnv(deferred.resolve);
+  return deferred.promise;
+}
--- a/dom/base/ContentFrameMessageManager.h
+++ b/dom/base/ContentFrameMessageManager.h
@@ -4,17 +4,16 @@
  * 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/. */
 
 #ifndef mozilla_dom_ContentFrameMessageManager_h
 #define mozilla_dom_ContentFrameMessageManager_h
 
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/MessageManagerGlobal.h"
-#include "mozilla/dom/ResolveSystemBinding.h"
 #include "nsContentUtils.h"
 #include "xpcpublic.h"
 
 namespace mozilla {
 namespace dom {
 
 #define NS_CONTENTFRAMEMESSAGEMANAGER_IID \
 { 0x97e192a6, 0xab7a, 0x4c8f, \
--- a/dom/base/ContentProcessMessageManager.cpp
+++ b/dom/base/ContentProcessMessageManager.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ContentProcessMessageManager.h"
 
 #include "nsContentCID.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/MessageManagerBinding.h"
 #include "mozilla/dom/ParentProcessMessageManager.h"
-#include "mozilla/dom/ResolveSystemBinding.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/ipc/SharedMap.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 bool ContentProcessMessageManager::sWasCreated = false;
 
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -42,17 +42,16 @@
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/MessageManagerBinding.h"
 #include "mozilla/dom/MessagePort.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentProcessMessageManager.h"
 #include "mozilla/dom/ParentProcessMessageManager.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/ProcessMessageManager.h"
-#include "mozilla/dom/ResolveSystemBinding.h"
 #include "mozilla/dom/SameProcessMessageQueue.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/ipc/SharedMap.h"
 #include "mozilla/dom/ipc/StructuredCloneData.h"
 #include "mozilla/dom/DOMStringList.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "mozilla/recordreplay/ParentIPC.h"
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -51,17 +51,16 @@
 #include "mozilla/dom/HTMLElementBinding.h"
 #include "mozilla/dom/HTMLEmbedElementBinding.h"
 #include "mozilla/dom/XULElementBinding.h"
 #include "mozilla/dom/XULFrameElementBinding.h"
 #include "mozilla/dom/XULMenuElementBinding.h"
 #include "mozilla/dom/XULPopupElementBinding.h"
 #include "mozilla/dom/XULTextElementBinding.h"
 #include "mozilla/dom/Promise.h"
-#include "mozilla/dom/ResolveSystemBinding.h"
 #include "mozilla/dom/WebIDLGlobalNameHash.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "mozilla/dom/XrayExpandoClass.h"
 #include "mozilla/dom/XULScrollElementBinding.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "ipc/ErrorIPCUtils.h"
 #include "mozilla/UseCounter.h"
@@ -3507,39 +3506,16 @@ UnwrapWindowProxyImpl(JSContext* cx,
   nsresult rv = UnwrapArg<nsPIDOMWindowInner>(cx, src, getter_AddRefs(inner));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsPIDOMWindowOuter> outer = inner->GetOuterWindow();
   outer.forget(ppArg);
   return NS_OK;
 }
 
-bool
-SystemGlobalResolve(JSContext* cx, JS::Handle<JSObject*> obj,
-                    JS::Handle<jsid> id, bool* resolvedp)
-{
-  if (!ResolveGlobal(cx, obj, id, resolvedp)) {
-    return false;
-  }
-
-  if (*resolvedp) {
-    return true;
-  }
-
-  return ResolveSystemBinding(cx, obj, id, resolvedp);
-}
-
-bool
-SystemGlobalEnumerate(JSContext* cx, JS::Handle<JSObject*> obj)
-{
-  bool ignored = false;
-  return JS_EnumerateStandardClasses(cx, obj) &&
-         ResolveSystemBinding(cx, obj, JSID_VOIDHANDLE, &ignored);
-}
-
 template<decltype(JS::NewMapObject) Method>
 bool
 GetMaplikeSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
                                size_t aSlotIndex,
                                JS::MutableHandle<JSObject*> aBackingObj,
                                bool* aBackingObjCreated)
 {
   JS::Rooted<JSObject*> reflector(aCx);
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -13571,131 +13571,16 @@ class CGRegisterWorkletBindings(CGAbstra
                     + condition)
             conditions.append(condition)
         lines = [CGIfWrapper(CGGeneric("return false;\n"), condition) for
                  condition in conditions]
         lines.append(CGGeneric("return true;\n"))
         return CGList(lines, "\n").define()
 
 
-class CGSystemBindingInitIds(CGAbstractMethod):
-    def __init__(self):
-        CGAbstractMethod.__init__(self, None, 'SystemBindingInitIds', 'bool',
-                                  [Argument('JSContext*', 'aCx')])
-
-    def definition_body(self):
-        return dedent("""
-            MOZ_ASSERT(NS_IsMainThread());
-
-            if (!idsInited) {
-              // We can't use range-based for because we need the index to call IdString.
-              for (uint32_t i = 0; i < ArrayLength(properties); ++i) {
-                if (!properties[i].id.init(aCx, IdString(i))) {
-                  return false;
-                }
-              }
-              idsInited = true;
-            }
-
-            return true;
-            """)
-
-
-class CGResolveSystemBinding(CGAbstractMethod):
-    def __init__(self):
-        CGAbstractMethod.__init__(self, None, 'ResolveSystemBinding', 'bool',
-                                  [Argument('JSContext*', 'aCx'),
-                                   Argument('JS::Handle<JSObject*>', 'aObj'),
-                                   Argument('JS::Handle<jsid>', 'aId'),
-                                   Argument('bool*', 'aResolvedp')])
-
-    def definition_body(self):
-        return dedent("""
-            MOZ_ASSERT(NS_IsMainThread());
-            MOZ_ASSERT(idsInited);
-
-            if (JSID_IS_VOID(aId)) {
-              for (const auto& property : properties) {
-                if (!property.enabled || property.enabled(aCx, aObj)) {
-                  if (!property.define(aCx)) {
-                    return false;
-                  }
-                  *aResolvedp = true;
-                }
-              }
-              return true;
-            }
-
-            for (const auto& property : properties) {
-              if (property.id == aId) {
-                if (!property.enabled || property.enabled(aCx, aObj)) {
-                  if (!property.define(aCx)) {
-                    return false;
-                  }
-                  *aResolvedp = true;
-                  break;
-                }
-              }
-            }
-            return true;
-            """)
-
-
-class CGMayResolveAsSystemBindingName(CGAbstractMethod):
-    def __init__(self):
-        CGAbstractMethod.__init__(self, None, 'MayResolveAsSystemBindingName', 'bool',
-                                  [Argument('jsid', 'aId')])
-
-    def definition_body(self):
-        return dedent("""
-            MOZ_ASSERT(NS_IsMainThread());
-            MOZ_ASSERT(idsInited);
-
-            for (const auto& property : properties) {
-              if (aId == property.id) {
-                return true;
-              }
-            }
-            return false;
-            """)
-
-
-class CGGetSystemBindingNames(CGAbstractMethod):
-    def __init__(self):
-        CGAbstractMethod.__init__(self, None, 'GetSystemBindingNames', 'void',
-                                  [Argument('JSContext*', 'aCx'),
-                                   Argument('JS::Handle<JSObject*>', 'aObj'),
-                                   Argument('JS::AutoIdVector&', 'aNames'),
-                                   Argument('bool', 'aEnumerableOnly'),
-                                   Argument('mozilla::ErrorResult&', 'aRv')])
-
-    def definition_body(self):
-        return dedent("""
-            MOZ_ASSERT(NS_IsMainThread());
-
-            if (aEnumerableOnly) {
-              return;
-            }
-
-            if (!SystemBindingInitIds(aCx)) {
-              aRv.NoteJSContextException(aCx);
-              return;
-            }
-
-            for (const auto& property : properties) {
-              if (!property.enabled || property.enabled(aCx, aObj)) {
-                if (!aNames.append(property.id)) {
-                  aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-                  return;
-                }
-              }
-            }
-            """)
-
-
 def getGlobalNames(config):
     names = []
     for desc in config.getDescriptors(registersGlobalNamesOnWindow=True):
         names.append((desc.name, desc))
         names.extend((n.identifier.name, desc) for n in desc.interface.namedConstructors)
     return names
 
 class CGGlobalNames(CGGeneric):
@@ -17255,80 +17140,16 @@ class GlobalGenRoots():
 
         # Add include guards.
         curr = CGIncludeGuard('RegisterWorkletBindings', curr)
 
         # Done.
         return curr
 
     @staticmethod
-    def ResolveSystemBinding(config):
-        curr = CGList([], "\n")
-
-        descriptors = config.getDescriptors(hasInterfaceObject=True,
-                                            isExposedInWindow=True,
-                                            register=True)
-        properties = [desc.name for desc in descriptors]
-
-        curr.append(CGStringTable("IdString", properties, static=True))
-
-        initValues = []
-        for desc in descriptors:
-            bindingNS = toBindingNamespace(desc.name)
-            if desc.isExposedConditionally():
-                enabled = "%s::ConstructorEnabled" % bindingNS
-            else:
-                enabled = "nullptr"
-            define = "%s::GetConstructorObject" % bindingNS
-            initValues.append("{ %s, %s },\n" % (enabled, define))
-        curr.append(CGGeneric(fill("""
-            struct SystemProperty
-            {
-              WebIDLGlobalNameHash::ConstructorEnabled enabled;
-              ProtoGetter define;
-              PinnedStringId id;
-            };
-
-            static SystemProperty properties[] = {
-              $*{init}
-            };
-
-            static bool idsInited = false;
-            """,
-            init="".join(initValues))))
-
-        curr.append(CGSystemBindingInitIds())
-        curr.append(CGResolveSystemBinding())
-        curr.append(CGMayResolveAsSystemBindingName())
-        curr.append(CGGetSystemBindingNames())
-
-        # Wrap all of that in our namespaces.
-        curr = CGNamespace.build(['mozilla', 'dom'],
-                                 CGWrapper(curr, post='\n'))
-        curr = CGWrapper(curr, post='\n')
-
-        # Add the includes
-        defineIncludes = [CGHeaders.getDeclarationFilename(desc.interface)
-                          for desc in config.getDescriptors(hasInterfaceObject=True,
-                                                            register=True,
-                                                            isExposedInWindow=True)]
-        defineIncludes.append("nsThreadUtils.h")  # For NS_IsMainThread
-        defineIncludes.append("js/Id.h")  # For jsid
-        defineIncludes.append("mozilla/dom/WebIDLGlobalNameHash.h")
-
-        curr = CGHeaders([], [], [], [], [], defineIncludes,
-                         'ResolveSystemBinding', curr)
-
-        # Add include guards.
-        curr = CGIncludeGuard('ResolveSystemBinding', curr)
-
-        # Done.
-        return curr
-
-    @staticmethod
     def UnionTypes(config):
         unionTypes = UnionsForFile(config, None)
         (includes, implincludes, declarations,
          traverseMethods, unlinkMethods,
          unionStructs) = UnionTypes(unionTypes, config)
 
         unions = CGList(traverseMethods +
                         unlinkMethods +
--- a/dom/bindings/WebIDLGlobalNameHash.cpp
+++ b/dom/bindings/WebIDLGlobalNameHash.cpp
@@ -201,10 +201,84 @@ WebIDLGlobalNameHash::GetNames(JSContext
         return false;
       }
     }
   }
 
   return true;
 }
 
+/* static */
+bool
+WebIDLGlobalNameHash::ResolveForSystemGlobal(JSContext* aCx,
+                                             JS::Handle<JSObject*> aObj,
+                                             JS::Handle<jsid> aId,
+                                             bool* aResolvedp)
+{
+  MOZ_ASSERT(JS_IsGlobalObject(aObj));
+
+  // First we try to resolve standard classes.
+  if (!JS_ResolveStandardClass(aCx, aObj, aId, aResolvedp)) {
+    return false;
+  }
+  if (*aResolvedp) {
+    return true;
+  }
+
+  // We don't resolve any non-string entries.
+  if (!JSID_IS_STRING(aId)) {
+    return true;
+  }
+
+  // XXX(nika): In the Window case, we unwrap our global object here to handle
+  // XRays. I don't think we ever create xrays to system globals, so I believe
+  // we can skip this step.
+  MOZ_ASSERT(!xpc::WrapperFactory::IsXrayWrapper(aObj), "Xrays not supported!");
+
+  // Look up the corresponding entry in the name table, and resolve if enabled.
+  const WebIDLNameTableEntry* entry = GetEntry(JSID_TO_FLAT_STRING(aId));
+  if (entry && (!entry->mEnabled || entry->mEnabled(aCx, aObj))) {
+    if (NS_WARN_IF(!GetPerInterfaceObjectHandle(aCx, entry->mConstructorId,
+                                                entry->mCreate,
+                                                /* aDefineOnGlobal = */ true))) {
+      return Throw(aCx, NS_ERROR_FAILURE);
+    }
+
+    *aResolvedp = true;
+  }
+  return true;
+}
+
+/* static */
+bool
+WebIDLGlobalNameHash::NewEnumerateSystemGlobal(JSContext* aCx,
+                                               JS::Handle<JSObject*> aObj,
+                                               JS::AutoIdVector& aProperties,
+                                               bool aEnumerableOnly)
+{
+  MOZ_ASSERT(JS_IsGlobalObject(aObj));
+
+  if (!JS_NewEnumerateStandardClasses(aCx, aObj, aProperties, aEnumerableOnly)) {
+    return false;
+  }
+
+  // All properties defined on our global are non-enumerable, so we can skip
+  // remaining properties.
+  if (aEnumerableOnly) {
+    return true;
+  }
+
+  // Enumerate all entries & add enabled ones.
+  for (size_t i = 0; i < sCount; ++i) {
+    const WebIDLNameTableEntry& entry = sEntries[i];
+    if (!entry.mEnabled || entry.mEnabled(aCx, aObj)) {
+      JSString* str = JS_AtomizeStringN(aCx, sNames + entry.mNameOffset,
+                                        entry.mNameLength);
+      if (!str || !aProperties.append(NON_INTEGER_ATOM_TO_JSID(str))) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/bindings/WebIDLGlobalNameHash.h
+++ b/dom/bindings/WebIDLGlobalNameHash.h
@@ -59,16 +59,27 @@ public:
     // aObj in the past (and therefore can't have been deleted).
     UnresolvedNamesOnly
   };
   // Returns false if an exception has been thrown on aCx.
   static bool GetNames(JSContext* aCx, JS::Handle<JSObject*> aObj,
                        NameType aNameType,
                        JS::AutoIdVector& aNames);
 
+  // Helpers for resolving & enumerating names on the system global.
+  // NOTE: These are distinct as it currently lacks a ProtoAndIfaceCache, and is
+  // an XPCOM global.
+  static bool ResolveForSystemGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
+                                     JS::Handle<jsid> aId, bool* aResolvedp);
+
+  static bool NewEnumerateSystemGlobal(JSContext* aCx,
+                                       JS::Handle<JSObject*> aObj,
+                                       JS::AutoIdVector& aProperties,
+                                       bool aEnumerableOnly);
+
 private:
   friend struct WebIDLNameTableEntry;
 
   // Look up an entry by key name. `nullptr` if the entry was not found.
   // The impl of GetEntry is generated by Codegen.py in RegisterBindings.cpp
   static const WebIDLNameTableEntry* GetEntry(JSFlatString* aKey);
 
   // The total number of names in the hash.
--- a/dom/bindings/mozwebidlcodegen/__init__.py
+++ b/dom/bindings/mozwebidlcodegen/__init__.py
@@ -129,28 +129,26 @@ class WebIDLCodegenManager(LoggingMixin)
     GLOBAL_DECLARE_FILES = {
         'GeneratedAtomList.h',
         'GeneratedEventList.h',
         'PrototypeList.h',
         'RegisterBindings.h',
         'RegisterWorkerBindings.h',
         'RegisterWorkerDebuggerBindings.h',
         'RegisterWorkletBindings.h',
-        'ResolveSystemBinding.h',
         'UnionConversions.h',
         'UnionTypes.h',
     }
 
     # Global parser derived definition files.
     GLOBAL_DEFINE_FILES = {
         'RegisterBindings.cpp',
         'RegisterWorkerBindings.cpp',
         'RegisterWorkerDebuggerBindings.cpp',
         'RegisterWorkletBindings.cpp',
-        'ResolveSystemBinding.cpp',
         'UnionTypes.cpp',
         'PrototypeList.cpp',
     }
 
     def __init__(self, config_path, webidl_root, inputs, exported_header_dir,
                  codegen_dir, state_path, cache_dir=None, make_deps_path=None,
                  make_deps_target=None):
         """Create an instance that manages WebIDLs in the build system.
--- a/dom/security/featurepolicy/FeaturePolicy.cpp
+++ b/dom/security/featurepolicy/FeaturePolicy.cpp
@@ -31,35 +31,35 @@ void
 FeaturePolicy::InheritPolicy(FeaturePolicy* aParentPolicy)
 {
   MOZ_ASSERT(aParentPolicy);
 
   mInheritedDeniedFeatureNames.Clear();
 
   RefPtr<FeaturePolicy> dest = this;
   RefPtr<FeaturePolicy> src = aParentPolicy;
-  nsCOMPtr<nsIPrincipal> origin = mDefaultOrigin;
-  FeaturePolicyUtils::ForEachFeature([dest, src, origin](const char* aFeatureName) {
+  FeaturePolicyUtils::ForEachFeature([dest, src](const char* aFeatureName) {
     nsString featureName;
     featureName.AppendASCII(aFeatureName);
 
     // If the destination has a declared feature (via the HTTP header or 'allow'
-    // attribute) we allow the feature only if both parent FeaturePolicy and this
-    // one allow the current origin.
-    if (dest->HasDeclaredFeature(featureName)) {
-      if (!dest->AllowsFeatureInternal(featureName, origin) ||
-          !src->AllowsFeatureInternal(featureName, origin)) {
+    // attribute) we allow the feature if the destination allows it and the
+    // parent allows its origin or the destinations' one.
+    if (dest->HasDeclaredFeature(featureName) &&
+        dest->AllowsFeatureInternal(featureName, dest->mDefaultOrigin)) {
+      if (!src->AllowsFeatureInternal(featureName, src->mDefaultOrigin) &&
+          !src->AllowsFeatureInternal(featureName, dest->mDefaultOrigin)) {
         dest->SetInheritedDeniedFeature(featureName);
       }
       return;
     }
 
     // If there was not a declared feature, we allow the feature if the parent
     // FeaturePolicy allows the current origin.
-    if (!src->AllowsFeatureInternal(featureName, origin)) {
+    if (!src->AllowsFeatureInternal(featureName, dest->mDefaultOrigin)) {
       dest->SetInheritedDeniedFeature(featureName);
     }
   });
 }
 
 void
 FeaturePolicy::SetInheritedDeniedFeature(const nsAString& aFeatureName)
 {
--- a/gfx/layers/apz/src/APZUpdater.cpp
+++ b/gfx/layers/apz/src/APZUpdater.cpp
@@ -441,20 +441,22 @@ APZUpdater::IsUpdaterThread() const
   return CompositorThreadHolder::IsInCompositorThread();
 }
 
 void
 APZUpdater::RunOnControllerThread(LayersId aLayersId, already_AddRefed<Runnable> aTask)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
+  RefPtr<Runnable> task = aTask;
+
   RunOnUpdaterThread(aLayersId, NewRunnableFunction(
       "APZUpdater::RunOnControllerThread",
       &APZThreadUtils::RunOnControllerThread,
-      std::move(aTask)));
+      std::move(task)));
 }
 
 bool
 APZUpdater::UsingWebRenderUpdaterThread() const
 {
   return mIsUsingWebRender;
 }
 
--- a/gfx/layers/apz/util/APZThreadUtils.cpp
+++ b/gfx/layers/apz/util/APZThreadUtils.cpp
@@ -36,19 +36,19 @@ APZThreadUtils::AssertOnControllerThread
   if (!GetThreadAssertionsEnabled()) {
     return;
   }
 
   MOZ_ASSERT(sControllerThread == MessageLoop::current());
 }
 
 /*static*/ void
-APZThreadUtils::RunOnControllerThread(already_AddRefed<Runnable> aTask)
+APZThreadUtils::RunOnControllerThread(RefPtr<Runnable>&& aTask)
 {
-  RefPtr<Runnable> task = aTask;
+  RefPtr<Runnable> task = std::move(aTask);
 
   if (!sControllerThread) {
     // Could happen on startup
     NS_WARNING("Dropping task posted to controller thread");
     return;
   }
 
   if (sControllerThread == MessageLoop::current()) {
--- a/gfx/layers/apz/util/APZThreadUtils.h
+++ b/gfx/layers/apz/util/APZThreadUtils.h
@@ -40,17 +40,17 @@ public:
    */
   static void AssertOnControllerThread();
 
   /**
    * Run the given task on the APZ "controller thread" for this platform. If
    * this function is called from the controller thread itself then the task is
    * run immediately without getting queued.
    */
-  static void RunOnControllerThread(already_AddRefed<Runnable> aTask);
+  static void RunOnControllerThread(RefPtr<Runnable>&& aTask);
 
   /**
    * Returns true if currently on APZ "controller thread".
    */
   static bool IsControllerThread();
 };
 
 // A base class for GenericNamedTimerCallback<Function>.
--- a/gfx/layers/wr/ClipManager.cpp
+++ b/gfx/layers/wr/ClipManager.cpp
@@ -287,19 +287,22 @@ ClipManager::DefineScrollLayers(const Ac
     // If we've already defined this scroll layer before, we can early-exit
     return scrollId;
   }
   // Recurse to define the ancestors
   Maybe<wr::WrClipId> ancestorScrollId = DefineScrollLayers(aASR->mParent, aItem, aSc);
 
   Maybe<ScrollMetadata> metadata = aASR->mScrollableFrame->ComputeScrollMetadata(
       mManager, aItem->ReferenceFrame(), Nothing(), nullptr);
-  MOZ_ASSERT(metadata);
+  if (!metadata) {
+    MOZ_ASSERT_UNREACHABLE("Expected scroll metadata to be available!");
+    return ancestorScrollId;
+  }
+
   FrameMetrics& metrics = metadata->GetMetrics();
-
   if (!metrics.IsScrollable()) {
     // This item is a scrolling no-op, skip over it in the ASR chain.
     return ancestorScrollId;
   }
 
   LayoutDeviceRect contentRect =
       metrics.GetExpandedScrollableRect() * metrics.GetDevPixelsPerCSSPixel();
   LayoutDeviceRect clipBounds =
--- a/js/xpconnect/idl/nsIXPCScriptable.idl
+++ b/js/xpconnect/idl/nsIXPCScriptable.idl
@@ -74,17 +74,18 @@ interface nsIXPCScriptable : nsISupports
     void   preCreate(in nsISupports nativeObj, in JSContextPtr cx,
                      in JSObjectPtr globalObj, out JSObjectPtr parentObj);
 
     boolean enumerate(in nsIXPConnectWrappedNative wrapper,
                      in JSContextPtr cx, in JSObjectPtr obj);
 
     boolean newEnumerate(in nsIXPConnectWrappedNative wrapper,
                         in JSContextPtr cx, in JSObjectPtr obj,
-                        in JSAutoIdVector properties);
+                        in JSAutoIdVector properties,
+                        in boolean enumerableOnly);
 
     boolean resolve(in nsIXPConnectWrappedNative wrapper,
                     in JSContextPtr cx, in JSObjectPtr obj, in jsid id,
                     out boolean resolvedp);
 
     void   finalize(in nsIXPConnectWrappedNative wrapper,
                     in JSFreeOpPtr fop, in JSObjectPtr obj);
 
--- a/js/xpconnect/public/xpc_map_end.h
+++ b/js/xpconnect/public/xpc_map_end.h
@@ -60,17 +60,17 @@ XPC_MAP_CLASSNAME::GetJSClass()
 /**************************************************************/
 
 #if !((XPC_MAP_FLAGS) & XPC_SCRIPTABLE_WANT_PRECREATE)
 NS_IMETHODIMP XPC_MAP_CLASSNAME::PreCreate(nsISupports* nativeObj, JSContext * cx, JSObject * globalObj, JSObject * *parentObj)
     {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
 #if !((XPC_MAP_FLAGS) & XPC_SCRIPTABLE_WANT_NEWENUMERATE)
-NS_IMETHODIMP XPC_MAP_CLASSNAME::NewEnumerate(nsIXPConnectWrappedNative* wrapper, JSContext * cx, JSObject * obj, JS::AutoIdVector& properties, bool* _retval)
+NS_IMETHODIMP XPC_MAP_CLASSNAME::NewEnumerate(nsIXPConnectWrappedNative* wrapper, JSContext * cx, JSObject * obj, JS::AutoIdVector& properties, bool enumerableOnly, bool* _retval)
     {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
 #if !((XPC_MAP_FLAGS) & XPC_SCRIPTABLE_WANT_ENUMERATE)
 NS_IMETHODIMP XPC_MAP_CLASSNAME::Enumerate(nsIXPConnectWrappedNative* wrapper, JSContext * cx, JSObject * obj, bool* _retval)
     {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -202,16 +202,17 @@ NS_IMPL_ISUPPORTS(nsXPCComponents_Interf
                        XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE)
 #include "xpc_map_end.h" /* This will #undef the above */
 
 
 NS_IMETHODIMP
 nsXPCComponents_Interfaces::NewEnumerate(nsIXPConnectWrappedNative* wrapper,
                                          JSContext* cx, JSObject* obj,
                                          JS::AutoIdVector& properties,
+                                         bool enumerableOnly,
                                          bool* _retval)
 {
 
     if (!properties.reserve(nsXPTInterfaceInfo::InterfaceCount())) {
         *_retval = false;
         return NS_OK;
     }
 
@@ -389,16 +390,17 @@ NS_IMPL_ISUPPORTS(nsXPCComponents_Interf
                        XPC_SCRIPTABLE_WANT_NEWENUMERATE | \
                        XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE)
 #include "xpc_map_end.h" /* This will #undef the above */
 
 NS_IMETHODIMP
 nsXPCComponents_InterfacesByID::NewEnumerate(nsIXPConnectWrappedNative* wrapper,
                                              JSContext* cx, JSObject* obj,
                                              JS::AutoIdVector& properties,
+                                             bool enumerableOnly,
                                              bool* _retval)
 {
 
     if (!properties.reserve(nsXPTInterfaceInfo::InterfaceCount())) {
         *_retval = false;
         return NS_OK;
     }
 
@@ -584,16 +586,17 @@ NS_IMPL_ISUPPORTS(nsXPCComponents_Classe
                        XPC_SCRIPTABLE_WANT_NEWENUMERATE | \
                        XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE)
 #include "xpc_map_end.h" /* This will #undef the above */
 
 NS_IMETHODIMP
 nsXPCComponents_Classes::NewEnumerate(nsIXPConnectWrappedNative* wrapper,
                                       JSContext* cx, JSObject* obj,
                                       JS::AutoIdVector& properties,
+                                      bool enumerableOnly,
                                       bool* _retval)
 {
     nsCOMPtr<nsIComponentRegistrar> compMgr;
     if (NS_FAILED(NS_GetComponentRegistrar(getter_AddRefs(compMgr))) || !compMgr) {
         return NS_ERROR_UNEXPECTED;
     }
 
     nsCOMPtr<nsISimpleEnumerator> e;
@@ -779,16 +782,17 @@ NS_IMPL_ISUPPORTS(nsXPCComponents_Classe
                        XPC_SCRIPTABLE_WANT_NEWENUMERATE | \
                        XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE)
 #include "xpc_map_end.h" /* This will #undef the above */
 
 NS_IMETHODIMP
 nsXPCComponents_ClassesByID::NewEnumerate(nsIXPConnectWrappedNative* wrapper,
                                           JSContext* cx, JSObject* obj,
                                           JS::AutoIdVector& properties,
+                                          bool enumerableOnly,
                                           bool* _retval)
 {
 
     nsCOMPtr<nsIComponentRegistrar> compMgr;
     if (NS_FAILED(NS_GetComponentRegistrar(getter_AddRefs(compMgr))) || !compMgr) {
         return NS_ERROR_UNEXPECTED;
     }
 
@@ -988,16 +992,17 @@ NS_IMPL_ISUPPORTS(nsXPCComponents_Result
                        XPC_SCRIPTABLE_WANT_NEWENUMERATE | \
                        XPC_SCRIPTABLE_ALLOW_PROP_MODS_DURING_RESOLVE)
 #include "xpc_map_end.h" /* This will #undef the above */
 
 NS_IMETHODIMP
 nsXPCComponents_Results::NewEnumerate(nsIXPConnectWrappedNative* wrapper,
                                       JSContext* cx, JSObject* obj,
                                       JS::AutoIdVector& properties,
+                                      bool enumerableOnly,
                                       bool* _retval)
 {
     const char* name;
     const void* iter = nullptr;
     while (nsXPCException::IterateNSResults(nullptr, &name, nullptr, &iter)) {
         RootedString idstr(cx, JS_NewStringCopyZ(cx, name));
         if (!idstr) {
             *_retval = false;
--- a/js/xpconnect/src/XPCRuntimeService.cpp
+++ b/js/xpconnect/src/XPCRuntimeService.cpp
@@ -5,29 +5,36 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "xpcprivate.h"
 
 #include "nsContentUtils.h"
 #include "BackstagePass.h"
 #include "nsIPrincipal.h"
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/WebIDLGlobalNameHash.h"
+
+using namespace mozilla::dom;
 
 NS_IMPL_ISUPPORTS(BackstagePass,
                   nsIXPCScriptable,
                   nsIGlobalObject,
                   nsIClassInfo,
                   nsIScriptObjectPrincipal,
                   nsISupportsWeakReference)
 
+// XXX(nika): It appears we don't have support for mayresolve hooks in
+// nsIXPCScriptable, and I don't really want to add it because I'd rather just
+// kill nsIXPCScriptable alltogether, so we don't use it here.
+
 // The nsIXPCScriptable map declaration that will generate stubs for us...
 #define XPC_MAP_CLASSNAME         BackstagePass
 #define XPC_MAP_QUOTED_CLASSNAME "BackstagePass"
 #define XPC_MAP_FLAGS (XPC_SCRIPTABLE_WANT_RESOLVE | \
-                       XPC_SCRIPTABLE_WANT_ENUMERATE | \
+                       XPC_SCRIPTABLE_WANT_NEWENUMERATE | \
                        XPC_SCRIPTABLE_WANT_FINALIZE | \
                        XPC_SCRIPTABLE_WANT_PRECREATE | \
                        XPC_SCRIPTABLE_USE_JSSTUB_FOR_ADDPROPERTY |  \
                        XPC_SCRIPTABLE_USE_JSSTUB_FOR_DELPROPERTY |  \
                        XPC_SCRIPTABLE_DONT_ENUM_QUERY_INTERFACE |  \
                        XPC_SCRIPTABLE_IS_GLOBAL_OBJECT |  \
                        XPC_SCRIPTABLE_DONT_REFLECT_INTERFACE_NAMES)
 #include "xpc_map_end.h" /* This will #undef the above */
@@ -53,27 +60,31 @@ BackstagePass::SetGlobalObject(JSObject*
 NS_IMETHODIMP
 BackstagePass::Resolve(nsIXPConnectWrappedNative* wrapper,
                        JSContext * cx, JSObject * objArg,
                        jsid idArg, bool* resolvedp,
                        bool* _retval)
 {
     JS::RootedObject obj(cx, objArg);
     JS::RootedId id(cx, idArg);
-    *_retval = mozilla::dom::SystemGlobalResolve(cx, obj, id, resolvedp);
+    *_retval =
+        WebIDLGlobalNameHash::ResolveForSystemGlobal(cx, obj, id, resolvedp);
     return *_retval ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
-BackstagePass::Enumerate(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
-                         JSObject* objArg, bool* _retval)
+BackstagePass::NewEnumerate(nsIXPConnectWrappedNative* wrapper, JSContext* cx,
+                            JSObject* objArg, JS::AutoIdVector& properties,
+                            bool enumerableOnly, bool* _retval)
 {
     JS::RootedObject obj(cx, objArg);
-    *_retval = mozilla::dom::SystemGlobalEnumerate(cx, obj);
-    return *_retval ? NS_OK : NS_ERROR_FAILURE;
+    *_retval =
+        WebIDLGlobalNameHash::NewEnumerateSystemGlobal(cx, obj, properties,
+                                                       enumerableOnly);
+    return *_retval ?  NS_OK : NS_ERROR_FAILURE;
 }
 
 /***************************************************************************/
 NS_IMETHODIMP
 BackstagePass::GetInterfaces(uint32_t* aCount, nsIID * **aArray)
 {
     *aCount = 2;
     nsIID** array = static_cast<nsIID**>(moz_xmalloc(2 * sizeof(nsIID*)));
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -936,17 +936,18 @@ XPC_WN_NewEnumerate(JSContext* cx, Handl
         return Throw(NS_ERROR_XPC_BAD_OP_ON_WN_PROTO, cx);
     }
 
     if (!XPC_WN_Shared_Enumerate(cx, obj)) {
         return false;
     }
 
     bool retval = true;
-    nsresult rv = scr->NewEnumerate(wrapper, cx, obj, properties, &retval);
+    nsresult rv = scr->NewEnumerate(wrapper, cx, obj, properties,
+                                    enumerableOnly, &retval);
     if (NS_FAILED(rv)) {
         return Throw(rv, cx);
     }
     return retval;
 }
 
 /***************************************************************************/
 /***************************************************************************/
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -22,17 +22,16 @@
 #include "WrapperFactory.h"
 #include "AccessCheck.h"
 
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/DOMPrefs.h"
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/Promise.h"
-#include "mozilla/dom/ResolveSystemBinding.h"
 
 #include "nsDOMMutationObserver.h"
 #include "nsICycleCollectorListener.h"
 #include "nsCycleCollector.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsScriptSecurityManager.h"
 #include "nsIPermissionManager.h"
@@ -575,20 +574,16 @@ InitClassesWithNewWrappedGlobal(JSContex
 {
     MOZ_ASSERT(aJSContext, "bad param");
     MOZ_ASSERT(aCOMObj, "bad param");
 
     // We pass null for the 'extra' pointer during global object creation, so
     // we need to have a principal.
     MOZ_ASSERT(aPrincipal);
 
-    if (!SystemBindingInitIds(aJSContext)) {
-      return NS_ERROR_FAILURE;
-    }
-
     InitGlobalObjectOptions(aOptions, aPrincipal);
 
     // Call into XPCWrappedNative to make a new global object, scope, and global
     // prototype.
     xpcObjectHelper helper(aCOMObj);
     MOZ_ASSERT(helper.GetScriptableFlags() & XPC_SCRIPTABLE_IS_GLOBAL_OBJECT);
     RefPtr<XPCWrappedNative> wrappedGlobal;
     nsresult rv =
--- a/layout/forms/nsFileControlFrame.cpp
+++ b/layout/forms/nsFileControlFrame.cpp
@@ -629,16 +629,21 @@ nsFileControlFrame::UpdateDisplayedValue
   uint32_t oldLength = aNotify ? 0 : text->TextLength();
   text->SetText(aValue, aNotify);
   if (!aNotify) {
     // We can't notify during Reflow so we need to tell the text frame
     // about the text content change we just did.
     if (auto* textFrame = static_cast<nsTextFrame*>(text->GetPrimaryFrame())) {
       textFrame->NotifyNativeAnonymousTextnodeChange(oldLength);
     }
+    nsBlockFrame* label = do_QueryFrame(mTextContent->GetPrimaryFrame());
+    if (label && label->LinesBegin() != label->LinesEnd()) {
+      label->AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
+      label->LinesBegin()->MarkDirty();
+    }
   }
 }
 
 nsresult
 nsFileControlFrame::SetFormProperty(nsAtom* aName,
                                     const nsAString& aValue)
 {
   if (nsGkAtoms::value == aName) {
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -214,27 +214,20 @@ nsLineLayout::BeginLineReflow(nscoord aI
 
   psd->mNoWrap = !mStyleText->WhiteSpaceCanWrapStyle() || mSuppressLineWrap;
   psd->mWritingMode = aWritingMode;
 
   // If this is the first line of a block then see if the text-indent
   // property amounts to anything.
 
   if (0 == mLineNumber && !HasPrevInFlow(mBlockReflowInput->mFrame)) {
-    const nsStyleCoord &textIndent = mStyleText->mTextIndent;
-    nscoord pctBasis = 0;
-    if (textIndent.HasPercent()) {
-      pctBasis =
-        mBlockReflowInput->GetContainingBlockContentISize(aWritingMode);
-    }
-    nscoord indent = textIndent.ComputeCoordPercentCalc(pctBasis);
-
-    mTextIndent = indent;
-
-    psd->mICoord += indent;
+    nscoord pctBasis = mBlockReflowInput->ComputedISize();
+    mTextIndent = nsLayoutUtils::ResolveToLength<false>(mStyleText->mTextIndent,
+                                                        pctBasis);
+    psd->mICoord += mTextIndent;
   }
 
   PerFrameData* pfd = NewPerFrameData(mBlockReflowInput->mFrame);
   pfd->mAscent = 0;
   pfd->mSpan = psd;
   psd->mFrame = pfd;
   nsIFrame* frame = mBlockReflowInput->mFrame;
   if (frame->IsRubyTextContainerFrame()) {
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -2040,17 +2040,17 @@ skip-if(isDebugBuild&&winWidget) == 1330
 == 1375315-10.html 1375315-10-ref.html
 == 1375315-11.html 1375315-11-ref.html
 == 1375315-12.html 1375315-12-ref.html
 == 1374062.html 1374062-ref.html
 == 1375513.html 1375513-ref.html
 == 1375674.html 1375674-ref.html
 == 1372041.html 1372041-ref.html
 == 1376092.html 1376092-ref.html
-fuzzy-if(Android&&debug,1-1,1-1) needs-focus == 1377447-1.html 1377447-1-ref.html
+fuzzy-if(Android,0-3,0-3) needs-focus == 1377447-1.html 1377447-1-ref.html
 needs-focus != 1377447-1.html 1377447-2.html
 == 1379041.html 1379041-ref.html
 == 1379696.html 1379696-ref.html
 == 1380224-1.html 1380224-1-ref.html
 == 1384065.html 1384065-ref.html
 == 1384275-1.html 1384275-1-ref.html
 == 1381821.html 1381821-ref.html
 == 1395650-1.html 1395650-1-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/file/label-min-inline-size-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>Testcase for bug 1500530</title>
+  <style type="text/css">
+html,body {
+  color:black; background-color:black; font:48pt/1 Arial; padding:0; margin:0;
+}
+
+div { width: 100px; height: 20px; margin-top: -2px; background: lime; }
+
+  </style>
+</head>
+<body>
+
+<div></div>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/file/label-min-inline-size.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>Testcase for bug 1500530</title>
+  <style type="text/css">
+html,body {
+  color:black; background-color:white; font:48pt/1 Arial; padding:0; margin:0;
+}
+
+input {
+  font-family: Arial;
+  font-size: 48pt;
+  vertical-align: top;
+  background: lime;
+}
+div { text-indent: -24ch; margin-top: -2px; }
+
+mask {
+  position: absolute;
+  left: 100px; right: 0; top: 0; bottom: 0;
+  background: black;
+}
+
+mask2 {
+  position: absolute;
+  left: 0; right: 0; top: 18px; bottom: 0;
+  background: black;
+}
+
+  </style>
+</head>
+<body>
+
+<mask></mask>
+<mask2></mask2>
+<div><input type="file"></div>
+
+
+</body>
+</html>
--- a/layout/reftests/forms/input/file/reftest.list
+++ b/layout/reftests/forms/input/file/reftest.list
@@ -1,8 +1,9 @@
 fuzzy-if(gtkWidget||webrender,0-1,0-34) fails-if(Android) == simple.html simple-ref.xul
 fuzzy-if(gtkWidget||webrender,0-1,0-17) fails-if(Android) == rtl.html rtl-ref.xul
 fuzzy-if(gtkWidget||webrender,0-1,0-34) fails-if(Android) == size.html simple-ref.xul
 fuzzy-if(gtkWidget||webrender,0-1,0-10) fails-if(Android) == background.html background-ref.xul
 fuzzy-if(gtkWidget,0-1,0-10) fails-if(Android) == style.html style-ref.xul
 != width-clip.html width-clip-ref.html
 fails-if(Android) == color-inherit.html color-inherit-ref.html
-fuzzy-if(Android,1-2,2-2) fails-if(webrender) == dynamic-max-width.html dynamic-max-width-ref.html # bug 1496542 for webrender.
+fuzzy-if(Android,1-2,2-2) fails-if(webrender&&!cocoaWidget) == dynamic-max-width.html dynamic-max-width-ref.html # bug 1496542 for webrender.
+== label-min-inline-size.html label-min-inline-size-ref.html
--- a/layout/reftests/text-indent/text-indent-parent-dynamic-ref.html
+++ b/layout/reftests/text-indent/text-indent-parent-dynamic-ref.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <html>
 <body>
 
 <div id="x" style="background: lightgreen; height: 3em; width: 500px; padding: 4px;">
-  <div style="text-indent: 200px; width: 200px; background: yellow;">X</div>
+  <div style="text-indent: 80px; width: 200px; background: yellow;">X</div>
 </div>
 
 </body>
 </html>
--- a/layout/reftests/text-indent/text-indent-single-line-percent-ref.html
+++ b/layout/reftests/text-indent/text-indent-single-line-percent-ref.html
@@ -1,14 +1,14 @@
 <!DOCTYPE html>
 <head>
 <title>text-indent test</title>
 <style type="text/css">
 div { width: 500px; }
-p { width: 300px; text-indent: 50px; }
+p { width: 300px; text-indent: 30px; }
 </style>
 </head>
 <body>
 <div>
 <p>text</p>
 </div>
 </body>
 </html>
--- a/layout/style/res/forms.css
+++ b/layout/style/res/forms.css
@@ -481,16 +481,17 @@ input[type="file"] {
   cursor: default;
 
   border: none;
   background-color: transparent;
   padding: unset;
 }
 
 input[type="file"] > label {
+  display: inline-block;
   min-inline-size: 12em;
   padding-inline-start: 5px;
   text-align: match-parent;
 
   color: unset;
   font-size: unset;
   letter-spacing: unset;
 
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-text/text-indent/text-indent-percentage-001.xht.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[text-indent-percentage-001.xht]
-  expected:
-    if debug and not webrender and e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-text/text-indent/text-indent-percentage-002.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[text-indent-percentage-002.html]
-  expected: FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-text/text-indent/text-indent-percentage-003.html.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[text-indent-percentage-003.html]
-  expected:
-    if debug and not webrender and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
-    if not debug and not webrender and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
-    if debug and not webrender and e10s and (os == "win") and (version == "10.0.15063") and (processor == "x86_64") and (bits == 64): FAIL
-    if not debug and not webrender and e10s and (os == "win") and (version == "10.0.15063") and (processor == "x86_64") and (bits == 64): FAIL
deleted file mode 100644
--- a/testing/web-platform/meta/css/css-text/text-indent/text-indent-percentage-004.html.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[text-indent-percentage-004.html]
-  expected: FAIL
--- a/testing/web-platform/meta/feature-policy/feature-policy-frame-policy-allowed-for-self.https.sub.html.ini
+++ b/testing/web-platform/meta/feature-policy/feature-policy-frame-policy-allowed-for-self.https.sub.html.ini
@@ -1,33 +1,12 @@
 [feature-policy-frame-policy-allowed-for-self.https.sub.html]
+  [Test frame policy on sandboxed iframe with allow="fullscreen https://www.web-platform.test:8443".]
+    expected: FAIL
+
   [Test frame policy on sandboxed iframe with no allow attribute.]
     expected: FAIL
 
-  [Test frame policy on cross origin iframe with allow = "*".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'none';".]
+  [Test frame policy on data: URL origin iframe with allow = "*".]
     expected: FAIL
 
-  [Test frame policy on cross origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" and header policy = "Feature-Policy: fullscreen 'self';".]
+  [Test frame policy on data: URL origin iframe with allow = "*" and allowfullscreen.]
     expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "*" and allowfullscreen.]
-    expected: FAIL
-
-  [Test frame policy on cross origin iframe with allow = "'self' https://www.web-platform.test:8443 https://www.example.com" and allowfullscreen.]
-    expected: FAIL
--- a/testing/web-platform/meta/feature-policy/feature-policy-frame-policy-allowed-for-some.https.sub.html.ini
+++ b/testing/web-platform/meta/feature-policy/feature-policy-frame-policy-allowed-for-some.https.sub.html.ini
@@ -1,28 +1,6 @@
 [feature-policy-frame-policy-allowed-for-some.https.sub.html]
-  [Test frame policy on another cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on another cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'self';".]
-    expected: FAIL
-
-  [Test frame policy on another cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on another cross origin iframe with allow = "*" and allowfullscreen.]
+  [Test frame policy on data: URL cross origin iframe with allow = "*".]
     expected: FAIL
 
-  [Test frame policy on another cross origin iframe with allow = "*".]
-    expected: FAIL
-
-  [Test frame policy on another cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen *;".]
-    expected: FAIL
-
-  [Test frame policy on another cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'self';".]
+  [Test frame policy on data: URL cross origin iframe with allow = "*" and allowfullscreen.]
     expected: FAIL
-
-  [Test frame policy on another cross origin iframe with allow = "*" and header policy = "Feature-Policy: fullscreen 'none';".]
-    expected: FAIL
-
-  [Test frame policy on another cross origin iframe with allow = "*" and allowfullscreen.]
-    expected: FAIL
-
--- a/testing/web-platform/tests/css/CSS2/css1/c547-indent-001-ref.xht
+++ b/testing/web-platform/tests/css/CSS2/css1/c547-indent-001-ref.xht
@@ -27,23 +27,17 @@
   }
 
   p.one
   {
   background-color: aqua;
   margin-top: 0em;
   }
 
-  div#X
-  {
-  float: left;
-  margin-left: 50%;
-  }
-
-  div#after-X
+  div
   {
   background-color: aqua;
   width: 25%;
   }
   ]]>
   </style>
 
  </head>
@@ -53,14 +47,12 @@
   <p class="eight">The first line of this sentence should be indented halfway across the page, but the rest of it should be flush with the normal left margin of the page.</p>
 
   <p class="one-first">Only the first line of this sentence</p>
 
   <p class="one">should be indented,<br />
   the others should all be<br />
   <em>aligned on the left</em> of the window.</p>
 
-  <div id="X">X</div>
-
-  <div id="after-X"><br />The X on the previous line should be centered across the window.</div>
+  <div><span style="padding-left:50%">X The first X in this sentence should be indented to the center of this block.</span></div>
 
  </body>
 </html>
--- a/testing/web-platform/tests/css/CSS2/css1/c547-indent-001.xht
+++ b/testing/web-platform/tests/css/CSS2/css1/c547-indent-001.xht
@@ -22,12 +22,12 @@
   </p>
   <p class="one">
    Only the first line of this sentence<br />
    should be indented,<br />
    the others should all be<br />
    <em>aligned on the left</em> of the window.
   </p>
   <div>
-   X The X on the previous line should be centered across the window.
+   X The first X in this sentence should be indented to the center of this block.
   </div>
  </body>
 </html>
--- a/testing/web-platform/tests/css/CSS2/text/text-indent-percent-001-ref.xht
+++ b/testing/web-platform/tests/css/CSS2/text/text-indent-percent-001-ref.xht
@@ -1,14 +1,14 @@
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head>
 <title>text-indent test</title>
 <style type="text/css">
 div { width: 500px; }
-p { width: 300px; text-indent: 50px; }
+p { width: 300px; text-indent: 30px; }
 </style>
 </head>
 <body>
 <div>
 <p>text</p>
 </div>
 
 
-</body></html>
\ No newline at end of file
+</body></html>
--- a/testing/web-platform/tests/css/css-text/text-indent/reference/text-indent-percentage-001-ref.xht
+++ b/testing/web-platform/tests/css/css-text/text-indent/reference/text-indent-percentage-001-ref.xht
@@ -14,21 +14,21 @@
 			.reference{
 				margin-left: 50%;
 			}
 		]]></style>
 	</head>
 	<body>
 		<p>Test passes if there is no red visible on the page.</p>
 		<div id="parent">
-			<div>X</div>
+			<div style="padding-left: 100px">X</div>
 		</div>
 		<p>Test passes if the following two text blocks look same in terms of margin-left and text-indent respectively.</p>
 		<div>
 			<span class="reference">abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz.</span><br />
 			ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ.<br />
 		</div>
 		<div>
 			<span class="reference">abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz abcdefghijklmnopqrstuvwxyz.</span><br />
 			ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ ABCDEFGHIJKLMNOPQRSTUVWXYZ.<br />
 		</div>
 	</body>
-</html>
\ No newline at end of file
+</html>
--- a/testing/web-platform/tests/css/css-text/text-indent/reference/text-indent-percentage-002-ref.html
+++ b/testing/web-platform/tests/css/css-text/text-indent/reference/text-indent-percentage-002-ref.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html lang=en>
 <meta charset="utf-8">
 <title>CSS Text Test reference</title>
 <link rel="author" title="Florian Rivoal" href="http://florian.rivoal.net/">
 <style>
+body { background: white; }
 div { padding-left: 50px; }
 </style>
 
-<p>Test passes if there is a single black X below and no red.
 <div>X</div>
--- a/testing/web-platform/tests/css/css-text/text-indent/text-indent-percentage-001.xht
+++ b/testing/web-platform/tests/css/css-text/text-indent/text-indent-percentage-001.xht
@@ -13,29 +13,29 @@
 			{
 				font: 16px/1em Ahem;
 				position: relative;
 				width: 400px;
 			}
 			#reference1
 			{
 				color: red;
-				left: 0;
+				left: 100px; /* see comments for #test1 below */
 				position: absolute;
 				top: 0;
 				z-index: -1;
 			}
 			#reference2
 			{
 				margin-left: 50%;
 			}
 			#test1
 			{
-    			text-indent: 50%;
-    			margin-left: -50%;
+    			margin-left: -50%; /* -50% * 400px = -200px which makes the inline-size of this block 600px */
+    			text-indent: 50%;  /* 50% * 600px = 300px (which is 100px from the start of #parent due to the negative margin) */
 			}
 			#test2
 			{
     			text-indent: 50%;
 			}
 		]]></style>
 	</head>
 	<body>
--- a/testing/web-platform/tests/css/css-text/text-indent/text-indent-percentage-002.html
+++ b/testing/web-platform/tests/css/css-text/text-indent/text-indent-percentage-002.html
@@ -3,25 +3,22 @@
 <meta charset="utf-8">
 <title>CSS Text Test: text-indent percentage resolution basis</title>
 <link rel="author" title="Florian Rivoal" href="http://florian.rivoal.net/">
 <link rel="help" href="https://drafts.csswg.org/css-text-3/#text-indent-property">
 <meta name="flags" content="">
 <link rel="match" href="reference/text-indent-percentage-002-ref.html">
 <meta name="assert" content="Percentages in text-indent refer to width of the element's content box">
 <style>
-section { position: absolute; }
+body { background: white; }
 section, div {
   border-right: 10px solid white;
   margin-right: 10px;
   padding-right: 10px;
 }
 div {
   box-sizing: border-box;
   width: 120px;
 }
-.test div { text-indent: 50%; color: red; }
-.ref div { text-indent: 50px; }
+.test div { text-indent: 50%; }
 </style>
 
-<p>Test passes if there is a single black X below and no red.
 <section class=test><div>X</div></section>
-<section class=ref><div>X</div></section>
--- a/testing/web-platform/tests/css/css-text/text-indent/text-indent-percentage-003.html
+++ b/testing/web-platform/tests/css/css-text/text-indent/text-indent-percentage-003.html
@@ -3,25 +3,22 @@
 <meta charset="utf-8">
 <title>CSS Text Test: text-indent percentage resolution basis</title>
 <link rel="author" title="Florian Rivoal" href="http://florian.rivoal.net/">
 <link rel="help" href="https://drafts.csswg.org/css-text-3/#text-indent-property">
 <meta name="flags" content="">
 <link rel="match" href="reference/text-indent-percentage-002-ref.html">
 <meta name="assert" content="Percentages in text-indent refer to width of the element's content box">
 <style>
-section { position: absolute; }
+body { background: white; }
 section, div {
   border-right: 10px solid white;
   margin-right: 10px;
   padding-right: 10px;
 }
 div {
   box-sizing: border-box;
   width: 120px;
 }
-.test div { text-indent: 50%; color: red; overflow: hidden; } /* overflow:hidden should not make any difference, but it does in some browsers */
-.ref div { text-indent: 50px; }
+.test div { text-indent: 50%; overflow: hidden; } /* overflow:hidden should not make any difference, but it does in some browsers */
 </style>
 
-<p>Test passes if there is a single black X below and no red.
 <section class=test><div>X</div></section>
-<section class=ref><div>X</div></section>
--- a/testing/web-platform/tests/css/css-text/text-indent/text-indent-percentage-004.html
+++ b/testing/web-platform/tests/css/css-text/text-indent/text-indent-percentage-004.html
@@ -3,25 +3,22 @@
 <meta charset="utf-8">
 <title>CSS Text Test: text-indent percentage resolution basis, in a calc expressiong</title>
 <link rel="author" title="Florian Rivoal" href="http://florian.rivoal.net/">
 <link rel="help" href="https://drafts.csswg.org/css-text-3/#text-indent-property">
 <meta name="flags" content="">
 <link rel="match" href="reference/text-indent-percentage-002-ref.html">
 <meta name="assert" content="Percentages in text-indent refer to width of the element's content box, when used in a calc expression">
 <style>
-section { position: absolute; }
+body { background: white; }
 section, div {
   border-right: 10px solid white;
   margin-right: 10px;
   padding-right: 10px;
 }
 div {
   box-sizing: border-box;
   width: 120px;
 }
-.test div { text-indent: calc(25px + 25%); color: red; }
-.ref div { text-indent: 50px; }
+.test div { text-indent: calc(25px + 25%); }
 </style>
 
-<p>Test passes if there is a single black X below and no red.
 <section class=test><div>X</div></section>
-<section class=ref><div>X</div></section>
--- a/toolkit/components/clearsitedata/ClearSiteData.cpp
+++ b/toolkit/components/clearsitedata/ClearSiteData.cpp
@@ -8,23 +8,25 @@
 
 #include "mozilla/OriginAttributes.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPrefs.h"
 #include "mozilla/Unused.h"
 #include "nsASCIIMask.h"
 #include "nsCharSeparatedTokenizer.h"
+#include "nsContentSecurityManager.h"
 #include "nsContentUtils.h"
 #include "nsIClearDataService.h"
 #include "nsIHttpChannel.h"
 #include "nsIHttpProtocolHandler.h"
 #include "nsIObserverService.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptError.h"
+#include "nsIScriptSecurityManager.h"
 #include "nsNetUtil.h"
 
 using namespace mozilla;
 
 namespace {
 
 StaticRefPtr<ClearSiteData> gClearSiteData;
 
@@ -189,44 +191,49 @@ ClearSiteData::Observe(nsISupports* aSub
 }
 
 void
 ClearSiteData::ClearDataFromChannel(nsIHttpChannel* aChannel)
 {
   nsresult rv;
   nsCOMPtr<nsIURI> uri;
 
+  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+  if (NS_WARN_IF(!ssm)) {
+    return;
+  }
+
+  nsCOMPtr<nsIPrincipal> principal;
+  rv = ssm->GetChannelResultPrincipal(aChannel, getter_AddRefs(principal));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  nsCOMPtr<nsIContentSecurityManager> csm =
+    do_GetService(NS_CONTENTSECURITYMANAGER_CONTRACTID);
+
+  bool secure;
+  rv = csm->IsOriginPotentiallyTrustworthy(principal, &secure);
+  if (NS_WARN_IF(NS_FAILED(rv)) || !secure) {
+    return;
+  }
+
   // We want to use the final URI to check if Clear-Site-Data should be allowed
   // or not.
   rv = aChannel->GetURI(getter_AddRefs(uri));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
-  if (!IsSecureURI(uri)) {
-    return;
-  }
-
   uint32_t flags = ParseHeader(aChannel, uri);
   if (flags == 0) {
     // Nothing to do.
     return;
   }
 
-  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
-  if (NS_WARN_IF(!ssm)) {
-    return;
-  }
-
-  nsCOMPtr<nsIPrincipal> principal;
-  rv = ssm->GetChannelURIPrincipal(aChannel, getter_AddRefs(principal));
-  if (NS_WARN_IF(NS_FAILED(rv)) || !principal) {
-    return;
-  }
-
   int32_t cleanFlags = 0;
   RefPtr<PendingCleanupHolder> holder = new PendingCleanupHolder(aChannel);
 
   if (flags & eCache) {
     LogOpToConsole(aChannel, uri, eCache);
     cleanFlags |= nsIClearDataService::CLEAR_ALL_CACHES;
   }
 
@@ -259,31 +266,16 @@ ClearSiteData::ClearDataFromChannel(nsIH
   }
 
   if (flags & eExecutionContexts) {
     LogOpToConsole(aChannel, uri, eExecutionContexts);
     BrowsingContextsReload(holder, principal);
   }
 }
 
-bool
-ClearSiteData::IsSecureURI(nsIURI* aURI) const
-{
-  MOZ_ASSERT(aURI);
-
-  bool prioriAuthenticated = false;
-  if (NS_WARN_IF(NS_FAILED(NS_URIChainHasFlags(aURI,
-                                               nsIProtocolHandler::URI_IS_POTENTIALLY_TRUSTWORTHY,
-                                               &prioriAuthenticated)))) {
-    return false;
-  }
-
-  return prioriAuthenticated;
-}
-
 uint32_t
 ClearSiteData::ParseHeader(nsIHttpChannel* aChannel, nsIURI* aURI) const
 {
   MOZ_ASSERT(aChannel);
 
   nsAutoCString headerValue;
   nsresult rv = aChannel->GetResponseHeader(NS_LITERAL_CSTRING("Clear-Site-Data"),
                                             headerValue);
--- a/toolkit/components/clearsitedata/ClearSiteData.h
+++ b/toolkit/components/clearsitedata/ClearSiteData.h
@@ -34,21 +34,16 @@ private:
 
   class PendingCleanupHolder;
 
   // Starts the cleanup if the channel contains the Clear-Site-Data header and
   // if the URI is secure.
   void
   ClearDataFromChannel(nsIHttpChannel* aChannel);
 
-  // This method checks if the protocol handler of the URI has the
-  // URI_IS_POTENTIALLY_TRUSTWORTHY flag.
-  bool
-  IsSecureURI(nsIURI* aURI) const;
-
   // From the Clear-Site-Data header, it returns a bitmap with Type values.
   uint32_t
   ParseHeader(nsIHttpChannel* aChannel, nsIURI* aURI) const;
 
   enum Type
   {
     eCache = 0x01,
     eCookies = 0x02,
--- a/toolkit/content/widgets/findbar.js
+++ b/toolkit/content/widgets/findbar.js
@@ -663,16 +663,20 @@ class MozFindbar extends XULElement {
   close(aNoAnim) {
     if (this.hidden)
       return;
 
     if (aNoAnim)
       this.setAttribute("noanim", true);
     this.hidden = true;
 
+    let event = document.createEvent("Events");
+    event.initEvent("findbarclose", true, false);
+    this.dispatchEvent(event);
+
     // 'focusContent()' iterates over all listeners in the chrome
     // process, so we need to call it from here.
     this.browser.finder.focusContent();
     this.browser.finder.onFindbarClose();
 
     this._cancelTimers();
     this._updateBrowserWithState();
 
--- a/toolkit/content/widgets/tree.xml
+++ b/toolkit/content/widgets/tree.xml
@@ -9,28 +9,17 @@
 %treeDTD;
 ]>
 
 <bindings id="treeBindings"
    xmlns="http://www.mozilla.org/xbl"
    xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
    xmlns:xbl="http://www.mozilla.org/xbl">
 
-  <binding id="tree-base" extends="chrome://global/content/bindings/general.xml#basecontrol">
-    <implementation>
-      <method name="_isAccelPressed">
-        <parameter name="aEvent"/>
-        <body><![CDATA[
-          return aEvent.getModifierState("Accel");
-        ]]></body>
-      </method>
-    </implementation>
-  </binding>
-
-  <binding id="tree" extends="chrome://global/content/bindings/tree.xml#tree-base">
+  <binding id="tree" extends="chrome://global/content/bindings/general.xml#basecontrol">
     <content hidevscroll="true" hidehscroll="true" clickthrough="never">
       <children includes="treecols"/>
       <xul:stack class="tree-stack" flex="1">
         <xul:treerows class="tree-rows" flex="1" xbl:inherits="hidevscroll">
           <children/>
         </xul:treerows>
         <xul:textbox anonid="input" class="tree-input" left="0" top="0" hidden="true"/>
       </xul:stack>
@@ -411,29 +400,29 @@
         <parameter name="event"/>
         <body>
           <![CDATA[
             event.preventDefault();
 
             if (this.view.rowCount == 0)
               return;
 
-            if (this._isAccelPressed(event) && this.view.selection.single) {
+            if (event.getModifierState("Accel") && this.view.selection.single) {
               this.treeBoxObject.scrollByLines(offset);
               return;
             }
 
             var c = this.currentIndex + offset;
             if (offset > 0 ? c > edge : c < edge) {
               if (this.view.selection.isSelected(edge) && this.view.selection.count <= 1)
                 return;
               c = edge;
             }
 
-            if (!this._isAccelPressed(event))
+            if (!event.getModifierState("Accel"))
               this.view.selection.timedSelect(c, this._selectDelay);
             else // Ctrl+Up/Down moves the anchor without selecting
               this.currentIndex = c;
             this.treeBoxObject.ensureRowIsVisible(c);
           ]]>
         </body>
       </method>
 
@@ -464,17 +453,17 @@
 
             if (c == edge) {
               if (this.view.selection.isSelected(c))
                 return;
             }
 
             // Extend the selection from the existing pivot, if any
             this.view.selection.rangedSelect(-1, c + offset,
-                                             this._isAccelPressed(event));
+                                             event.getModifierState("Accel"));
             this.treeBoxObject.ensureRowIsVisible(c + offset);
 
           ]]>
         </body>
       </method>
 
       <method name="_moveByPage">
         <parameter name="offset"/>
@@ -482,17 +471,17 @@
         <parameter name="event"/>
         <body>
           <![CDATA[
             event.preventDefault();
 
             if (this.view.rowCount == 0)
               return;
 
-            if (this.pageUpOrDownMovesSelection == this._isAccelPressed(event)) {
+            if (this.pageUpOrDownMovesSelection == event.getModifierState("Accel")) {
                this.treeBoxObject.scrollByPages(offset);
                return;
             }
 
             if (this.view.rowCount == 1 && !this.view.selection.isSelected(0)) {
               this.view.selection.timedSelect(0, this._selectDelay);
               return;
             }
@@ -532,17 +521,17 @@
         <body>
           <![CDATA[
             event.preventDefault();
 
             if (this.view.rowCount == 0)
               return;
 
             if (this.view.rowCount == 1 && !this.view.selection.isSelected(0) &&
-                !(this.pageUpOrDownMovesSelection == this._isAccelPressed(event))) {
+                !(this.pageUpOrDownMovesSelection == event.getModifierState("Accel"))) {
               this.view.selection.timedSelect(0, this._selectDelay);
               return;
             }
 
             if (this.view.selection.single)
               return;
 
             var c = this.currentIndex;
@@ -557,26 +546,26 @@
 
             if (offset > 0) {
               i += p - 1;
               if (c >= i) {
                  i = c + p;
                  this.treeBoxObject.ensureRowIsVisible(i > edge ? edge : i);
               }
               // Extend the selection from the existing pivot, if any
-              this.view.selection.rangedSelect(-1, i > edge ? edge : i, this._isAccelPressed(event));
+              this.view.selection.rangedSelect(-1, i > edge ? edge : i, event.getModifierState("Accel"));
 
             } else {
 
               if (c <= i) {
                  i = c <= p ? 0 : c - p;
                  this.treeBoxObject.ensureRowIsVisible(i);
               }
               // Extend the selection from the existing pivot, if any
-              this.view.selection.rangedSelect(-1, i, this._isAccelPressed(event));
+              this.view.selection.rangedSelect(-1, i, event.getModifierState("Accel"));
             }
 
           ]]>
         </body>
       </method>
 
       <method name="_moveToEdge">
         <parameter name="edge"/>
@@ -589,17 +578,17 @@
               return;
 
             if (this.view.selection.isSelected(edge) && this.view.selection.count == 1) {
               this.currentIndex = edge;
               return;
             }
 
             // Normal behaviour is to select the first/last row
-            if (!this._isAccelPressed(event))
+            if (!event.getModifierState("Accel"))
               this.view.selection.timedSelect(edge, this._selectDelay);
 
             // In a multiselect tree Ctrl+Home/End moves the anchor
             else if (!this.view.selection.single)
               this.currentIndex = edge;
 
             this.treeBoxObject.ensureRowIsVisible(edge);
           ]]>
@@ -622,17 +611,17 @@
             }
 
             if (this.view.selection.single ||
                 (this.view.selection.isSelected(edge)) && this.view.selection.isSelected(this.currentIndex))
               return;
 
             // Extend the selection from the existing pivot, if any.
             // -1 doesn't work here, so using currentIndex instead
-            this.view.selection.rangedSelect(this.currentIndex, edge, this._isAccelPressed(event));
+            this.view.selection.rangedSelect(this.currentIndex, edge, event.getModifierState("Accel"));
 
             this.treeBoxObject.ensureRowIsVisible(edge);
           ]]>
         </body>
       </method>
       <method name="_handleEnter">
         <parameter name="event"/>
         <body><![CDATA[
@@ -901,22 +890,22 @@
       <handler event="keypress">
         <![CDATA[
          if (this._editingColumn)
            return;
 
          if (event.charCode == " ".charCodeAt(0)) {
            var c = this.currentIndex;
            if (!this.view.selection.isSelected(c) ||
-               (!this.view.selection.single && this._isAccelPressed(event))) {
+               (!this.view.selection.single && event.getModifierState("Accel"))) {
              this.view.selection.toggleSelect(c);
              event.preventDefault();
            }
          } else if (!this.disableKeyNavigation && event.charCode > 0 &&
-                    !event.altKey && !this._isAccelPressed(event) &&
+                    !event.altKey && !event.getModifierState("Accel") &&
                     !event.metaKey && !event.ctrlKey) {
            var l = this._keyNavigate(event);
            if (l >= 0) {
              this.view.selection.timedSelect(l, this._selectDelay);
              this.treeBoxObject.ensureRowIsVisible(l);
            }
            event.preventDefault();
          }
@@ -939,17 +928,17 @@
         Array.forEach(this.getElementsByTagName("splitter"), function(splitter) {
           if (!splitter.hasAttribute("resizeafter"))
             splitter.setAttribute("resizeafter", "farthest");
         });
       ]]></constructor>
     </implementation>
   </binding>
 
-  <binding id="treerows" extends="chrome://global/content/bindings/tree.xml#tree-base">
+  <binding id="treerows" extends="chrome://global/content/bindings/general.xml#basecontrol">
     <content>
       <xul:hbox flex="1" class="tree-bodybox">
         <children/>
       </xul:hbox>
       <xul:scrollbar height="0" minwidth="0" minheight="0" orient="vertical" xbl:inherits="collapsed=hidevscroll" style="position:relative; z-index:2147483647;"
         oncontextmenu="event.stopPropagation(); event.preventDefault();"
         onclick="event.stopPropagation(); event.preventDefault();"
         ondblclick="event.stopPropagation();"
@@ -978,17 +967,17 @@
           else if (event.detail == 0)
             tree.removeAttribute("hidevscroll");
           event.stopPropagation();
         ]]>
       </handler>
     </handlers>
   </binding>
 
-  <binding id="treebody" extends="chrome://global/content/bindings/tree.xml#tree-base">
+  <binding id="treebody" extends="chrome://global/content/bindings/general.xml#basecontrol">
     <implementation>
       <constructor>
         if ("_ensureColumnOrder" in this.parentNode)
           this.parentNode._ensureColumnOrder();
       </constructor>
 
       <field name="_lastSelectedRow">
         -1
@@ -996,17 +985,17 @@
     </implementation>
     <handlers>
       <!-- If there is no modifier key, we select on mousedown, not
            click, so that drags work correctly. -->
       <handler event="mousedown" clickcount="1">
       <![CDATA[
          if (this.parentNode.disabled)
            return;
-         if (((!this._isAccelPressed(event) ||
+         if (((!event.getModifierState("Accel") ||
              !this.parentNode.pageUpOrDownMovesSelection) &&
              !event.shiftKey && !event.metaKey) ||
              this.parentNode.view.selection.single) {
            var b = this.parentNode.treeBoxObject;
            var cell = b.getCellAt(event.clientX, event.clientY);
            var view = this.parentNode.view;
 
            // save off the last selected row
@@ -1066,17 +1055,17 @@
                 view.selection.select(parentIndex);
             }
           }
           this.parentNode.changeOpenState(cell.row);
           return;
         }
 
         if (!view.selection.single) {
-          var augment = this._isAccelPressed(event);
+          var augment = event.getModifierState("Accel");
           if (event.shiftKey) {
             view.selection.rangedSelect(-1, cell.row, augment);
             b.ensureRowIsVisible(cell.row);
             return;
           }
           if (augment) {
             view.selection.toggleSelect(cell.row);
             b.ensureRowIsVisible(cell.row);
@@ -1130,17 +1119,17 @@
           this.parentNode.changeOpenState(row);
       ]]>
       </handler>
 
     </handlers>
   </binding>
 
   <binding id="treecol-base"
-           extends="chrome://global/content/bindings/tree.xml#tree-base">
+           extends="chrome://global/content/bindings/general.xml#basecontrol">
     <implementation>
       <constructor>
         this.parentNode.parentNode._columnsDirty = true;
       </constructor>
 
       <property name="ordinal">
         <getter><![CDATA[
           var val = this.getAttribute("ordinal");
@@ -1348,17 +1337,17 @@
 
   <binding id="treecol-image" extends="chrome://global/content/bindings/tree.xml#treecol-base">
     <content>
       <xul:image class="treecol-icon" xbl:inherits="src"/>
     </content>
   </binding>
 
   <binding id="columnpicker" display="xul:button"
-           extends="chrome://global/content/bindings/tree.xml#tree-base">
+           extends="chrome://global/content/bindings/general.xml#basecontrol">
     <content>
       <xul:image class="tree-columnpicker-icon"/>
       <xul:menupopup anonid="popup">
         <xul:menuseparator anonid="menuseparator"/>
         <xul:menuitem anonid="menuitem" label="&restoreColumnOrder.label;"/>
       </xul:menupopup>
     </content>
 
--- a/toolkit/recordreplay/HashTable.cpp
+++ b/toolkit/recordreplay/HashTable.cpp
@@ -82,17 +82,17 @@ class StableHashTableInfo
 
   // Buffer with executable memory for use in binding functions.
   uint8_t* mCallbackStorage;
   static const size_t CallbackStorageCapacity = 4096;
 
   // Get an existing key in the table.
   KeyInfo* FindKeyInfo(HashNumber aOriginalHash, const void* aKey, HashInfo** aHashInfo = nullptr) {
     HashToKeyMap::iterator iter = mHashToKey.find(aOriginalHash);
-    MOZ_ASSERT(iter != mHashToKey.end());
+    MOZ_RELEASE_ASSERT(iter != mHashToKey.end());
 
     HashInfo* hashInfo = iter->second.get();
     for (KeyInfo& keyInfo : hashInfo->mKeys) {
       if (keyInfo.mKey == aKey) {
         if (aHashInfo) {
           *aHashInfo = hashInfo;
         }
         return &keyInfo;
@@ -109,17 +109,17 @@ public:
     , mHashGenerator(0)
     , mCallbackStorage(nullptr)
   {
     // Use AllocateMemory, as the result will have RWX permissions.
     mCallbackStorage = (uint8_t*) AllocateMemory(CallbackStorageCapacity, MemoryKind::Tracked);
   }
 
   ~StableHashTableInfo() {
-    MOZ_ASSERT(mHashToKey.empty());
+    MOZ_RELEASE_ASSERT(mHashToKey.empty());
     DeallocateMemory(mCallbackStorage, CallbackStorageCapacity, MemoryKind::Tracked);
   }
 
   bool AppearsValid() {
     return mMagic == MagicNumber;
   }
 
   void AddKey(HashNumber aOriginalHash, const void* aKey, HashNumber aNewHash) {
@@ -170,17 +170,17 @@ public:
         }
       }
     }
     return false;
   }
 
   HashNumber GetOriginalHashNumber(const void* aKey) {
     KeyToHashMap::iterator iter = mKeyToHash.find(aKey);
-    MOZ_ASSERT(iter != mKeyToHash.end());
+    MOZ_RELEASE_ASSERT(iter != mKeyToHash.end());
     return iter->second;
   }
 
   class Assembler : public recordreplay::Assembler {
   public:
     explicit Assembler(StableHashTableInfo& aInfo)
       : recordreplay::Assembler(aInfo.mCallbackStorage, CallbackStorageCapacity)
     {}
@@ -207,17 +207,17 @@ public:
     return mLastNewHash;
   }
 
   bool HasLastKey() {
     return !!mLastKey;
   }
 
   HashNumber GetLastNewHash(const void* aKey) {
-    MOZ_ASSERT(aKey == mLastKey);
+    MOZ_RELEASE_ASSERT(aKey == mLastKey);
     return mLastNewHash;
   }
 
   bool IsEmpty() { return mHashToKey.empty(); }
 
   // Move aOther's contents into this one and clear aOther out. Callbacks for
   // the tables are left alone.
   void MoveContentsFrom(StableHashTableInfo& aOther) {
@@ -297,17 +297,17 @@ WrapPLHashAllocEntry(void* aAllocPrivate
   if (info->HasLastKey()) {
     uint32_t originalHash = info->mKeyHash(aKey);
     info->AddKey(originalHash, aKey, info->GetLastNewHash(aKey));
   } else {
     // A few PLHashTables are manipulated directly by Gecko code, in which case
     // the hashes are supplied directly to the table and we don't have a chance
     // to modify them. Fortunately, none of these tables are iterated in a way
     // that can cause the replay to diverge, so just punt in these cases.
-    MOZ_ASSERT(info->IsEmpty());
+    MOZ_RELEASE_ASSERT(info->IsEmpty());
   }
 
   return info->mAllocOps
          ? info->mAllocOps->allocEntry(info->mAllocPrivate, aKey)
          : (PLHashEntry*) malloc(sizeof(PLHashEntry));
 }
 
 static void
--- a/toolkit/recordreplay/Lock.cpp
+++ b/toolkit/recordreplay/Lock.cpp
@@ -130,20 +130,31 @@ Lock::Find(void* aNativeLock)
 
   if (gLocks) {
     LockMap::iterator iter = gLocks->find(aNativeLock);
     if (iter != gLocks->end()) {
       // Now that we know the lock is recorded, check whether thread events
       // should be generated right now. Doing things in this order avoids
       // reentrancy issues when initializing the thread-local state used by
       // these calls.
-      if (AreThreadEventsPassedThrough() || HasDivergedFromRecording()) {
+      Lock* lock = iter->second;
+      if (AreThreadEventsPassedThrough()) {
         return nullptr;
       }
-      return iter->second;
+      if (HasDivergedFromRecording()) {
+        // When diverged from the recording, don't allow uses of locks that are
+        // held by idling threads that have not diverged from the recording.
+        // This will cause the process to deadlock, so rewind instead.
+        if (lock->mOwner && Thread::GetById(lock->mOwner)->IsIdle()) {
+          EnsureNotDivergedFromRecording();
+          Unreachable();
+        }
+        return nullptr;
+      }
+      return lock;
     }
   }
 
   return nullptr;
 }
 
 void
 Lock::Enter()
@@ -166,24 +177,29 @@ Lock::Enter()
   if (IsRecording()) {
     acquires->mAcquires->WriteScalar(thread->Id());
   } else {
     // Wait until this thread is next in line to acquire the lock, or until it
     // has been instructed to diverge from the recording.
     while (thread->Id() != acquires->mNextOwner && !thread->MaybeDivergeFromRecording()) {
       Thread::Wait();
     }
+    if (!thread->HasDivergedFromRecording()) {
+      mOwner = thread->Id();
+    }
   }
 }
 
 void
 Lock::Exit()
 {
   Thread* thread = Thread::Current();
   if (IsReplaying() && !thread->HasDivergedFromRecording()) {
+    mOwner = 0;
+
     // Notify the next owner before releasing the lock.
     LockAcquires* acquires = gLockAcquires.Get(mId);
     acquires->ReadAndNotifyNextOwner(thread);
   }
 }
 
 struct AtomicLock : public detail::MutexImpl
 {
--- a/toolkit/recordreplay/Lock.h
+++ b/toolkit/recordreplay/Lock.h
@@ -25,19 +25,22 @@ namespace recordreplay {
 // which they originally occurred.
 
 // Information about a recorded lock.
 class Lock
 {
   // Unique ID for this lock.
   size_t mId;
 
+  // When replaying, any thread owning this lock as part of the recording.
+  Atomic<size_t, SequentiallyConsistent, Behavior::DontPreserve> mOwner;
+
 public:
   explicit Lock(size_t aId)
-    : mId(aId)
+    : mId(aId), mOwner(0)
   {
     MOZ_ASSERT(aId);
   }
 
   size_t Id() { return mId; }
 
   // When recording, this is called after the lock has been acquired, and
   // records the acquire in the lock's acquire order stream. When replaying,
--- a/toolkit/recordreplay/MemorySnapshot.cpp
+++ b/toolkit/recordreplay/MemorySnapshot.cpp
@@ -657,16 +657,27 @@ HandleDirtyMemoryFault(uint8_t* aAddress
   // it so that execution can proceed.
   uint8_t* original = AllocatePageCopy();
   MemoryMove(original, aAddress, PageSize);
   gMemoryInfo->mActiveDirty.insert(aAddress, DirtyPage(aAddress, original, executable));
   DirectUnprotectMemory(aAddress, PageSize, executable);
   return true;
 }
 
+bool
+MemoryRangeIsTracked(void* aAddress, size_t aSize)
+{
+  for (uint8_t* ptr = PageBase(aAddress); ptr < (uint8_t*) aAddress + aSize; ptr += PageSize) {
+    if (!IsTrackedAddress(ptr, nullptr)) {
+      return false;
+    }
+  }
+  return true;
+}
+
 void
 UnrecoverableSnapshotFailure()
 {
   if (gMemoryInfo) {
     AutoSpinLock lock(gMemoryInfo->mTrackedRegionsLock);
     DirectUnprotectMemory(PageBase(&errno), PageSize, false);
     for (auto region : gMemoryInfo->mTrackedRegionsByAllocationOrder) {
       DirectUnprotectMemory(region.mBase, region.mSize, region.mExecutable,
--- a/toolkit/recordreplay/MemorySnapshot.h
+++ b/toolkit/recordreplay/MemorySnapshot.h
@@ -45,16 +45,21 @@ void* AllocateMemoryTryAddress(void* aAd
 // Note a range of memory that was just allocated from the system, and the
 // kind of memory allocation that was performed.
 void RegisterAllocatedMemory(void* aBaseAddress, size_t aSize, MemoryKind aKind);
 
 // Exclude a region of memory from snapshots, before the first checkpoint has
 // been reached.
 void AddInitialUntrackedMemoryRegion(uint8_t* aBase, size_t aSize);
 
+// Return whether a range of memory is in a tracked region. This excludes
+// memory that was allocated after the last checkpoint and is not write
+// protected.
+bool MemoryRangeIsTracked(void* aAddress, size_t aSize);
+
 // Initialize the memory snapshots system.
 void InitializeMemorySnapshots();
 
 // Take the first heap memory snapshot.
 void TakeFirstMemorySnapshot();
 
 // Take a differential heap memory snapshot compared to the last one,
 // associated with the last saved checkpoint.
--- a/toolkit/recordreplay/MiddlemanCall.cpp
+++ b/toolkit/recordreplay/MiddlemanCall.cpp
@@ -158,17 +158,17 @@ ProcessMiddlemanCall(const char* aInputD
   BufferStream inputStream(aInputData, aInputSize);
   BufferStream outputStream(aOutputData);
 
   while (!inputStream.IsEmpty()) {
     MiddlemanCall* call = new MiddlemanCall();
     call->DecodeInput(inputStream);
 
     const Redirection& redirection = gRedirections[call->mCallId];
-    MOZ_RELEASE_ASSERT(gRedirections[call->mCallId].mMiddlemanCall);
+    MOZ_RELEASE_ASSERT(redirection.mMiddlemanCall);
 
     CallArguments arguments;
     call->mArguments.CopyTo(&arguments);
 
     {
       MiddlemanCallContext cx(call, &arguments, MiddlemanCallPhase::MiddlemanInput);
       redirection.mMiddlemanCall(cx);
     }
@@ -318,16 +318,19 @@ MangleSystemValue(const void* aValue, bo
 {
   return (const void*) ((size_t)aValue | (1ULL << (aFromRecording ? 63 : 62)));
 }
 
 void
 Middleman_SystemOutput(MiddlemanCallContext& aCx, const void** aOutput, bool aUpdating)
 {
   if (!*aOutput) {
+    if (aCx.mPhase == MiddlemanCallPhase::MiddlemanOutput) {
+      aCx.mCall->SetMiddlemanValue(*aOutput);
+    }
     return;
   }
 
   switch (aCx.mPhase) {
   case MiddlemanCallPhase::ReplayPreface:
     if (!HasDivergedFromRecording()) {
       // If we haven't diverged from the recording, use the output value saved
       // in the recording.
--- a/toolkit/recordreplay/MiddlemanCall.h
+++ b/toolkit/recordreplay/MiddlemanCall.h
@@ -349,17 +349,17 @@ void ProcessMiddlemanCall(const char* aI
 // In the middleman process, reset all call state.
 void ResetMiddlemanCalls();
 
 ///////////////////////////////////////////////////////////////////////////////
 // Middleman Call Helpers
 ///////////////////////////////////////////////////////////////////////////////
 
 // Capture the contents of an input buffer at BufferArg with element count at CountArg.
-template <size_t BufferArg, size_t CountArg, typename ElemType>
+template <size_t BufferArg, size_t CountArg, typename ElemType = char>
 static inline void
 Middleman_Buffer(MiddlemanCallContext& aCx)
 {
   if (aCx.AccessPreface()) {
     auto& buffer = aCx.mArguments->Arg<BufferArg, void*>();
     auto byteSize = aCx.mArguments->Arg<CountArg, size_t>() * sizeof(ElemType);
     aCx.ReadOrWritePrefaceBuffer(&buffer, byteSize);
   }
--- a/toolkit/recordreplay/ProcessRedirect.cpp
+++ b/toolkit/recordreplay/ProcessRedirect.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ProcessRedirect.h"
 
 #include "InfallibleVector.h"
 #include "MiddlemanCall.h"
+#include "ipc/ParentInternal.h"
 #include "mozilla/Sprintf.h"
 
 #include <dlfcn.h>
 #include <string.h>
 
 namespace {
 
 #include "udis86/udis86.c"
@@ -94,16 +95,21 @@ RecordReplayInterceptCall(int aCallId, C
     // If the redirection has a middleman call hook, try to perform the call in
     // the middleman instead.
     if (redirection.mMiddlemanCall) {
       if (SendCallToMiddleman(aCallId, aArguments, /* aPopulateOutput = */ true)) {
         return 0;
       }
     }
 
+    if (parent::InRepaintStressMode()) {
+      // We're about to crash, so print out the name of the call that failed.
+      Print("Could not perform middleman call: %s\n", redirection.mName);
+    }
+
     // Calling any redirection which performs the standard steps will cause
     // debugger operations that have diverged from the recording to fail.
     EnsureNotDivergedFromRecording();
     Unreachable();
   }
 
   if (IsRecording()) {
     // Call the original function, passing through events while we do so.
--- a/toolkit/recordreplay/ProcessRedirectDarwin.cpp
+++ b/toolkit/recordreplay/ProcessRedirectDarwin.cpp
@@ -147,17 +147,17 @@ namespace recordreplay {
   MACRO(csops, RR_SaveRvalHadErrorNegative<RR_WriteBuffer<2, 3>>) \
   MACRO(__getlogin, RR_SaveRvalHadErrorNegative<RR_WriteBuffer<0, 1>>) \
   MACRO(__workq_kernreturn, nullptr, Preamble___workq_kernreturn) \
   MACRO(start_wqthread, nullptr, Preamble_start_wqthread)        \
   /* pthreads interface functions */                             \
   MACRO(pthread_cond_wait, nullptr, Preamble_pthread_cond_wait)  \
   MACRO(pthread_cond_timedwait, nullptr, Preamble_pthread_cond_timedwait) \
   MACRO(pthread_cond_timedwait_relative_np, nullptr, Preamble_pthread_cond_timedwait_relative_np) \
-  MACRO(pthread_create, nullptr, Preamble_pthread_create, nullptr, Preamble_SetError) \
+  MACRO(pthread_create, nullptr, Preamble_pthread_create)        \
   MACRO(pthread_join, nullptr, Preamble_pthread_join)            \
   MACRO(pthread_mutex_init, nullptr, Preamble_pthread_mutex_init) \
   MACRO(pthread_mutex_destroy, nullptr, Preamble_pthread_mutex_destroy) \
   MACRO(pthread_mutex_lock, nullptr, Preamble_pthread_mutex_lock) \
   MACRO(pthread_mutex_trylock, nullptr, Preamble_pthread_mutex_trylock) \
   MACRO(pthread_mutex_unlock, nullptr, Preamble_pthread_mutex_unlock) \
   /* C Library functions */                                      \
   MACRO(dlclose, nullptr, Preamble_Veto<0>)                      \
@@ -286,16 +286,17 @@ namespace recordreplay {
   /* Don't handle release/retain calls explicitly in the middleman, all resources */ \
   /* will be cleaned up when its calls are reset. */             \
   MACRO(CFRelease, RR_ScalarRval, nullptr, nullptr, Preamble_Veto<0>) \
   MACRO(CFRetain, RR_ScalarRval, nullptr, nullptr, MiddlemanPreamble_CFRetain) \
   MACRO(CFRunLoopAddSource)                                      \
   MACRO(CFRunLoopGetCurrent, RR_ScalarRval)                      \
   MACRO(CFRunLoopRemoveSource)                                   \
   MACRO(CFRunLoopSourceCreate, RR_ScalarRval, Preamble_CFRunLoopSourceCreate) \
+  MACRO(CFRunLoopSourceInvalidate)                               \
   MACRO(CFRunLoopSourceSignal)                                   \
   MACRO(CFRunLoopWakeUp)                                         \
   MACRO(CFStringAppendCharacters)                                \
   MACRO(CFStringCompare, RR_ScalarRval, nullptr,                 \
         Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CFTypeArg<1>>) \
   MACRO(CFStringCreateArrayBySeparatingStrings, RR_ScalarRval)   \
   MACRO(CFStringCreateMutable, RR_ScalarRval)                    \
   MACRO(CFStringCreateWithBytes, RR_ScalarRval)                  \
@@ -323,19 +324,19 @@ namespace recordreplay {
   MACRO(CFStringTokenizerCreate, RR_ScalarRval)                  \
   MACRO(CFStringTokenizerGetCurrentTokenRange, RR_ComplexScalarRval) \
   MACRO(CFURLCreateFromFileSystemRepresentation, RR_ScalarRval)  \
   MACRO(CFURLCreateFromFSRef, RR_ScalarRval)                     \
   MACRO(CFURLCreateWithFileSystemPath, RR_ScalarRval)            \
   MACRO(CFURLCreateWithString, RR_ScalarRval)                    \
   MACRO(CFURLGetFileSystemRepresentation, RR_Compose<RR_ScalarRval, RR_WriteBuffer<2, 3>>) \
   MACRO(CFURLGetFSRef, RR_Compose<RR_ScalarRval, RR_WriteBufferFixedSize<1, sizeof(FSRef)>>) \
-  MACRO(CFUUIDCreate, RR_ScalarRval)                             \
+  MACRO(CFUUIDCreate, RR_ScalarRval, nullptr, Middleman_CreateCFTypeRval) \
   MACRO(CFUUIDCreateString, RR_ScalarRval)                       \
-  MACRO(CFUUIDGetUUIDBytes, RR_ComplexScalarRval)                \
+  MACRO(CFUUIDGetUUIDBytes, RR_ComplexScalarRval, nullptr, Middleman_CFTypeArg<0>) \
   MACRO(CGAffineTransformConcat, RR_OversizeRval<sizeof(CGAffineTransform)>) \
   MACRO(CGBitmapContextCreateImage, RR_ScalarRval)               \
   MACRO(CGBitmapContextCreateWithData,                           \
         RR_Compose<RR_ScalarRval, RR_CGBitmapContextCreateWithData>, nullptr, \
         Middleman_CGBitmapContextCreateWithData)                 \
   MACRO(CGBitmapContextGetBytesPerRow, RR_ScalarRval)            \
   MACRO(CGBitmapContextGetHeight, RR_ScalarRval)                 \
   MACRO(CGBitmapContextGetWidth, RR_ScalarRval)                  \
@@ -381,29 +382,31 @@ namespace recordreplay {
   MACRO(CGContextSetShouldSubpixelPositionFonts, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
   MACRO(CGContextSetShouldSubpixelQuantizeFonts, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
   MACRO(CGContextSetTextDrawingMode, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
   MACRO(CGContextSetTextMatrix, nullptr, nullptr,                \
         Middleman_Compose<Middleman_UpdateCFTypeArg<0>,          \
                           Middleman_StackArgumentData<sizeof(CGAffineTransform)>>) \
   MACRO(CGContextScaleCTM, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
   MACRO(CGContextTranslateCTM, nullptr, nullptr, Middleman_UpdateCFTypeArg<0>) \
-  MACRO(CGDataProviderCreateWithData, RR_Compose<RR_ScalarRval, RR_CGDataProviderCreateWithData>) \
-  MACRO(CGDataProviderRelease)                                   \
+  MACRO(CGDataProviderCreateWithData, RR_Compose<RR_ScalarRval, RR_CGDataProviderCreateWithData>, \
+        nullptr, Middleman_CGDataProviderCreateWithData)         \
+  MACRO(CGDataProviderRelease, nullptr, nullptr, nullptr, Preamble_Veto<0>) \
   MACRO(CGDisplayCopyColorSpace, RR_ScalarRval)                  \
   MACRO(CGDisplayIOServicePort, RR_ScalarRval)                   \
   MACRO(CGEventSourceCounterForEventType, RR_ScalarRval)         \
   MACRO(CGFontCopyTableForTag, RR_ScalarRval, nullptr,           \
         Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
   MACRO(CGFontCopyTableTags, RR_ScalarRval, nullptr,             \
         Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
   MACRO(CGFontCopyVariations, RR_ScalarRval, nullptr,            \
         Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
   MACRO(CGFontCreateCopyWithVariations, RR_ScalarRval)           \
-  MACRO(CGFontCreateWithDataProvider, RR_ScalarRval)             \
+  MACRO(CGFontCreateWithDataProvider, RR_ScalarRval, nullptr,    \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
   MACRO(CGFontCreateWithFontName, RR_ScalarRval, nullptr,        \
         Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
   MACRO(CGFontCreateWithPlatformFont, RR_ScalarRval)             \
   MACRO(CGFontGetAscent, RR_ScalarRval, nullptr, Middleman_CFTypeArg<0>) \
   MACRO(CGFontGetCapHeight, RR_ScalarRval, nullptr, Middleman_CFTypeArg<0>) \
   MACRO(CGFontGetDescent, RR_ScalarRval, nullptr, Middleman_CFTypeArg<0>) \
   MACRO(CGFontGetFontBBox, RR_OversizeRval<sizeof(CGRect)>, nullptr, \
         Middleman_Compose<Middleman_CFTypeArg<1>, Middleman_OversizeRval<sizeof(CGRect)>>) \
@@ -437,20 +440,24 @@ namespace recordreplay {
   MACRO(CTFontCopyFeatures, RR_ScalarRval, nullptr,              \
         Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
   MACRO(CTFontCopyFontDescriptor, RR_ScalarRval, nullptr,        \
         Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
   MACRO(CTFontCopyGraphicsFont, RR_ScalarRval, nullptr,          \
         Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
   MACRO(CTFontCopyTable, RR_ScalarRval, nullptr,                 \
         Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
-  MACRO(CTFontCopyVariationAxes, RR_ScalarRval)                  \
+  MACRO(CTFontCopyVariationAxes, RR_ScalarRval, nullptr,         \
+        Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CreateCFTypeRval>) \
   MACRO(CTFontCreateForString, RR_ScalarRval, nullptr,           \
         Middleman_Compose<Middleman_CFTypeArg<0>, Middleman_CFTypeArg<1>, Middleman_CreateCFTypeRval>) \
-  MACRO(CTFontCreatePathForGlyph, RR_ScalarRval)                 \
+  MACRO(CTFontCreatePathForGlyph, RR_ScalarRval, nullptr,        \
+        Middleman_Compose<Middleman_CFTypeArg<0>,                \
+                          Middleman_BufferFixedSize<2, sizeof(CGAffineTransform)>, \
+                          Middleman_CreateCFTypeRval>)           \
   MACRO(CTFontCreateWithFontDescriptor, RR_ScalarRval, nullptr,  \
         Middleman_Compose<Middleman_CFTypeArg<0>,                \
                           Middleman_BufferFixedSize<1, sizeof(CGAffineTransform)>, \
                           Middleman_CreateCFTypeRval>)                 \
   MACRO(CTFontCreateWithGraphicsFont, RR_ScalarRval, nullptr,    \
         Middleman_Compose<Middleman_CFTypeArg<0>,                \
                           Middleman_BufferFixedSize<1, sizeof(CGAffineTransform)>, \
                           Middleman_CFTypeArg<2>,                \
@@ -584,16 +591,21 @@ namespace recordreplay {
   MACRO(NSRectFill)                                              \
   MACRO(NSSearchPathForDirectoriesInDomains, RR_ScalarRval)      \
   MACRO(NSSetFocusRingStyle, RR_ScalarRval)                      \
   MACRO(NSTemporaryDirectory, RR_ScalarRval)                     \
   MACRO(OSSpinLockLock, nullptr, Preamble_OSSpinLockLock)        \
   MACRO(ReleaseEvent, RR_ScalarRval)                             \
   MACRO(RemoveEventFromQueue, RR_ScalarRval)                     \
   MACRO(RetainEvent, RR_ScalarRval)                              \
+  MACRO(SCDynamicStoreCopyProxies, RR_ScalarRval)                \
+  MACRO(SCDynamicStoreCreate, RR_ScalarRval)                     \
+  MACRO(SCDynamicStoreCreateRunLoopSource, RR_ScalarRval)        \
+  MACRO(SCDynamicStoreKeyCreateProxies, RR_ScalarRval)           \
+  MACRO(SCDynamicStoreSetNotificationKeys, RR_ScalarRval)        \
   MACRO(SendEventToEventTarget, RR_ScalarRval)                   \
   /* These are not public APIs, but other redirected functions may be aliases for */ \
   /* these which are dynamically installed on the first call in a way that our */ \
   /* redirection mechanism doesn't completely account for. */    \
   MACRO(SLDisplayCopyColorSpace, RR_ScalarRval)                  \
   MACRO(SLDisplayIOServicePort, RR_ScalarRval)                   \
   MACRO(SLEventSourceCounterForEventType, RR_ScalarRval)         \
   MACRO(SLMainDisplayID, RR_ScalarRval)                          \
@@ -684,37 +696,31 @@ ReplayInvokeCallback(size_t aCallbackId)
     MOZ_CRASH();
   }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // Middleman Call Helpers
 ///////////////////////////////////////////////////////////////////////////////
 
-static bool
-TestObjCObjectClass(id aObj, const char* aName)
-{
-  Class cls = object_getClass(aObj);
-  while (cls) {
-    const char* className = class_getName(cls);
-    if (!strcmp(className, aName)) {
-      return true;
-    }
-    cls = class_getSuperclass(cls);
-  }
-  return false;
-}
-
 // Inputs that originate from static data in the replaying process itself
 // rather than from previous middleman calls.
 enum class ObjCInputKind {
   StaticClass,
   ConstantString,
 };
 
+// Internal layout of a constant compile time CFStringRef.
+struct CFConstantString {
+  Class mClass;
+  size_t mStuff;
+  char* mData;
+  size_t mLength;
+};
+
 // Capture an Objective C or CoreFoundation input to a call, which may come
 // either from another middleman call, or from static data in the replaying
 // process.
 static void
 Middleman_ObjCInput(MiddlemanCallContext& aCx, id* aThingPtr)
 {
   MOZ_RELEASE_ASSERT(aCx.AccessPreface());
 
@@ -748,32 +754,33 @@ Middleman_ObjCInput(MiddlemanCallContext
         size_t len = strlen(className) + 1;
         aCx.WriteInputScalar(len);
         aCx.WriteInputBytes(className, len);
         return;
       }
     }
 
     // Watch for constant compile time strings baked into the generated code or
-    // stored in system libraries. We can crash here if the object came from
-    // e.g. a replayed pointer from the recording, as can happen if not enough
-    // redirections have middleman call hooks. We could do better here to make
-    // sure the pointer looks like it could be a constant string, but it seems
-    // better and simpler to crash more reliably here than mask problems due to
-    // missing middleman call hooks.
-    if (TestObjCObjectClass(*aThingPtr, "NSString")) {
-      AutoPassThroughThreadEvents pt;
-      CFIndex len = CFStringGetLength((CFStringRef)*aThingPtr);
-      InfallibleVector<UniChar> buffer;
-      buffer.appendN(0, len);
-      CFStringGetCharacters((CFStringRef)*aThingPtr, { 0, len }, buffer.begin());
-      aCx.WriteInputScalar((size_t) ObjCInputKind::ConstantString);
-      aCx.WriteInputScalar(len);
-      aCx.WriteInputBytes(buffer.begin(), len * sizeof(UniChar));
-      return;
+    // stored in system libraries. Be careful when accessing the pointer as in
+    // the case where a middleman call hook for a function is missing the
+    // pointer could have originated from the recording and its address may not
+    // be mapped. In this case we would rather gracefully recover and fail to
+    // paint, instead of crashing.
+    if (MemoryRangeIsTracked(*aThingPtr, sizeof(CFConstantString))) {
+      CFConstantString* str = (CFConstantString*) *aThingPtr;
+      if (str->mClass == objc_lookUpClass("__NSCFConstantString") &&
+          str->mLength <= 4096 && // Sanity check.
+          MemoryRangeIsTracked(str->mData, str->mLength)) {
+        InfallibleVector<UniChar> buffer;
+        NS_ConvertUTF8toUTF16 converted(str->mData, str->mLength);
+        aCx.WriteInputScalar((size_t) ObjCInputKind::ConstantString);
+        aCx.WriteInputScalar(str->mLength);
+        aCx.WriteInputBytes(converted.get(), str->mLength * sizeof(UniChar));
+        return;
+      }
     }
 
     aCx.MarkAsFailed();
     return;
   }
 
   switch ((ObjCInputKind) aCx.ReadInputScalar()) {
   case ObjCInputKind::StaticClass: {
@@ -1207,16 +1214,22 @@ Preamble_pthread_create(CallArguments* a
   auto& startArg = aArguments->Arg<3, void*>();
 
   int detachState;
   int rv = pthread_attr_getdetachstate(attr, &detachState);
   MOZ_RELEASE_ASSERT(rv == 0);
 
   *token = Thread::StartThread((Thread::Callback) start, startArg,
                                detachState == PTHREAD_CREATE_JOINABLE);
+  if (!*token) {
+    // Don't create new threads after diverging from the recording.
+    MOZ_RELEASE_ASSERT(HasDivergedFromRecording());
+    return Preamble_SetError(aArguments);
+  }
+
   aArguments->Rval<ssize_t>() = 0;
   return PreambleResult::Veto;
 }
 
 static PreambleResult
 Preamble_pthread_join(CallArguments* aArguments)
 {
   if (AreThreadEventsPassedThrough()) {
@@ -1747,16 +1760,17 @@ Middleman_CFArrayGetValueAtIndex(Middlem
 
   // We can't probe the array to see what callbacks it uses, so look at where
   // it came from to see whether its elements should be treated as CFTypes.
   MiddlemanCall* call = LookupMiddlemanCall(array);
   bool isCFTypeRval = false;
   if (call) {
     switch (call->mCallId) {
     case CallEvent_CTLineGetGlyphRuns:
+    case CallEvent_CTFontCopyVariationAxes:
     case CallEvent_CTFontDescriptorCreateMatchingFontDescriptors:
       isCFTypeRval = true;
       break;
     default:
       break;
     }
   }
 
@@ -2043,16 +2057,49 @@ RR_CGDataProviderCreateWithData(Stream& 
   auto& releaseData = aArguments->Arg<3, CGDataProviderReleaseDataCallback>();
 
   if (IsReplaying()) {
     // Immediately release the data, since there is no data provider to do it for us.
     releaseData(info, data, size);
   }
 }
 
+static void
+ReleaseDataCallback(void*, const void* aData, size_t)
+{
+  free((void*) aData);
+}
+
+static void
+Middleman_CGDataProviderCreateWithData(MiddlemanCallContext& aCx)
+{
+  Middleman_Buffer<1, 2>(aCx);
+  Middleman_CreateCFTypeRval(aCx);
+
+  auto& info = aCx.mArguments->Arg<0, void*>();
+  auto& data = aCx.mArguments->Arg<1, const void*>();
+  auto& size = aCx.mArguments->Arg<2, size_t>();
+  auto& releaseData = aCx.mArguments->Arg<3, CGDataProviderReleaseDataCallback>();
+
+  // Make a copy of the data that won't be released the next time middleman
+  // calls are reset, in case CoreGraphics decides to hang onto the data
+  // provider after that point.
+  if (aCx.mPhase == MiddlemanCallPhase::MiddlemanInput) {
+    void* newData = malloc(size);
+    memcpy(newData, data, size);
+    data = newData;
+    releaseData = ReleaseDataCallback;
+  }
+
+  // Immediately release the data in the replaying process.
+  if (aCx.mPhase == MiddlemanCallPhase::ReplayInput) {
+    releaseData(info, data, size);
+  }
+}
+
 static PreambleResult
 Preamble_CGPathApply(CallArguments* aArguments)
 {
   if (AreThreadEventsPassedThrough()) {
     return PreambleResult::Redirect;
   }
 
   auto& path = aArguments->Arg<0, CGPathRef>();
--- a/toolkit/recordreplay/ProcessRewind.cpp
+++ b/toolkit/recordreplay/ProcessRewind.cpp
@@ -194,16 +194,21 @@ DivergeFromRecording()
 
   Thread* thread = Thread::Current();
   MOZ_RELEASE_ASSERT(thread->IsMainThread());
 
   if (!thread->HasDivergedFromRecording()) {
     // Reset middleman call state whenever we first diverge from the recording.
     child::SendResetMiddlemanCalls();
 
+    // Make sure all non-main threads are idle before we begin diverging. This
+    // thread's new behavior can change values used by other threads and induce
+    // recording mismatches.
+    Thread::WaitForIdleThreads();
+
     thread->DivergeFromRecording();
   }
 
   gUnhandledDivergeAllowed = true;
 }
 
 extern "C" {
 
--- a/toolkit/recordreplay/Thread.cpp
+++ b/toolkit/recordreplay/Thread.cpp
@@ -232,18 +232,17 @@ Thread::SpawnThread(Thread* aThread)
 }
 
 /* static */ NativeThreadId
 Thread::StartThread(Callback aStart, void* aArgument, bool aNeedsJoin)
 {
   Thread* thread = Thread::Current();
   RecordingEventSection res(thread);
   if (!res.CanAccessEvents()) {
-    EnsureNotDivergedFromRecording();
-    Unreachable();
+    return 0;
   }
 
   MonitorAutoLock lock(*gMonitor);
 
   size_t id = 0;
   if (IsRecording()) {
     // Look for an idle thread.
     for (id = MainThreadId + 1; id <= MaxRecordedThreadId; id++) {
@@ -359,34 +358,26 @@ RecordReplayInterface_InternalAreThreadE
 }
 
 } // extern "C"
 
 ///////////////////////////////////////////////////////////////////////////////
 // Thread Coordination
 ///////////////////////////////////////////////////////////////////////////////
 
-// Whether all threads should attempt to idle.
-static Atomic<bool, SequentiallyConsistent, Behavior::DontPreserve> gThreadsShouldIdle;
-
-// Whether all threads are considered to be idle.
-static Atomic<bool, SequentiallyConsistent, Behavior::DontPreserve> gThreadsAreIdle;
-
 /* static */ void
 Thread::WaitForIdleThreads()
 {
   MOZ_RELEASE_ASSERT(CurrentIsMainThread());
 
-  MOZ_RELEASE_ASSERT(!gThreadsShouldIdle);
-  MOZ_RELEASE_ASSERT(!gThreadsAreIdle);
-  gThreadsShouldIdle = true;
-
   MonitorAutoLock lock(*gMonitor);
   for (size_t i = MainThreadId + 1; i <= MaxRecordedThreadId; i++) {
-    GetById(i)->mUnrecordedWaitNotified = false;
+    Thread* thread = GetById(i);
+    thread->mShouldIdle = true;
+    thread->mUnrecordedWaitNotified = false;
   }
   while (true) {
     bool done = true;
     for (size_t i = MainThreadId + 1; i <= MaxRecordedThreadId; i++) {
       Thread* thread = GetById(i);
       if (!thread->mIdle) {
         done = false;
 
@@ -421,33 +412,31 @@ Thread::WaitForIdleThreads()
       }
     }
     if (done) {
       break;
     }
     MonitorAutoUnlock unlock(*gMonitor);
     WaitNoIdle();
   }
+}
 
-  gThreadsAreIdle = true;
+/* static */ void
+Thread::ResumeSingleIdleThread(size_t aId)
+{
+  GetById(aId)->mShouldIdle = false;
+  Notify(aId);
 }
 
 /* static */ void
 Thread::ResumeIdleThreads()
 {
   MOZ_RELEASE_ASSERT(CurrentIsMainThread());
-
-  MOZ_RELEASE_ASSERT(gThreadsAreIdle);
-  gThreadsAreIdle = false;
-
-  MOZ_RELEASE_ASSERT(gThreadsShouldIdle);
-  gThreadsShouldIdle = false;
-
   for (size_t i = MainThreadId + 1; i <= MaxRecordedThreadId; i++) {
-    Notify(i);
+    ResumeSingleIdleThread(i);
   }
 }
 
 void
 Thread::NotifyUnrecordedWait(const std::function<void()>& aCallback, bool aOnlyWhenDiverged)
 {
   MonitorAutoLock lock(*gMonitor);
   if (mUnrecordedWaitCallback) {
@@ -459,26 +448,27 @@ Thread::NotifyUnrecordedWait(const std::
     MOZ_RELEASE_ASSERT(!mUnrecordedWaitNotified);
   }
 
   mUnrecordedWaitCallback = aCallback;
   mUnrecordedWaitOnlyWhenDiverged = aOnlyWhenDiverged;
 
   // The main thread might be able to make progress now by calling the routine
   // if it is waiting for idle replay threads.
-  if (gThreadsShouldIdle) {
+  if (mShouldIdle) {
     Notify(MainThreadId);
   }
 }
 
 /* static */ void
 Thread::MaybeWaitForCheckpointSave()
 {
   MonitorAutoLock lock(*gMonitor);
-  while (gThreadsShouldIdle) {
+  Thread* thread = Thread::Current();
+  while (thread->mShouldIdle) {
     MonitorAutoUnlock unlock(*gMonitor);
     Wait();
   }
 }
 
 extern "C" {
 
 MOZ_EXPORT void
@@ -526,32 +516,32 @@ Thread::Wait()
   int stackSeparator = 0;
   if (!SaveThreadState(thread->Id(), &stackSeparator)) {
     // We just restored a checkpoint, notify the main thread since it is waiting
     // for all threads to restore their stacks.
     Notify(MainThreadId);
   }
 
   thread->mIdle = true;
-  if (gThreadsShouldIdle) {
+  if (thread->mShouldIdle) {
     // Notify the main thread that we just became idle.
     Notify(MainThreadId);
   }
 
   do {
     // Do the actual waiting for another thread to notify this one.
     WaitNoIdle();
 
     // Rewind this thread if the main thread told us to do so. The main
     // thread is responsible for rewinding its own stack.
     if (ShouldRestoreThreadStack(thread->Id())) {
       RestoreThreadStack(thread->Id());
       Unreachable();
     }
-  } while (gThreadsShouldIdle);
+  } while (thread->mShouldIdle);
 
   thread->mIdle = false;
   thread->SetPassThrough(false);
 }
 
 /* static */ void
 Thread::WaitForever()
 {
--- a/toolkit/recordreplay/Thread.h
+++ b/toolkit/recordreplay/Thread.h
@@ -122,16 +122,19 @@ private:
   size_t mStackSize;
 
   // File descriptor to block on when the thread is idle, fixed at creation.
   FileHandle mIdlefd;
 
   // File descriptor to notify to wake the thread up, fixed at creation.
   FileHandle mNotifyfd;
 
+  // Whether the thread should attempt to idle.
+  Atomic<bool, SequentiallyConsistent, Behavior::DontPreserve> mShouldIdle;
+
   // Whether the thread is waiting on idlefd.
   Atomic<bool, SequentiallyConsistent, Behavior::DontPreserve> mIdle;
 
   // Any callback which should be invoked so the thread can make progress,
   // and whether the callback has been invoked yet while the main thread is
   // waiting for threads to become idle. Protected by the thread monitor.
   std::function<void()> mUnrecordedWaitCallback;
   bool mUnrecordedWaitOnlyWhenDiverged;
@@ -284,16 +287,23 @@ public:
 
   // Wait for all other threads to enter the idle state necessary for saving
   // or restoring a checkpoint. This may only be called on the main thread.
   static void WaitForIdleThreads();
 
   // After WaitForIdleThreads(), the main thread will call this to allow
   // other threads to resume execution.
   static void ResumeIdleThreads();
+
+  // Allow a single thread to resume execution.
+  static void ResumeSingleIdleThread(size_t aId);
+
+  // Return whether this thread is in the idle state entered after
+  // WaitForIdleThreads.
+  bool IsIdle() { return mIdle; }
 };
 
 // This uses a stack pointer instead of TLS to make sure events are passed
 // through, for avoiding thorny reentrance issues.
 class AutoEnsurePassThroughThreadEventsUseStackPointer
 {
   Thread* mThread;
   bool mPassedThrough;
--- a/toolkit/recordreplay/ipc/ChildIPC.cpp
+++ b/toolkit/recordreplay/ipc/ChildIPC.cpp
@@ -540,17 +540,17 @@ PaintFromMainThread()
     gChannel->SendMessage(PaintMessage(navigation::LastNormalCheckpoint(),
                                        gPaintWidth, gPaintHeight));
   }
 }
 
 void
 NotifyPaintComplete()
 {
-  MOZ_RELEASE_ASSERT(Thread::Current()->Id() == gCompositorThreadId);
+  MOZ_RELEASE_ASSERT(!gCompositorThreadId || Thread::Current()->Id() == gCompositorThreadId);
 
   // Notify the main thread in case it is waiting for this paint to complete.
   {
     MonitorAutoLock lock(*gMonitor);
     if (--gNumPendingPaints == 0) {
       gMonitor->Notify();
     }
   }
@@ -572,32 +572,31 @@ Repaint(size_t* aWidth, size_t* aHeight)
     return;
   }
 
   // Ignore the request to repaint if the compositor thread has already
   // diverged from the recording. In this case we have already done a repaint
   // and the last graphics we sent will still be correct.
   Thread* compositorThread = Thread::GetById(gCompositorThreadId);
   if (!compositorThread->WillDivergeFromRecordingSoon()) {
+    // Allow the compositor to diverge from the recording so it can perform
+    // any paint we are about to trigger, or finish any in flight paint that
+    // that existed at the point we are paused at.
+    Thread::GetById(gCompositorThreadId)->SetShouldDivergeFromRecording();
+    Thread::ResumeSingleIdleThread(gCompositorThreadId);
+
     // Create an artifical vsync to see if graphics have changed since the last
     // paint and a new paint is needed.
     NotifyVsyncObserver();
 
-    if (gNumPendingPaints) {
-      // Allow the compositor to diverge from the recording so it can perform
-      // any paint we just triggered, or finish any in flight paint that that
-      // existed at the point we are paused at.
-      Thread::GetById(gCompositorThreadId)->SetShouldDivergeFromRecording();
-
-      // Wait for the compositor to finish all in flight paints, including any
-      // one we just triggered.
-      MonitorAutoLock lock(*gMonitor);
-      while (gNumPendingPaints) {
-        gMonitor->Wait();
-      }
+    // Wait for the compositor to finish all in flight paints, including any
+    // one we just triggered.
+    MonitorAutoLock lock(*gMonitor);
+    while (gNumPendingPaints) {
+      gMonitor->Wait();
     }
   }
 
   if (gDrawTargetBuffer) {
     memcpy(gGraphicsShmem, gDrawTargetBuffer, gDrawTargetBufferSize);
     *aWidth = gPaintWidth;
     *aHeight = gPaintHeight;
   } else {
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/environments/jsm.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/environments/jsm.js
@@ -18,18 +18,18 @@ module.exports = {
     "btoa": false,
     "debug": false,
     "dump": false,
     // These globals are defined in XPCWrappedNativeScope::AttachComponentsObject.
     "Cc": false,
     "Ci": false,
     "Cr": false,
     "Cu": false,
-    // These globals are made available via WebIDL files, see ResolveSystemBinding in:
-    // https://searchfox.org/mozilla-central/source/__GENERATED__/dom/bindings/ResolveSystemBinding.cpp
+    // These globals are made available via WebIDL files, see WebIDLGlobalNameHash.
+    // XXX(nika): We should also explicitly include window-defined globals here now.
     "AbortController": false,
     "AbortSignal": false,
     "AddonManagerPermissions": false,
     "ChannelWrapper": false,
     "ChromeUtils": false,
     "ChromeWorker": false,
     "DOMError": false,
     "DOMException": false,