Merge mozilla-central to autoland
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 14 Sep 2016 12:17:20 +0200
changeset 413595 b9c4a0402a0a90ff4ef6223fcd2fa92422f8bf44
parent 413594 bd37ea2f83d30f87940378c47f7f3cdd61d675bc (current diff)
parent 413530 501e27643a529ce2844924cd47f01ef3150fa0ba (diff)
child 413596 820356f508df8c83436f8ae3de1a3cbbd28b19bf
push id29455
push userahalberstadt@mozilla.com
push dateWed, 14 Sep 2016 14:17:09 +0000
milestone51.0a1
Merge mozilla-central to autoland
js/src/jskwgen.cpp
netwerk/base/nsURLParsers.cpp
testing/docker/desktop-test/bin/test.sh
testing/docker/desktop1604-test/bin/test.sh
testing/web-platform/meta/XMLHttpRequest/event-readystatechange-loaded.htm.ini
testing/web-platform/meta/XMLHttpRequest/open-url-bogus.htm.ini
testing/web-platform/meta/url/url-constructor.html.ini
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -848,44 +848,60 @@
                 return this;
               throw Components.results.NS_NOINTERFACE;
             }
           });
         ]]>
         </body>
       </method>
 
+      <field name="serializationHelper">
+        Cc["@mozilla.org/network/serialization-helper;1"]
+          .getService(Ci.nsISerializationHelper);
+      </field>
+
+      <field name="mIconLoadingPrincipal">
+        null
+      </field>
+
       <method name="setIcon">
         <parameter name="aTab"/>
         <parameter name="aURI"/>
         <parameter name="aLoadingPrincipal"/>
         <body>
           <![CDATA[
             let browser = this.getBrowserForTab(aTab);
             browser.mIconURL = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
+            let loadingPrincipal = aLoadingPrincipal
+              ? aLoadingPrincipal
+              : Services.scriptSecurityManager.getSystemPrincipal();
 
             if (aURI) {
               if (!(aURI instanceof Ci.nsIURI)) {
                 aURI = makeURI(aURI);
               }
-              // We do not serialize the principal from within SessionStore.jsm,
-              // hence if aLoadingPrincipal is null we default to the
-              // systemPrincipal which will allow the favicon to load.
-              let loadingPrincipal = aLoadingPrincipal
-                ? aLoadingPrincipal
-                : Services.scriptSecurityManager.getSystemPrincipal();
               PlacesUIUtils.loadFavicon(browser, loadingPrincipal, aURI);
             }
 
             let sizedIconUrl = browser.mIconURL || "";
             if (sizedIconUrl != aTab.getAttribute("image")) {
-              if (sizedIconUrl)
+              if (sizedIconUrl) {
                 aTab.setAttribute("image", sizedIconUrl);
-              else
+                if (!browser.mIconLoadingPrincipal ||
+                    !browser.mIconLoadingPrincipal.equals(loadingPrincipal)) {
+                  aTab.setAttribute("iconLoadingPrincipal",
+                    this.serializationHelper.serializeToString(loadingPrincipal));
+                  browser.mIconLoadingPrincipal = loadingPrincipal;
+                }
+              }
+              else {
                 aTab.removeAttribute("image");
+                aTab.removeAttribute("iconLoadingPrincipal");
+                delete browser.mIconLoadingPrincipal;
+              }
               this._tabAttrModified(aTab, ["image"]);
             }
 
             this._callProgressListeners(browser, "onLinkIconAvailable", [browser.mIconURL]);
           ]]>
         </body>
       </method>
 
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media.js
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media.js
@@ -538,16 +538,17 @@ function test() {
       for (let test of gTests) {
         info(test.desc);
         yield test.run();
 
         // Cleanup before the next test
         yield expectNoObserverCalled();
       }
     }).then(finish, ex => {
+     Cu.reportError(ex);
      ok(false, "Unexpected Exception: " + ex);
      finish();
     });
   }, true);
   let rootDir = getRootDirectory(gTestPath);
   rootDir = rootDir.replace("chrome://mochitests/content/",
                             "https://example.com/");
   content.location = rootDir + "get_user_media.html";
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_anim.js
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_anim.js
@@ -88,16 +88,17 @@ function test() {
     Task.spawn(function* () {
       yield SpecialPowers.pushPrefEnv({"set": [[PREF_PERMISSION_FAKE, true]]});
 
       for (let test of gTests) {
         info(test.desc);
         yield test.run();
       }
     }).then(finish, ex => {
+     Cu.reportError(ex);
      ok(false, "Unexpected Exception: " + ex);
      finish();
     });
   }, true);
   let rootDir = getRootDirectory(gTestPath);
   rootDir = rootDir.replace("chrome://mochitests/content/",
                             "https://example.com/");
   content.location = rootDir + "get_user_media.html";
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_in_frame.js
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_in_frame.js
@@ -257,16 +257,17 @@ function test() {
       for (let test of gTests) {
         info(test.desc);
         yield test.run();
 
         // Cleanup before the next test
         yield expectNoObserverCalled();
       }
     }).then(finish, ex => {
+     Cu.reportError(ex);
      ok(false, "Unexpected Exception: " + ex);
      finish();
     });
   }, true);
   let rootDir = getRootDirectory(gTestPath);
   rootDir = rootDir.replace("chrome://mochitests/content/",
                             "https://example.com/");
   let url = rootDir + "get_user_media.html";
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_tear_off_tab.js
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_tear_off_tab.js
@@ -90,16 +90,17 @@ function test() {
       for (let test of gTests) {
         info(test.desc);
         yield test.run();
 
         // Cleanup before the next test
         yield expectNoObserverCalled();
       }
     }).then(finish, ex => {
+     Cu.reportError(ex);
      ok(false, "Unexpected Exception: " + ex);
      finish();
     });
   }, true);
   let rootDir = getRootDirectory(gTestPath);
   rootDir = rootDir.replace("chrome://mochitests/content/",
                             "https://example.com/");
   content.location = rootDir + "get_user_media.html";
--- a/browser/components/preferences/in-content/advanced.js
+++ b/browser/components/preferences/in-content/advanced.js
@@ -732,19 +732,17 @@ var gAdvancedPane = {
    *                              requests one
    */
 
   /**
    * Displays the user's certificates and associated options.
    */
   showCertificates: function ()
   {
-    openDialog("chrome://pippki/content/certManager.xul",
-               "mozilla:certmanager",
-               "modal=yes", null);
+    gSubDialog.open("chrome://pippki/content/certManager.xul");
   },
 
   /**
    * Displays a dialog from which the user can manage his security devices.
    */
   showSecurityDevices: function ()
   {
     gSubDialog.open("chrome://pippki/content/device_manager.xul");
--- a/browser/components/sessionstore/SessionHistory.jsm
+++ b/browser/components/sessionstore/SessionHistory.jsm
@@ -167,17 +167,17 @@ var SessionHistoryInternal = {
       let x = {}, y = {};
       shEntry.getScrollPosition(x, y);
       if (x.value != 0 || y.value != 0)
         entry.scroll = x.value + "," + y.value;
     }
 
     // Collect triggeringPrincipal data for the current history entry.
     try {
-      let triggeringPrincipal = this.serializeTriggeringPrincipal(shEntry);
+      let triggeringPrincipal = Utils.serializePrincipal(shEntry.triggeringPrincipal);
       if (triggeringPrincipal) {
         entry.triggeringPrincipal_b64 = triggeringPrincipal;
       }
     } catch (ex) {
       // Not catching anything specific here, just possible errors
       // from writeCompoundObject() and the like.
       debug("Failed serializing triggeringPrincipal data: " + ex);
     }
@@ -214,49 +214,16 @@ var SessionHistoryInternal = {
         entry.children = children;
       }
     }
 
     return entry;
   },
 
   /**
-   * Serialize triggeringPrincipal data contained in the given session history entry.
-   *
-   * @param shEntry
-   *        The session history entry.
-   * @return The base64 encoded triggeringPrincipal data.
-   */
-  serializeTriggeringPrincipal: function (shEntry) {
-    if (!shEntry.triggeringPrincipal) {
-      return null;
-    }
-
-    let binaryStream = Cc["@mozilla.org/binaryoutputstream;1"].
-                       createInstance(Ci.nsIObjectOutputStream);
-    let pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe);
-    pipe.init(false, false, 0, 0xffffffff, null);
-    binaryStream.setOutputStream(pipe.outputStream);
-    binaryStream.writeCompoundObject(shEntry.triggeringPrincipal, Ci.nsIPrincipal, true);
-    binaryStream.close();
-
-    // Now we want to read the data from the pipe's input end and encode it.
-    let scriptableStream = Cc["@mozilla.org/binaryinputstream;1"].
-                           createInstance(Ci.nsIBinaryInputStream);
-    scriptableStream.setInputStream(pipe.inputStream);
-    let triggeringPrincipalBytes =
-      scriptableStream.readByteArray(scriptableStream.available());
-
-    // We can stop doing base64 encoding once our serialization into JSON
-    // is guaranteed to handle all chars in strings, including embedded
-    // nulls.
-    return btoa(String.fromCharCode.apply(null, triggeringPrincipalBytes));
-  },
-
-  /**
    * Restores session history data for a given docShell.
    *
    * @param docShell
    *        The docShell that owns the session history.
    * @param tabData
    *        The tabdata including all history entries.
    */
   restore: function (docShell, tabData) {
@@ -383,26 +350,17 @@ var SessionHistoryInternal = {
     // Bug 1286472. To remain backward compatible we still have to support that
     // field for a few cycles before we can remove it within Bug 1289785.
     if (entry.owner_b64) {
       entry.triggeringPricipal_b64 = entry.owner_b64;
       delete entry.owner_b64;
     }
 
     if (entry.triggeringPrincipal_b64) {
-      var triggeringPrincipalInput = Cc["@mozilla.org/io/string-input-stream;1"]
-                                       .createInstance(Ci.nsIStringInputStream);
-      var binaryData = atob(entry.triggeringPrincipal_b64);
-      triggeringPrincipalInput.setData(binaryData, binaryData.length);
-      var binaryStream = Cc["@mozilla.org/binaryinputstream;1"].
-                         createInstance(Ci.nsIObjectInputStream);
-      binaryStream.setInputStream(triggeringPrincipalInput);
-      try { // Catch possible deserialization exceptions
-        shEntry.triggeringPrincipal = binaryStream.readObject(true);
-      } catch (ex) { debug(ex); }
+      shEntry.triggeringPrincipal = Utils.deserializePrincipal(entry.triggeringPrincipal_b64);
     }
 
     if (entry.children && shEntry instanceof Ci.nsISHContainer) {
       for (var i = 0; i < entry.children.length; i++) {
         //XXXzpao Wallpaper patch for bug 514751
         if (!entry.children[i].url)
           continue;
 
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -806,22 +806,20 @@ var SessionStoreInternal = {
             tab.crop = "center";
           }
         } else if (tab.hasAttribute("customizemode")) {
           win.gCustomizeMode.setTab(tab);
         }
 
         // Restore the tab icon.
         if ("image" in tabData) {
-          // Using null as the loadingPrincipal because serializing
-          // the principal would be overkill. Within SetIcon we
-          // default to the systemPrincipal if aLoadingPrincipal is
-          // null which will allow the favicon to load.
-          win.gBrowser.setIcon(tab, tabData.image, null);
-          TabStateCache.update(browser, {image: null});
+          // Use the serialized contentPrincipal with the new icon load.
+          let loadingPrincipal = Utils.deserializePrincipal(tabData.iconLoadingPrincipal);
+          win.gBrowser.setIcon(tab, tabData.image, loadingPrincipal);
+          TabStateCache.update(browser, { image: null, iconLoadingPrincipal: null });
         }
 
         let event = win.document.createEvent("Events");
         event.initEvent("SSTabRestoring", true, false);
         tab.dispatchEvent(event);
         break;
       case "SessionStore:restoreTabContentStarted":
         if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
@@ -1770,16 +1768,17 @@ var SessionStoreInternal = {
     let tabTitle = this._replaceLoadingTitle(aTab.label, tabbrowser, aTab);
     let {permanentKey} = aTab.linkedBrowser;
 
     let tabData = {
       permanentKey,
       state: tabState,
       title: tabTitle,
       image: tabbrowser.getIcon(aTab),
+      iconLoadingPrincipal: Utils.serializePrincipal(aTab.linkedBrowser.contentPrincipal),
       pos: aTab._tPos,
       closedAt: Date.now()
     };
 
     let closedTabs = this._windows[aWindow.__SSi]._closedTabs;
 
     // Determine whether the tab contains any information worth saving. Note
     // that there might be pending state changes queued in the child that
@@ -3316,16 +3315,17 @@ var SessionStoreInternal = {
       formdata: tabData.formdata || null,
       disallow: tabData.disallow || null,
       pageStyle: tabData.pageStyle || null,
 
       // This information is only needed until the tab has finished restoring.
       // When that's done it will be removed from the cache and we always
       // collect it in TabState._collectBaseTabData().
       image: tabData.image || "",
+      iconLoadingPrincipal: tabData.iconLoadingPrincipal || null,
       userTypedValue: tabData.userTypedValue || "",
       userTypedClear: tabData.userTypedClear || 0
     });
 
     browser.messageManager.sendAsyncMessage("SessionStore:restoreHistory",
                                             {tabData: tabData, epoch: epoch, loadArguments});
 
     // Restore tab attributes.
--- a/browser/components/sessionstore/TabAttributes.jsm
+++ b/browser/components/sessionstore/TabAttributes.jsm
@@ -7,17 +7,19 @@
 this.EXPORTED_SYMBOLS = ["TabAttributes"];
 
 // We never want to directly read or write these attributes.
 // 'image' should not be accessed directly but handled by using the
 //         gBrowser.getIcon()/setIcon() methods.
 // 'muted' should not be accessed directly but handled by using the
 //         tab.linkedBrowser.audioMuted/toggleMuteAudio methods.
 // 'pending' is used internal by sessionstore and managed accordingly.
-const ATTRIBUTES_TO_SKIP = new Set(["image", "muted", "pending"]);
+// 'iconLoadingPrincipal' is same as 'image' that it should be handled by
+//                        using the gBrowser.getIcon()/setIcon() methods.
+const ATTRIBUTES_TO_SKIP = new Set(["image", "muted", "pending", "iconLoadingPrincipal"]);
 
 // A set of tab attributes to persist. We will read a given list of tab
 // attributes when collecting tab data and will re-set those attributes when
 // the given tab data is restored to a new tab.
 this.TabAttributes = Object.freeze({
   persist: function (name) {
     return TabAttributesInternal.persist(name);
   },
--- a/browser/components/sessionstore/TabState.jsm
+++ b/browser/components/sessionstore/TabState.jsm
@@ -11,16 +11,18 @@ const Cu = Components.utils;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 
 XPCOMUtils.defineLazyModuleGetter(this, "PrivacyFilter",
   "resource:///modules/sessionstore/PrivacyFilter.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TabStateCache",
   "resource:///modules/sessionstore/TabStateCache.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TabAttributes",
   "resource:///modules/sessionstore/TabAttributes.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Utils",
+  "resource:///modules/sessionstore/Utils.jsm");
 
 /**
  * Module that contains tab state collection methods.
  */
 this.TabState = Object.freeze({
   update: function (browser, data) {
     TabStateInternal.update(browser, data);
   },
@@ -117,16 +119,21 @@ var TabStateInternal = {
     // be read from the tab/browser every time we collect data.
 
     // Store the tab icon.
     if (!("image" in tabData)) {
       let tabbrowser = tab.ownerGlobal.gBrowser;
       tabData.image = tabbrowser.getIcon(tab);
     }
 
+    // Store the serialized contentPrincipal of this tab to use for the icon.
+    if (!("iconLoadingPrincipal" in tabData)) {
+      tabData.iconLoadingPrincipal = Utils.serializePrincipal(browser.contentPrincipal);
+    }
+
     // If there is a userTypedValue set, then either the user has typed something
     // in the URL bar, or a new tab was opened with a URI to load.
     // If so, we also track whether we were still in the process of loading something.
     if (!("userTypedValue" in tabData) && browser.userTypedValue) {
       tabData.userTypedValue = browser.userTypedValue;
       // We always used to keep track of the loading state as an integer, where
       // '0' indicated the user had typed since the last load (or no load was
       // ongoing), and any positive value indicated we had started a load since
--- a/browser/components/sessionstore/Utils.jsm
+++ b/browser/components/sessionstore/Utils.jsm
@@ -6,16 +6,21 @@
 
 this.EXPORTED_SYMBOLS = ["Utils"];
 
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/Services.jsm", this);
+Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
+
+XPCOMUtils.defineLazyServiceGetter(this, "serializationHelper",
+                                   "@mozilla.org/network/serialization-helper;1",
+                                   "nsISerializationHelper");
 
 this.Utils = Object.freeze({
   makeURI: function (url) {
     return Services.io.newURI(url, null, null);
   },
 
   makeInputStream: function (aString) {
     let stream = Cc["@mozilla.org/io/string-input-stream;1"].
@@ -55,10 +60,40 @@ this.Utils = Object.freeze({
   shallowCopy: function (obj) {
     let retval = {};
 
     for (let key of Object.keys(obj)) {
       retval[key] = obj[key];
     }
 
     return retval;
+  },
+
+  /**
+   * Serialize principal data.
+   *
+   * @param {nsIPrincipal} principal The principal to serialize.
+   * @return {String} The base64 encoded principal data.
+   */
+  serializePrincipal(principal) {
+    if (!principal)
+      return null;
+
+    return serializationHelper.serializeToString(principal);
+  },
+
+  /**
+   * Deserialize a base64 encoded principal (serialized with
+   * Utils::serializePrincipal).
+   *
+   * @param {String} principal_b64 A base64 encoded serialized principal.
+   * @return {nsIPrincipal} A deserialized principal.
+   */
+  deserializePrincipal(principal_b64) {
+    if (!principal_b64)
+      return null;
+
+    let principal = serializationHelper.deserializeObject(principal_b64);
+    principal.QueryInterface(Ci.nsIPrincipal);
+
+    return principal;
   }
 });
--- a/browser/components/sessionstore/test/browser_attributes.js
+++ b/browser/components/sessionstore/test/browser_attributes.js
@@ -12,28 +12,31 @@ const PREF = "browser.sessionstore.resto
 add_task(function* test() {
   Services.prefs.setBoolPref(PREF, true)
   registerCleanupFunction(() => Services.prefs.clearUserPref(PREF));
 
   // Add a new tab with a nice icon.
   let tab = gBrowser.addTab("about:robots");
   yield promiseBrowserLoaded(tab.linkedBrowser);
 
-  // Check that the tab has an 'image' attribute.
+  // Check that the tab has 'image' and 'iconLoadingPrincipal' attributes.
   ok(tab.hasAttribute("image"), "tab.image exists");
+  ok(tab.hasAttribute("iconLoadingPrincipal"), "tab.iconLoadingPrincipal exists");
 
   tab.toggleMuteAudio();
   // Check that the tab has a 'muted' attribute.
   ok(tab.hasAttribute("muted"), "tab.muted exists");
 
   // Make sure we do not persist 'image' or 'muted' attributes.
   ss.persistTabAttribute("image");
   ss.persistTabAttribute("muted");
+  ss.persistTabAttribute("iconLoadingPrincipal");
   let {attributes} = JSON.parse(ss.getTabState(tab));
   ok(!("image" in attributes), "'image' attribute not saved");
+  ok(!("iconLoadingPrincipal" in attributes), "'iconLoadingPrincipal' attribute not saved");
   ok(!("muted" in attributes), "'muted' attribute not saved");
   ok(!("custom" in attributes), "'custom' attribute not saved");
 
   // Test persisting a custom attribute.
   tab.setAttribute("custom", "foobar");
   ss.persistTabAttribute("custom");
 
   ({attributes} = JSON.parse(ss.getTabState(tab)));
--- a/browser/components/sessionstore/test/browser_label_and_icon.js
+++ b/browser/components/sessionstore/test/browser_label_and_icon.js
@@ -1,13 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
+const {classes: Cc, interfaces: Ci} = Components;
+
 /**
  * Make sure that tabs are restored on demand as otherwise the tab will start
  * loading immediately and we can't check its icon and label.
  */
 add_task(function setup() {
   Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", true);
 
   registerCleanupFunction(() => {
@@ -34,11 +36,18 @@ add_task(function test_label_and_icon() 
   tab = gBrowser.addTab("about:blank");
   ss.setTabState(tab, state);
   yield promiseTabRestoring(tab);
 
   // Check that label and icon are set for the restoring tab.
   ok(gBrowser.getIcon(tab).startsWith("data:image/png;"), "icon is set");
   is(tab.label, "Gort! Klaatu barada nikto!", "label is set");
 
+  let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
+                    .getService(Ci.nsISerializationHelper);
+  let serializedPrincipal = tab.getAttribute("iconLoadingPrincipal");
+  let iconLoadingPrincipal = serhelper.deserializeObject(serializedPrincipal)
+                                      .QueryInterface(Ci.nsIPrincipal);
+  is(iconLoadingPrincipal.origin, "about:robots", "correct loadingPrincipal used");
+
   // Cleanup.
   yield promiseRemoveTab(tab);
 });
--- a/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/Preferences.jsm
+++ b/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/Preferences.jsm
@@ -5,81 +5,102 @@
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["Preferences"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/Timer.jsm");
 Cu.import("resource://testing-common/TestUtils.jsm");
 Cu.import("resource://testing-common/ContentTask.jsm");
 
 this.Preferences = {
 
   init(libDir) {
-    Services.prefs.setBoolPref("browser.preferences.inContent", true);
-
     let panes = [
       ["paneGeneral", null],
       ["paneSearch", null],
       ["paneContent", null],
       ["paneApplications", null],
       ["panePrivacy", null],
+      ["panePrivacy", null, DNTDialog],
       ["paneSecurity", null],
       ["paneSync", null],
       ["paneAdvanced", "generalTab"],
       ["paneAdvanced", "dataChoicesTab"],
       ["paneAdvanced", "networkTab"],
       ["paneAdvanced", "updateTab"],
       ["paneAdvanced", "encryptionTab"],
+      ["paneAdvanced", "encryptionTab", certManager],
     ];
-    for (let [primary, advanced] of panes) {
+    for (let [primary, advanced, customFn] of panes) {
       let configName = primary.replace(/^pane/, "prefs") + (advanced ? "-" + advanced : "");
+      if (customFn) {
+        configName += "-" + customFn.name;
+      }
       this.configurations[configName] = {};
-      this.configurations[configName].applyConfig = prefHelper.bind(null, primary, advanced);
+      this.configurations[configName].applyConfig = prefHelper.bind(null, primary, advanced, customFn);
     }
   },
 
-  configurations: {
-    "panePrivacy-DNTDialog": {
-      applyConfig: Task.async(function*() {
-        let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
-        yield prefHelper("panePrivacy", null);
-
-        yield ContentTask.spawn(browserWindow.gBrowser.selectedBrowser, null, function* () {
-          content.document.getElementById("doNotTrackSettings").click();
-        });
-      }),
-    },
-  },
+  configurations: {},
 };
 
-let prefHelper = Task.async(function*(primary, advanced) {
+let prefHelper = Task.async(function*(primary, advanced = null, customFn = null) {
   let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
-  let selectedBrowser = browserWindow.gBrowser;
+  let selectedBrowser = browserWindow.gBrowser.selectedBrowser;
+
+  // close any dialog that might still be open
+  yield ContentTask.spawn(selectedBrowser, null, function*() {
+    if (!content.window.gSubDialog) {
+      return;
+    }
+    content.window.gSubDialog.close();
+  });
+
   let readyPromise = null;
   if (selectedBrowser.currentURI.specIgnoringRef == "about:preferences") {
-    readyPromise = new Promise((resolve) => {
-      browserWindow.addEventListener("MozAfterPaint", function paneSwitch() {
-        browserWindow.removeEventListener("MozAfterPaint", paneSwitch);
-        resolve();
-      });
-    });
-
+    if (selectedBrowser.currentURI.spec == "about:preferences#" + primary.replace(/^pane/, "")) {
+      // We're already on the correct pane.
+      readyPromise = Promise.resolve();
+    } else {
+      readyPromise = paintPromise(browserWindow);
+    }
   } else {
     readyPromise = TestUtils.topicObserved("advanced-pane-loaded");
   }
 
   if (primary == "paneAdvanced") {
     browserWindow.openAdvancedPreferences(advanced);
   } else {
     browserWindow.openPreferences(primary);
   }
 
   yield readyPromise;
 
-  // close any dialog that might still be open
-  yield ContentTask.spawn(selectedBrowser.selectedBrowser, null, function*() {
-    content.window.gSubDialog.close();
+  if (customFn) {
+    let customPaintPromise = paintPromise(browserWindow);
+    yield* customFn(selectedBrowser);
+    yield customPaintPromise;
+  }
+});
+
+function paintPromise(browserWindow) {
+  return new Promise((resolve) => {
+    browserWindow.addEventListener("MozAfterPaint", function onPaint() {
+      browserWindow.removeEventListener("MozAfterPaint", onPaint);
+      resolve();
+    });
   });
-});
+}
+
+function* DNTDialog(aBrowser) {
+  yield ContentTask.spawn(aBrowser, null, function* () {
+    content.document.getElementById("doNotTrackSettings").click();
+  });
+}
+
+function* certManager(aBrowser) {
+  yield ContentTask.spawn(aBrowser, null, function* () {
+    content.document.getElementById("viewCertificatesButton").click();
+  });
+}
--- a/config/recurse.mk
+++ b/config/recurse.mk
@@ -166,16 +166,12 @@ toolkit/library/target: widget/gtk/mozgt
 endif
 ifdef MOZ_LDAP_XPCOM
 ldap/target: config/external/nss/target mozglue/build/target
 toolkit/library/target: ldap/target
 endif
 ifeq ($(MOZ_REPLACE_MALLOC_LINKAGE),dummy library)
 mozglue/build/target memory/replace/logalloc/replay/target: memory/replace/dummy/target
 endif
-# js/src/target can end up invoking js/src/host rules (through object files
-# depending on jsautokw.h, which depends on host_jskwgen, and that can't
-# happen at the same time (bug #1146738)
-js/src/target: js/src/host
 endif
 # Most things are built during compile (target/host), but some things happen during export
 # Those need to depend on config/export for system wrappers.
 $(addprefix build/unix/stdc++compat/,target host) build/clang-plugin/target: config/export
--- a/devtools/client/debugger/test/mochitest/browser2.ini
+++ b/devtools/client/debugger/test/mochitest/browser2.ini
@@ -202,17 +202,17 @@ skip-if = e10s && debug
 skip-if = e10s && debug
 [browser_dbg_pretty-print-08.js]
 skip-if = e10s && debug
 [browser_dbg_pretty-print-09.js]
 skip-if = e10s && debug
 [browser_dbg_pretty-print-10.js]
 skip-if = e10s && debug
 [browser_dbg_pretty-print-11.js]
-skip-if = e10s && debug
+skip-if = e10s && debug || true
 [browser_dbg_pretty-print-12.js]
 skip-if = e10s && debug
 [browser_dbg_pretty-print-13.js]
 skip-if = e10s && debug
 [browser_dbg_pretty-print-on-paused.js]
 skip-if = e10s && debug
 [browser_dbg_progress-listener-bug.js]
 skip-if = e10s && debug
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -546,18 +546,17 @@ Toolbox.prototype = {
     shortcuts.on(L10N.getStr("toolbox.help.key"), selectOptions);
   },
 
   _splitConsoleOnKeypress: function (e) {
     if (e.keyCode === KeyCodes.DOM_VK_ESCAPE) {
       this.toggleSplitConsole();
       // If the debugger is paused, don't let the ESC key stop any pending
       // navigation.
-      let jsdebugger = this.getPanel("jsdebugger");
-      if (jsdebugger && jsdebugger.panelWin.gThreadClient.state == "paused") {
+      if (this._threadClient.state == "paused") {
         e.preventDefault();
       }
     }
   },
 
   /**
    * Add a shortcut key that should work when a split console
    * has focus to the toolbox.
--- a/devtools/client/responsive.html/actions/touch-simulation.js
+++ b/devtools/client/responsive.html/actions/touch-simulation.js
@@ -7,16 +7,16 @@
 "use strict";
 
 const {
   UPDATE_TOUCH_SIMULATION_ENABLED
 } = require("./index");
 
 module.exports = {
 
-  updateTouchSimulationEnabled(enabled) {
+  updateTouchSimulationEnabled(enabled = false) {
     return {
       type: UPDATE_TOUCH_SIMULATION_ENABLED,
       enabled,
     };
   },
 
 };
--- a/devtools/client/responsive.html/app.js
+++ b/devtools/client/responsive.html/app.js
@@ -43,16 +43,17 @@ let App = createClass({
   },
 
   onChangeViewportDevice(id, device) {
     window.postMessage({
       type: "change-viewport-device",
       device,
     }, "*");
     this.props.dispatch(changeDevice(id, device.name));
+    this.props.dispatch(updateTouchSimulationEnabled(device.touch));
   },
 
   onContentResize({ width, height }) {
     window.postMessage({
       type: "content-resize",
       width,
       height,
     }, "*");
--- a/devtools/client/responsive.html/manager.js
+++ b/devtools/client/responsive.html/manager.js
@@ -419,19 +419,20 @@ ResponsiveUI.prototype = {
     let { browserWindow, tab } = this;
 
     if (event.origin !== "chrome://devtools") {
       return;
     }
 
     switch (event.data.type) {
       case "change-viewport-device":
-        let { userAgent, pixelRatio } = event.data.device;
+        let { userAgent, pixelRatio, touch } = event.data.device;
         this.updateUserAgent(userAgent);
         this.updateDPPX(pixelRatio);
+        this.updateTouchSimulation(touch);
         break;
       case "content-resize":
         let { width, height } = event.data;
         this.emit("content-resize", {
           width,
           height,
         });
         break;
--- a/devtools/client/responsive.html/test/browser/browser_device_change.js
+++ b/devtools/client/responsive.html/test/browser/browser_device_change.js
@@ -35,83 +35,81 @@ addRDMTask(TEST_URL, function* ({ ui, ma
 
   // Wait until the viewport has been added and the device list has been loaded
   yield waitUntilState(store, state => state.viewports.length == 1
     && state.devices.listState == Types.deviceListState.LOADED);
 
   // Test defaults
   testViewportDimensions(ui, 320, 480);
   yield testUserAgent(ui, DEFAULT_UA);
-  testDevicePixelRatio(yield getViewportDevicePixelRatio(ui), DEFAULT_DPPX);
+  yield testDevicePixelRatio(ui, DEFAULT_DPPX);
+  yield testTouchEventsOverride(ui, false);
   testViewportSelectLabel(ui, "no device selected");
 
-  let waitingPixelRatio = onceDevicePixelRatioChange(ui);
-
-  // Test device with custom UA
+  // Test device with custom properties
   yield switchDevice(ui, "Fake Phone RDM Test");
   yield waitForViewportResizeTo(ui, testDevice.width, testDevice.height);
   yield testUserAgent(ui, testDevice.userAgent);
-
-  // Test device with custom pixelRatio
-  testDevicePixelRatio(yield waitingPixelRatio, testDevice.pixelRatio);
-  waitingPixelRatio = onceDevicePixelRatioChange(ui);
+  yield testDevicePixelRatio(ui, testDevice.pixelRatio);
+  yield testTouchEventsOverride(ui, true);
 
   // Test resetting device when resizing viewport
   yield testViewportResize(ui, ".viewport-vertical-resize-handle",
     [-10, -10], [testDevice.width, testDevice.height - 10], [0, -10], ui);
   yield testUserAgent(ui, DEFAULT_UA);
+  yield testDevicePixelRatio(ui, DEFAULT_DPPX);
+  yield testTouchEventsOverride(ui, false);
   testViewportSelectLabel(ui, "no device selected");
-  testDevicePixelRatio(yield waitingPixelRatio, DEFAULT_DPPX);
 
-  // Test device where UA field is blank
+  // Test device with generic properties
   yield switchDevice(ui, "Laptop (1366 x 768)");
   yield waitForViewportResizeTo(ui, 1366, 768);
   yield testUserAgent(ui, DEFAULT_UA);
+  yield testDevicePixelRatio(ui, 1);
+  yield testTouchEventsOverride(ui, false);
 
   ok(removeDevice(testDevice),
     "Test Device properly removed.");
 });
 
 function testViewportDimensions(ui, w, h) {
   let viewport = ui.toolWindow.document.querySelector(".viewport-content");
 
   is(ui.toolWindow.getComputedStyle(viewport).getPropertyValue("width"),
      `${w}px`, `Viewport should have width of ${w}px`);
   is(ui.toolWindow.getComputedStyle(viewport).getPropertyValue("height"),
      `${h}px`, `Viewport should have height of ${h}px`);
 }
 
-function testViewportSelectLabel(ui, label) {
+function testViewportSelectLabel(ui, expected) {
   let select = ui.toolWindow.document.querySelector(".viewport-device-selector");
-  is(select.selectedOptions[0].textContent, label,
-     `Select label should be changed to ${label}`);
+  is(select.selectedOptions[0].textContent, expected,
+     `Select label should be changed to ${expected}`);
 }
 
-function* testUserAgent(ui, value) {
+function* testUserAgent(ui, expected) {
   let ua = yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
     return content.navigator.userAgent;
   });
-  is(ua, value, `UA should be set to ${value}`);
+  is(ua, expected, `UA should be set to ${expected}`);
+}
+
+function* testDevicePixelRatio(ui, expected) {
+  let dppx = yield getViewportDevicePixelRatio(ui);
+  is(dppx, expected, `devicePixelRatio should be set to ${expected}`);
 }
 
-function testDevicePixelRatio(dppx, expected) {
-  is(dppx, expected, `devicePixelRatio should be set to ${expected}`);
+function* testTouchEventsOverride(ui, expected) {
+  let { document } = ui.toolWindow;
+  let touchButton = document.querySelector("#global-touch-simulation-button");
+
+  let flag = yield ui.emulationFront.getTouchEventsOverride();
+  is(flag === Ci.nsIDocShell.TOUCHEVENTS_OVERRIDE_ENABLED, expected,
+    `Touch events override should be ${expected ? "enabled" : "disabled"}`);
+  is(touchButton.classList.contains("active"), expected,
+    `Touch simulation button should be ${expected ? "" : "not"} active.`);
 }
 
 function* getViewportDevicePixelRatio(ui) {
   return yield ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
     return content.devicePixelRatio;
   });
 }
-
-function onceDevicePixelRatioChange(ui) {
-  return ContentTask.spawn(ui.getViewportBrowser(), {}, function* () {
-    let pixelRatio = content.devicePixelRatio;
-    let mql = content.matchMedia(`(resolution: ${pixelRatio}dppx)`);
-
-    return new Promise(resolve => {
-      mql.addListener(function listener() {
-        mql.removeListener(listener);
-        resolve(content.devicePixelRatio);
-      });
-    });
-  });
-}
--- a/devtools/client/webconsole/test/browser_webconsole_output_06.js
+++ b/devtools/client/webconsole/test/browser_webconsole_output_06.js
@@ -193,17 +193,17 @@ var inputTests = [
     printOutput: "[object Object]",
     inspectable: true,
     variablesViewLabel: "Object[1]",
   },
 
   // 21
   {
     input: '({0: "a", 1: "b", length: 1})',
-    output: 'Object { 1: "b", length: 1, 1 more\u2026 }',
+    output: 'Object { 0: "a", 1: "b", length: 1 }',
     printOutput: "[object Object]",
     inspectable: true,
     variablesViewLabel: "Object",
   },
 
   // 22
   {
     input: '({0: "a", 1: "b", length: 2})',
@@ -220,17 +220,17 @@ var inputTests = [
     printOutput: "[object Object]",
     inspectable: true,
     variablesViewLabel: "Object[3]",
   },
 
   // 24
   {
     input: '({0: "a", 2: "b", length: 2})',
-    output: 'Object { 2: "b", length: 2, 1 more\u2026 }',
+    output: 'Object { 0: "a", 2: "b", length: 2 }',
     printOutput: "[object Object]",
     inspectable: true,
     variablesViewLabel: "Object",
   },
 
   // 25
   {
     input: '({0: "a", 2: "b", length: 3})',
@@ -238,26 +238,26 @@ var inputTests = [
     printOutput: "[object Object]",
     inspectable: true,
     variablesViewLabel: "Object[3]",
   },
 
   // 26
   {
     input: '({0: "a", b: "b", length: 1})',
-    output: 'Object { b: "b", length: 1, 1 more\u2026 }',
+    output: 'Object { 0: "a", b: "b", length: 1 }',
     printOutput: "[object Object]",
     inspectable: true,
     variablesViewLabel: "Object",
   },
 
   // 27
   {
     input: '({0: "a", b: "b", length: 2})',
-    output: 'Object { b: "b", length: 2, 1 more\u2026 }',
+    output: 'Object { 0: "a", b: "b", length: 2 }',
     printOutput: "[object Object]",
     inspectable: true,
     variablesViewLabel: "Object",
   },
 
   // 28
   {
     input: '({42: "a"})',
--- a/devtools/server/actors/object.js
+++ b/devtools/server/actors/object.js
@@ -1085,38 +1085,38 @@ function enumWeakSetEntries(objectActor)
   };
 }
 
 /**
  * Functions for adding information to ObjectActor grips for the purpose of
  * having customized output. This object holds arrays mapped by
  * Debugger.Object.prototype.class.
  *
- * In each array you can add functions that take two
+ * In each array you can add functions that take three
  * arguments:
  *   - the ObjectActor instance and its hooks to make a preview for,
  *   - the grip object being prepared for the client,
  *   - the raw JS object after calling Debugger.Object.unsafeDereference(). This
  *   argument is only provided if the object is safe for reading properties and
  *   executing methods. See DevToolsUtils.isSafeJSObject().
  *
  * Functions must return false if they cannot provide preview
  * information for the debugger object, or true otherwise.
  */
 DebuggerServer.ObjectActorPreviewers = {
-  String: [function (objectActor, grip) {
-    return wrappedPrimitivePreviewer("String", String, objectActor, grip);
+  String: [function (objectActor, grip, rawObj) {
+    return wrappedPrimitivePreviewer("String", String, objectActor, grip, rawObj);
   }],
 
-  Boolean: [function (objectActor, grip) {
-    return wrappedPrimitivePreviewer("Boolean", Boolean, objectActor, grip);
+  Boolean: [function (objectActor, grip, rawObj) {
+    return wrappedPrimitivePreviewer("Boolean", Boolean, objectActor, grip, rawObj);
   }],
 
-  Number: [function (objectActor, grip) {
-    return wrappedPrimitivePreviewer("Number", Number, objectActor, grip);
+  Number: [function (objectActor, grip, rawObj) {
+    return wrappedPrimitivePreviewer("Number", Number, objectActor, grip, rawObj);
   }],
 
   Function: [function ({obj, hooks}, grip) {
     if (obj.name) {
       grip.name = obj.name;
     }
 
     if (obj.displayName) {
@@ -1374,47 +1374,46 @@ DebuggerServer.ObjectActorPreviewers = {
  *        The class to expect, eg. String. The valueOf() method of the class is
  *        invoked on the given object.
  * @param ObjectActor objectActor
  *        The object actor
  * @param Object grip
  *        The result grip to fill in
  * @return Booolean true if the object was handled, false otherwise
  */
-function wrappedPrimitivePreviewer(className, classObj, objectActor, grip) {
+function wrappedPrimitivePreviewer(className, classObj, objectActor, grip, rawObj) {
   let {obj, hooks} = objectActor;
 
   if (!obj.proto || obj.proto.class != className) {
     return false;
   }
 
-  let raw = obj.unsafeDereference();
   let v = null;
   try {
-    v = classObj.prototype.valueOf.call(raw);
+    v = classObj.prototype.valueOf.call(rawObj);
   } catch (ex) {
     // valueOf() can throw if the raw JS object is "misbehaved".
     return false;
   }
 
   if (v === null) {
     return false;
   }
 
-  let canHandle = GenericObject(objectActor, grip, className === "String");
+  let canHandle = GenericObject(objectActor, grip, rawObj, className === "String");
   if (!canHandle) {
     return false;
   }
 
   grip.preview.wrappedValue =
     hooks.createValueGrip(makeDebuggeeValueIfNeeded(obj, v));
   return true;
 }
 
-function GenericObject(objectActor, grip, specialStringBehavior = false) {
+function GenericObject(objectActor, grip, rawObj, specialStringBehavior = false) {
   let {obj, hooks} = objectActor;
   if (grip.preview || grip.displayString || hooks.getGripDepth() > 1) {
     return false;
   }
 
   let i = 0, names = [];
   let preview = grip.preview = {
     kind: "Object",
@@ -1854,17 +1853,19 @@ DebuggerServer.ObjectActorPreviewers.Obj
       } else {
         items.push(null);
       }
     }
 
     return true;
   },
 
-  GenericObject,
+  function Object(objectActor, grip, rawObj) {
+    return GenericObject(objectActor, grip, rawObj, /* specialStringBehavior = */ false);
+  },
 ];
 
 /**
  * Get thisDebugger.Object referent's `promiseState`.
  *
  * @returns Object
  *          An object of one of the following forms:
  *          - { state: "pending" }
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -12041,21 +12041,16 @@ nsDocument::UpdateVisibilityState()
 {
   dom::VisibilityState oldState = mVisibilityState;
   mVisibilityState = GetVisibilityState();
   if (oldState != mVisibilityState) {
     nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
                                          NS_LITERAL_STRING("visibilitychange"),
                                          /* bubbles = */ true,
                                          /* cancelable = */ false);
-    nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
-                                         NS_LITERAL_STRING("mozvisibilitychange"),
-                                         /* bubbles = */ true,
-                                         /* cancelable = */ false);
-
     EnumerateActivityObservers(NotifyActivityChanged, nullptr);
   }
 
   if (mVisibilityState == dom::VisibilityState::Visible) {
     MaybeActiveMediaComponents();
   }
 }
 
@@ -12105,37 +12100,23 @@ nsDocument::MaybeActiveMediaComponents()
 
   mEverInForeground = true;
   if (GetWindow()->GetMediaSuspend() == nsISuspendedTypes::SUSPENDED_BLOCK) {
     GetWindow()->SetMediaSuspend(nsISuspendedTypes::NONE_SUSPENDED);
   }
 }
 
 NS_IMETHODIMP
-nsDocument::GetMozHidden(bool* aHidden)
-{
-  *aHidden = MozHidden();
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsDocument::GetHidden(bool* aHidden)
 {
   *aHidden = Hidden();
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDocument::GetMozVisibilityState(nsAString& aState)
-{
-  WarnOnceAbout(ePrefixedVisibilityAPI);
-  return GetVisibilityState(aState);
-}
-
-NS_IMETHODIMP
 nsDocument::GetVisibilityState(nsAString& aState)
 {
   const EnumEntry& entry =
     VisibilityStateValues::strings[static_cast<int>(mVisibilityState)];
   aState.AssignASCII(entry.value, entry.length);
   return NS_OK;
 }
 
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2601,30 +2601,20 @@ public:
   {
     UnlockPointer(this);
   }
 #ifdef MOZILLA_INTERNAL_API
   bool Hidden() const
   {
     return mVisibilityState != mozilla::dom::VisibilityState::Visible;
   }
-  bool MozHidden() const
-  {
-    WarnOnceAbout(ePrefixedVisibilityAPI);
-    return Hidden();
-  }
   mozilla::dom::VisibilityState VisibilityState() const
   {
     return mVisibilityState;
   }
-  mozilla::dom::VisibilityState MozVisibilityState() const
-  {
-    WarnOnceAbout(ePrefixedVisibilityAPI);
-    return VisibilityState();
-  }
 #endif
   virtual mozilla::dom::StyleSheetList* StyleSheets() = 0;
   void GetSelectedStyleSheetSet(nsAString& aSheetSet);
   virtual void SetSelectedStyleSheetSet(const nsAString& aSheetSet) = 0;
   virtual void GetLastStyleSheetSet(nsString& aSheetSet) = 0;
   void GetPreferredStyleSheetSet(nsAString& aSheetSet);
   virtual mozilla::dom::DOMStringList* StyleSheetSets() = 0;
   virtual void EnableStyleSheetsForSet(const nsAString& aSheetSet) = 0;
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1684,17 +1684,18 @@ CanvasRenderingContext2D::SetErrorState(
 }
 
 void
 CanvasRenderingContext2D::RegisterAllocation()
 {
   // XXX - It would make more sense to track the allocation in
   // PeristentBufferProvider, rather than here.
   static bool registered = false;
-  if (!registered) {
+  // FIXME: Disable the reporter for now, see bug 1241865
+  if (!registered && false) {
     registered = true;
     RegisterStrongMemoryReporter(new Canvas2dPixelsReporter());
   }
 
   gCanvasAzureMemoryUsed += mWidth * mHeight * 4;
   JSContext* context = nsContentUtils::GetCurrentJSContext();
   if (context) {
     JS_updateMallocCounter(context, mWidth * mHeight * 4);
--- a/dom/canvas/OffscreenCanvas.cpp
+++ b/dom/canvas/OffscreenCanvas.cpp
@@ -142,20 +142,19 @@ OffscreenCanvas::GetContext(JSContext* a
         contextType == CanvasContextType::WebGL2) {
       WebGLContext* webGL = static_cast<WebGLContext*>(mCurrentContext.get());
       gl::GLContext* gl = webGL->GL();
       mCanvasRenderer->mContext = mCurrentContext;
       mCanvasRenderer->SetActiveThread();
       mCanvasRenderer->mGLContext = gl;
       mCanvasRenderer->SetIsAlphaPremultiplied(webGL->IsPremultAlpha() || !gl->Caps().alpha);
 
-      if (ImageBridgeChild::IsCreated()) {
+      if (RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton()) {
         TextureFlags flags = TextureFlags::ORIGIN_BOTTOM_LEFT;
-        mCanvasClient = ImageBridgeChild::GetSingleton()->
-          CreateCanvasClient(CanvasClient::CanvasClientTypeShSurf, flags);
+        mCanvasClient = imageBridge->CreateCanvasClient(CanvasClient::CanvasClientTypeShSurf, flags);
         mCanvasRenderer->SetCanvasClient(mCanvasClient);
 
         gl::GLScreenBuffer* screen = gl->Screen();
         gl::SurfaceCaps caps = screen->mCaps;
         auto forwarder = mCanvasClient->GetForwarder();
 
         UniquePtr<gl::SurfaceFactory> factory =
           gl::GLScreenBuffer::CreateFactory(gl, caps, forwarder, flags);
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -681,18 +681,21 @@ WebGLContext::CreateAndInitGLWith(FnCrea
     while (!fallbackCaps.empty()) {
         const gl::SurfaceCaps& caps = fallbackCaps.front();
         potentialGL = fnCreateGL(caps, flags, this, out_failReasons);
         if (potentialGL)
             break;
 
         fallbackCaps.pop();
     }
-    if (!potentialGL)
+    if (!potentialGL) {
+        out_failReasons->push_back(FailureReason("FEATURE_FAILURE_WEBGL_EXHAUSTED_CAPS",
+                                                 "Exhausted GL driver caps."));
         return false;
+    }
 
     FailureReason reason;
 
     mGL_OnlyClearInDestroyResourcesAndContext = potentialGL;
     MOZ_RELEASE_ASSERT(gl);
     if (!InitAndValidateGL(&reason)) {
         DestroyResourcesAndContext();
         MOZ_RELEASE_ASSERT(!gl);
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -647,42 +647,36 @@ EventListenerManager::RemoveEventListene
 {
   if (!aListenerHolder || !aEventMessage || mClearingListeners) {
     return;
   }
 
   Listener* listener;
 
   uint32_t count = mListeners.Length();
-  uint32_t typeCount = 0;
   bool deviceType = IsDeviceType(aEventMessage);
 
   RefPtr<EventListenerManager> kungFuDeathGrip(this);
 
   for (uint32_t i = 0; i < count; ++i) {
     listener = &mListeners.ElementAt(i);
     if (EVENT_TYPE_EQUALS(listener, aEventMessage, aUserType, aTypeString,
                           aAllEvents)) {
-      ++typeCount;
       if (listener->mListener == aListenerHolder &&
           listener->mFlags.EqualsForRemoval(aFlags)) {
         mListeners.RemoveElementAt(i);
-        --count;
         NotifyEventListenerRemoved(aUserType);
-        if (!deviceType) {
-          return;
+        if (!aAllEvents && deviceType) {
+          DisableDevice(aEventMessage);
         }
-        --typeCount;
+        return;
       }
     }
   }
 
-  if (!aAllEvents && deviceType && typeCount == 0) {
-    DisableDevice(aEventMessage);
-  }
 }
 
 bool
 EventListenerManager::ListenerCanHandle(const Listener* aListener,
                                         const WidgetEvent* aEvent,
                                         EventMessage aEventMessage) const
 
 {
@@ -922,16 +916,19 @@ EventListenerManager::RemoveEventHandler
   }
 
   EventMessage eventMessage = nsContentUtils::GetEventMessage(aName);
   Listener* listener = FindEventHandler(eventMessage, aName, aTypeString);
 
   if (listener) {
     mListeners.RemoveElementAt(uint32_t(listener - &mListeners.ElementAt(0)));
     NotifyEventListenerRemoved(aName);
+    if (IsDeviceType(eventMessage)) {
+      DisableDevice(eventMessage);
+    }
   }
 }
 
 nsresult
 EventListenerManager::CompileEventHandlerInternal(Listener* aListener,
                                                   const nsAString* aBody,
                                                   Element* aElement)
 {
--- a/dom/events/test/test_bug742376.html
+++ b/dom/events/test/test_bug742376.html
@@ -26,35 +26,49 @@ function hasListeners() {
          dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_ROTATION_VECTOR, window) ||
          dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_GAME_ROTATION_VECTOR, window);
 }
 
 is(hasListeners(), false, "Must not have listeners before tests start");
 
 function dumbListener(event) {}
 function dumbListener2(event) {}
+function dumbListener3(event) {}
 
 window.addEventListener("deviceorientation", dumbListener, false);
+window.addEventListener("random_event_name", function() {}, false);
 window.addEventListener("deviceorientation", dumbListener2, false);
 
 is(hasListeners(), true, "Listeners should have been added");
 
 window.setTimeout(function() {
 
   window.removeEventListener("deviceorientation", dumbListener, false);
   is(hasListeners(), true, "Only some listeners should have been removed");
   window.setTimeout(function() {
 
     window.removeEventListener("deviceorientation", dumbListener2, false);
     window.setTimeout(function() {
       is(hasListeners(), false, "Listeners should have been removed");
-      SimpleTest.finish();
+      testEventHandler();
     }, 0);
   }, 0);
 }, 0);
 
+function testEventHandler() {
+  window.ondeviceorientation = function() {}
+  window.setTimeout(function() {
+    is(hasListeners(), true, "Handler should have been added");
+    window.ondeviceorientation = null;
+    window.setTimeout(function() {
+      is(hasListeners(), false, "Handler should have been removed");
+      SimpleTest.finish();
+    }, 0);
+  }, 0)
+}
+
 SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -260,17 +260,17 @@ FetchRequest(nsIGlobalObject* aGlobal, c
     RefPtr<WorkerFetchResolver> resolver = WorkerFetchResolver::Create(worker, p);
     if (!resolver) {
       NS_WARNING("Could not add WorkerFetchResolver workerHolder to worker");
       aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
       return nullptr;
     }
 
     RefPtr<MainThreadFetchRunnable> run = new MainThreadFetchRunnable(resolver, r);
-    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(run));
+    worker->DispatchToMainThread(run.forget());
   }
 
   return p.forget();
 }
 
 MainThreadFetchResolver::MainThreadFetchResolver(Promise* aPromise)
   : mPromise(aPromise)
 {
@@ -1027,17 +1027,22 @@ FetchBody<Derived>::BeginConsumeBody()
 
   // The FetchBody is not thread-safe refcounted. We addref it here and release
   // it once the stream read is finished.
   if (!AddRefObject()) {
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIRunnable> r = new BeginConsumeBodyRunnable<Derived>(this);
-  nsresult rv = NS_DispatchToMainThread(r);
+  nsresult rv = NS_OK;
+  if (mWorkerPrivate) {
+    rv = mWorkerPrivate->DispatchToMainThread(r.forget());
+  } else {
+    rv = NS_DispatchToMainThread(r.forget());
+  }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     ReleaseObject();
     return rv;
   }
   return NS_OK;
 }
 
 /*
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -4123,16 +4123,27 @@ HTMLInputElement::MaybeInitPickers(Event
   }
   if (mType == NS_FORM_INPUT_DATE) {
     return InitDatePicker();
   }
 
   return NS_OK;
 }
 
+/**
+ * Return true if the input event should be ignore because of it's modifiers
+ */
+static bool
+IgnoreInputEventWithModifier(WidgetInputEvent* aEvent)
+{
+  return aEvent->IsShift() || aEvent->IsControl() || aEvent->IsAlt() ||
+         aEvent->IsMeta() || aEvent->IsAltGraph() || aEvent->IsFn() ||
+         aEvent->IsOS();
+}
+
 nsresult
 HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
 {
   if (!aVisitor.mPresContext) {
     // Hack alert! In order to open file picker even in case the element isn't
     // in document, try to init picker even without PresContext.
     return MaybeInitPickers(aVisitor);
   }
@@ -4269,20 +4280,17 @@ HTMLInputElement::PostHandleEvent(EventC
   }
 
   if (NS_SUCCEEDED(rv)) {
     WidgetKeyboardEvent* keyEvent = aVisitor.mEvent->AsKeyboardEvent();
     if (mType ==  NS_FORM_INPUT_NUMBER &&
         keyEvent && keyEvent->mMessage == eKeyPress &&
         aVisitor.mEvent->IsTrusted() &&
         (keyEvent->mKeyCode == NS_VK_UP || keyEvent->mKeyCode == NS_VK_DOWN) &&
-        !(keyEvent->IsShift() || keyEvent->IsControl() ||
-          keyEvent->IsAlt() || keyEvent->IsMeta() ||
-          keyEvent->IsAltGraph() || keyEvent->IsFn() ||
-          keyEvent->IsOS())) {
+        !IgnoreInputEventWithModifier(keyEvent)) {
       // We handle the up/down arrow keys specially for <input type=number>.
       // On some platforms the editor for the nested text control will
       // process these keys to send the cursor to the start/end of the text
       // control and as a result aVisitor.mEventStatus will already have been
       // set to nsEventStatus_eConsumeNoDefault. However, we know that
       // whenever the up/down arrow keys cause the value of the number
       // control to change the string in the text control will change, and
       // the cursor will be moved to the end of the text control, overwriting
@@ -4491,20 +4499,17 @@ HTMLInputElement::PostHandleEvent(EventC
                 aVisitor.mDOMEvent->StopPropagation();
               } else {
                 rv = NS_ERROR_FAILURE;
               }
             }
           }
           if (mType == NS_FORM_INPUT_NUMBER && aVisitor.mEvent->IsTrusted()) {
             if (mouseEvent->button == WidgetMouseEvent::eLeftButton &&
-                !(mouseEvent->IsShift() || mouseEvent->IsControl() ||
-                  mouseEvent->IsAlt() || mouseEvent->IsMeta() ||
-                  mouseEvent->IsAltGraph() || mouseEvent->IsFn() ||
-                  mouseEvent->IsOS())) {
+                !IgnoreInputEventWithModifier(mouseEvent)) {
               nsNumberControlFrame* numberControlFrame =
                 do_QueryFrame(GetPrimaryFrame());
               if (numberControlFrame) {
                 if (aVisitor.mEvent->mMessage == eMouseDown &&
                     IsMutable()) {
                   switch (numberControlFrame->GetSpinButtonForPointerEvent(
                             aVisitor.mEvent->AsMouseEvent())) {
                   case nsNumberControlFrame::eSpinButtonUp:
@@ -4657,20 +4662,17 @@ HTMLInputElement::PostHandleEventForRang
     case eTouchStart: {
       if (mIsDraggingRange) {
         break;
       }
       if (nsIPresShell::GetCapturingContent()) {
         break; // don't start drag if someone else is already capturing
       }
       WidgetInputEvent* inputEvent = aVisitor.mEvent->AsInputEvent();
-      if (inputEvent->IsShift() || inputEvent->IsControl() ||
-          inputEvent->IsAlt() || inputEvent->IsMeta() ||
-          inputEvent->IsAltGraph() || inputEvent->IsFn() ||
-          inputEvent->IsOS()) {
+      if (IgnoreInputEventWithModifier(inputEvent)) {
         break; // ignore
       }
       if (aVisitor.mEvent->mMessage == eMouseDown) {
         if (aVisitor.mEvent->AsMouseEvent()->buttons ==
               WidgetMouseEvent::eLeftButtonFlag) {
           StartRangeThumbDrag(inputEvent);
         } else if (mIsDraggingRange) {
           CancelRangeThumbDrag();
--- a/dom/interfaces/core/nsIDOMDocument.idl
+++ b/dom/interfaces/core/nsIDOMDocument.idl
@@ -392,19 +392,17 @@ interface nsIDOMDocument : nsIDOMNode
    * @see <http://dvcs.w3.org/hg/pointerlock/raw-file/default/index.html>
    */
   void mozExitPointerLock();
 
   /**
    * Visibility API implementation.
    */
   readonly attribute boolean hidden;
-  readonly attribute boolean mozHidden;
   readonly attribute DOMString visibilityState;
-  readonly attribute DOMString mozVisibilityState;
 
   /**
    * Returns "BackCompat" if we're in quirks mode or "CSS1Compat" if we're in
    * strict mode.  (XML documents are always in strict mode.)
    */
   readonly attribute DOMString compatMode;
 
   /**
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -148,18 +148,16 @@ NoExposedPropsWarning=Exposing chrome JS
 MutationEventWarning=Use of Mutation Events is deprecated. Use MutationObserver instead.
 # LOCALIZATION NOTE: Do not translate "Components"
 ComponentsWarning=The Components object is deprecated. It will soon be removed.
 PluginHangUITitle=Warning: Unresponsive plugin
 PluginHangUIMessage=%S may be busy, or it may have stopped responding. You can stop the plugin now, or you can continue to see if the plugin will complete.
 PluginHangUIWaitButton=Continue
 PluginHangUIStopButton=Stop plugin
 PrefixedFullscreenAPIWarning=Prefixed Fullscreen API is deprecated. Please use unprefixed API for fullscreen. For more help https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API
-# LOCALIZATION NOTE: Do not translate "mozHidden", "mozVisibilityState", "hidden", or "visibilityState"
-PrefixedVisibilityAPIWarning=‘mozHidden’ and ‘mozVisibilityState’ are deprecated.  Please use the unprefixed ‘hidden’ and ‘visibilityState’ instead.
 # LOCALIZATION NOTE: Do not translate "NodeIterator" or "detach()".
 NodeIteratorDetachWarning=Calling detach() on a NodeIterator no longer has an effect.
 # LOCALIZATION NOTE: Do not translate "LenientThis" and "this"
 LenientThisWarning=Ignoring get or set of property that has [LenientThis] because the “this” object is incorrect.
 # LOCALIZATION NOTE: Do not translate "nsIDOMWindowUtils", "getWindowWithOuterId", or "nsIWindowMediator"
 GetWindowWithOuterIdWarning=Use of nsIDOMWindowUtils.getOuterWindowWithId() is deprecated.  Instead, use the nsIWindowMediator method of the same name.
 # LOCALIZATION NOTE: Do not translate "getPreventDefault" or "defaultPrevented".
 GetPreventDefaultWarning=Use of getPreventDefault() is deprecated.  Use defaultPrevented instead.
--- a/dom/media/fmp4/MP4Decoder.cpp
+++ b/dom/media/fmp4/MP4Decoder.cpp
@@ -8,16 +8,17 @@
 #include "MediaDecoderStateMachine.h"
 #include "MP4Demuxer.h"
 #include "mozilla/Preferences.h"
 #include "nsCharSeparatedTokenizer.h"
 #ifdef MOZ_EME
 #include "mozilla/CDMProxy.h"
 #endif
 #include "mozilla/Logging.h"
+#include "mozilla/SharedThreadPool.h"
 #include "nsMimeTypes.h"
 #include "nsContentTypeParser.h"
 #include "VideoUtils.h"
 
 #ifdef XP_WIN
 #include "mozilla/WindowsVersion.h"
 #endif
 #ifdef MOZ_WIDGET_ANDROID
--- a/dom/media/mediasource/AutoTaskQueue.h
+++ b/dom/media/mediasource/AutoTaskQueue.h
@@ -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/. */
 
 #ifndef MOZILLA_AUTOTASKQUEUE_H_
 #define MOZILLA_AUTOTASKQUEUE_H_
 
 #include "mozilla/RefPtr.h"
+#include "mozilla/SharedThreadPool.h"
 #include "mozilla/TaskQueue.h"
 
 namespace mozilla {
 
 // A convenience TaskQueue not requiring explicit shutdown.
 class AutoTaskQueue : public AbstractThread
 {
 public:
--- a/dom/media/platforms/wmf/DXVA2Manager.cpp
+++ b/dom/media/platforms/wmf/DXVA2Manager.cpp
@@ -423,17 +423,17 @@ D3D9DXVA2Manager::Init(nsACString& aFail
   mDecoderService = decoderService;
 
   mResetToken = resetToken;
   mD3D9 = d3d9Ex;
   mDevice = device;
   mDeviceManager = deviceManager;
   mSyncSurface = syncSurf;
 
-  mTextureClientAllocator = new D3D9RecycleAllocator(layers::ImageBridgeChild::GetSingleton(),
+  mTextureClientAllocator = new D3D9RecycleAllocator(layers::ImageBridgeChild::GetSingleton().get(),
                                                      mDevice);
   mTextureClientAllocator->SetMaxPoolSize(5);
 
   Telemetry::Accumulate(Telemetry::MEDIA_DECODER_BACKEND_USED,
                         uint32_t(media::MediaDecoderBackend::WMFDXVA2D3D9));
 
   reporter.SetSuccessful();
 
@@ -747,17 +747,17 @@ D3D11DXVA2Manager::Init(nsACString& aFai
   desc.Usage = D3D11_USAGE_STAGING;
   desc.BindFlags = 0;
   desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
   desc.MiscFlags = 0;
 
   hr = mDevice->CreateTexture2D(&desc, NULL, getter_AddRefs(mSyncSurface));
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
 
-  mTextureClientAllocator = new D3D11RecycleAllocator(layers::ImageBridgeChild::GetSingleton(),
+  mTextureClientAllocator = new D3D11RecycleAllocator(layers::ImageBridgeChild::GetSingleton().get(),
                                                       mDevice);
   mTextureClientAllocator->SetMaxPoolSize(5);
 
   Telemetry::Accumulate(Telemetry::MEDIA_DECODER_BACKEND_USED,
                         uint32_t(media::MediaDecoderBackend::WMFDXVA2D3D11));
 
   reporter.SetSuccessful();
 
--- a/dom/media/systemservices/MediaSystemResourceManager.cpp
+++ b/dom/media/systemservices/MediaSystemResourceManager.cpp
@@ -37,17 +37,18 @@ MediaSystemResourceManager::Shutdown()
     sSingleton->CloseIPC();
     sSingleton = nullptr;
   }
 }
 
 /* static */ void
 MediaSystemResourceManager::Init()
 {
-  if (!ImageBridgeChild::IsCreated()) {
+  RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
+  if (!imageBridge) {
     NS_WARNING("ImageBridge does not exist");
     return;
   }
 
   if (InImageBridgeChildThread()) {
     if (!sSingleton) {
 #ifdef DEBUG
       static int timesCreated = 0;
@@ -68,17 +69,17 @@ MediaSystemResourceManager::Init()
       if (!sSingleton) {
         sSingleton = new MediaSystemResourceManager();
       }
       ReentrantMonitorAutoEnter childThreadAutoMon(barrier);
       done = true;
       barrier.NotifyAll();
     });
 
-  ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(runnable.forget());
+  imageBridge->GetMessageLoop()->PostTask(runnable.forget());
 
   // should stop the thread until done.
   while (!done) {
     barrier.Wait();
   }
 }
 
 MediaSystemResourceManager::MediaSystemResourceManager()
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -672,17 +672,17 @@ PluginInstanceParent::RecvInitDXGISurfac
         return true;
     }
 
     ImageContainer *container = GetImageContainer();
     if (!container) {
         return true;
     }
 
-    ImageBridgeChild* forwarder = ImageBridgeChild::GetSingleton();
+    RefPtr<ImageBridgeChild> forwarder = ImageBridgeChild::GetSingleton();
     if (!forwarder) {
         return true;
     }
 
     RefPtr<ID3D11Device> d3d11 = DeviceManagerDx::Get()->GetContentDevice();
     if (!d3d11) {
         return true;
     }
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -3125,17 +3125,17 @@ PluginModuleParent::RecvReturnSitesWithD
     mSitesWithDataCallbacks.erase(aCallbackId);
     return true;
 }
 
 layers::TextureClientRecycleAllocator*
 PluginModuleParent::EnsureTextureAllocator()
 {
     if (!mTextureAllocator) {
-        mTextureAllocator = new TextureClientRecycleAllocator(ImageBridgeChild::GetSingleton());
+        mTextureAllocator = new TextureClientRecycleAllocator(ImageBridgeChild::GetSingleton().get());
     }
     return mTextureAllocator;
 }
 
 #ifdef MOZ_CRASHREPORTER_INJECTOR
 
 // We only add the crash reporter to subprocess which have the filename
 // FlashPlayerPlugin*
--- a/dom/security/test/sri/iframe_require-sri-for_main.html
+++ b/dom/security/test/sri/iframe_require-sri-for_main.html
@@ -1,31 +1,40 @@
 <script>
   window.hasCORSLoaded = false; // set through script_crossdomain1.js
 </script>
 
-<!-- cors-enabled. should be loaded -->
+<!-- script tag cors-enabled. should be loaded -->
 <script src="http://example.com/tests/dom/security/test/sri/script_crossdomain1.js"
         crossorigin=""
         integrity="sha512-9Tv2DL1fHvmPQa1RviwKleE/jq72jgxj8XGLyWn3H6Xp/qbtfK/jZINoPFAv2mf0Nn1TxhZYMFULAbzJNGkl4Q=="
         onload="parent.postMessage('good_sriLoaded', '*');"></script>
 
-<!-- cors but not using SRI. should trigger onerror -->
+<!-- script tag cors but not using SRI. should trigger onerror -->
 <script src="http://example.com/tests/dom/security/test/sri/script_crossdomain5.js"
           onload="parent.postMessage('bad_nonsriLoaded', '*');"
           onerror="parent.postMessage('good_nonsriBlocked', '*');"></script>
 
-<!-- cors and integrity. it should just load fine. -->
+<!-- svg:script tag with cors but not using SRI. should trigger onerror -->
+<svg xmlns="http://www.w3.org/2000/svg">
+          <script xlink:href="http://example.com/tests/dom/security/test/sri/script_crossdomain3.js"
+          onload="parent.postMessage('bad_svg_nonsriLoaded', '*');"
+          onerror="parent.postMessage('good_svg_nonsriBlocked', '*');"></script>
+          ></script>
+</svg>
+
+<!-- stylesheet with cors and integrity. it should just load fine. -->
 <link rel="stylesheet" href="style1.css"
       integrity="sha256-qs8lnkunWoVldk5d5E+652yth4VTSHohlBKQvvgGwa8="
       onload="parent.postMessage('good_sriLoaded', '*');">
 
-<!-- not using SRI, should trigger onerror -->
+<!-- stylesheet not using SRI, should trigger onerror -->
 <link rel="stylesheet" href="style3.css"
       onload="parent.postMessage('bad_nonsriLoaded', '*');"
       onerror="parent.postMessage('good_nonsriBlocked', '*');">
 
+
 <p id="black-text">black text</p>
 <script>
   window.onload = function() {
     parent.postMessage("finish", '*');
   }
 </script>
--- a/dom/security/test/sri/test_require-sri-for_csp_directive.html
+++ b/dom/security/test/sri/test_require-sri-for_csp_directive.html
@@ -22,16 +22,22 @@
         ok(true, "Eligible SRI resources was correctly loaded.");
         break;
       case 'bad_nonsriLoaded':
         ok(false, "Eligible non-SRI resource should be blocked by the CSP!");
         break;
       case 'good_nonsriBlocked':
         ok(true, "Eligible non-SRI resources was correctly blocked by the CSP.");
         break;
+      case 'bad_svg_nonsriLoaded':
+        ok(false, 'Eligible non-SRI resource should be blocked by the CSP.');
+        break;
+      case 'good_svg_nonsriBlocked':
+        ok(true, 'Eligible non-SRI svg script was correctly blocked by the CSP.');
+        break;
       case 'finish':
         var blackText = frame.contentDocument.getElementById('black-text');
         var blackTextColor = frame.contentWindow.getComputedStyle(blackText, null).getPropertyValue('color');
         ok(blackTextColor == 'rgb(0, 0, 0)', "The second part should not be black.");
         removeEventListener('message', handler);
         SimpleTest.finish();
         break;
       default:
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/bugs/bug918719.sjs
@@ -0,0 +1,47 @@
+// Keep track of one in-flight XHR by allowing secondary ones to
+// tell us when the client has received a Progress event and wants
+// more data or to stop the in-flight XHR.
+
+const STATE_NAME = "bug918719_loading_event_test";
+const CHUNK_DATA = "chunk";
+
+function setReq(req) {
+  setObjectState(STATE_NAME, req);
+}
+
+function getReq() {
+  let req;
+  getObjectState(STATE_NAME, function(v) {
+    req = v;
+  });
+  return req;
+}
+
+function handleRequest(request, response)
+{
+  var pairs = request.queryString.split("&");
+  var command = pairs.shift();
+
+  response.setHeader("Content-Type", "text/plain");
+  response.setHeader("Cache-Control", "no-cache", false);
+
+  switch(command) {
+    case "more":
+      getReq().write(CHUNK_DATA);
+      break;
+
+    case "done":
+      getReq().finish();
+      setReq(null);
+      break;
+
+    default:
+      response.processAsync();
+      response.write(CHUNK_DATA);
+      setReq(response);
+      return;
+  }
+
+  response.setHeader("Content-Length", "2", false);
+  response.write("ok");
+}
--- a/dom/tests/mochitest/bugs/mochitest.ini
+++ b/dom/tests/mochitest/bugs/mochitest.ini
@@ -2,16 +2,17 @@
 support-files =
   bug289714.sjs
   bug346659-echoer.html
   bug346659-opener-echoer.html
   bug346659-opener.html
   bug346659-parent-echoer.html
   bug346659-parent.html
   bug458091_child.html
+  bug918719.sjs
   child_bug260264.html
   devicemotion_inner.html
   devicemotion_outer.html
   file_bug291653.html
   file_bug406375.html
   file_bug504862.html
   file_bug593174_1.html
   file_bug593174_2.html
@@ -150,16 +151,17 @@ skip-if = (buildapp == 'b2g' && toolkit 
 [test_bug809290.html]
 [test_bug817476.html]
 [test_bug823173.html]
 [test_bug848088.html]
 [test_bug850517.html]
 [test_bug857555.html]
 [test_bug862540.html]
 [test_bug876098.html]
+[test_bug918719.html]
 [test_bug927901.html]
 [test_devicemotion_multiple_listeners.html]
 skip-if = toolkit == 'android' #bug 775227
 [test_domparser_after_blank.html]
 [test_errorReporting.html]
 [test_onerror_message.html]
 [test_protochains.html]
 [test_resize_move_windows.html]
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/bugs/test_bug918719.html
@@ -0,0 +1,89 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=918719
+-->
+<head>
+  <title>Test for Bug 918719</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=918719">Mozilla Bug 918719</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+const SERVER_URL = "bug918719.sjs";
+
+function sendCommand(cmd) {
+  let xhr = new XMLHttpRequest();
+  xhr.open("get", SERVER_URL + "?" + cmd);
+  xhr.send();
+}
+
+function runTest() {
+  // Manipulate one in-flight XHR using secondary command XHRs, to guarantee
+  // that multiple OnDataAvailable events are triggered (which are where
+  // LOADING readystatechanges are triggered). We return a promise that will
+  // resolve with a count of the number of LOADING events that were detected.
+
+  return new Promise((resolve, reject) => {
+    let xhr = new XMLHttpRequest();
+    let numProgressEvents = 0;
+    let numLoadingEvents = 0;
+
+    xhr.onreadystatechange = e => {
+      if (xhr.readyState === xhr.LOADING) {
+        ++numLoadingEvents;
+      }
+    };
+
+    xhr.onprogress = e => {
+      if (++numProgressEvents < 2) {
+        sendCommand("more");
+      } else {
+        sendCommand("done");
+      }
+    };
+
+    xhr.onerror = e => {
+      reject(e);
+    };
+
+    xhr.onloadend = e => {
+      resolve(numLoadingEvents);
+    };
+
+    xhr.open("GET", SERVER_URL);
+    xhr.send();
+  });
+}
+
+function prefChangePromise(args) {
+  return new Promise(function(resolve) {
+    SpecialPowers.pushPrefEnv(args, resolve);
+  });
+}
+
+runTest().then(function(count) {
+  ok(count === 1, "Only one loading readystatechange event should have been fired with the pref off.");
+}).then(function() {
+  return prefChangePromise({"set": [["dom.fire_extra_xhr_loading_readystatechanges", true]]});
+}).then(function() {
+  return runTest();
+}).then(function(count) {
+  ok(count > 1, "Multiple loading readystatechange events should have been fired with the pref on.");
+  SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
+
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -273,19 +273,17 @@ partial interface Document {
     // this is deprecated from CustomElements v0
     [Throws, Func="CustomElementsRegistry::IsCustomElementsEnabled"]
     object registerElement(DOMString name, optional ElementRegistrationOptions options);
 };
 
 // http://dvcs.w3.org/hg/webperf/raw-file/tip/specs/PageVisibility/Overview.html#sec-document-interface
 partial interface Document {
   readonly attribute boolean hidden;
-  readonly attribute boolean mozHidden;
   readonly attribute VisibilityState visibilityState;
-  readonly attribute VisibilityState mozVisibilityState;
 };
 
 // http://dev.w3.org/csswg/cssom/#extensions-to-the-document-interface
 partial interface Document {
     [Constant]
     readonly attribute StyleSheetList styleSheets;
     attribute DOMString? selectedStyleSheetSet;
     readonly attribute DOMString? lastStyleSheetSet;
--- a/dom/workers/ServiceWorkerClient.cpp
+++ b/dom/workers/ServiceWorkerClient.cpp
@@ -226,14 +226,14 @@ ServiceWorkerClient::PostMessage(JSConte
   RefPtr<ServiceWorkerClientPostMessageRunnable> runnable =
     new ServiceWorkerClientPostMessageRunnable(mWindowId);
 
   runnable->Write(aCx, aMessage, transferable, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
-  aRv = NS_DispatchToMainThread(runnable);
+  aRv = workerPrivate->DispatchToMainThread(runnable.forget());
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 }
 
--- a/dom/workers/ServiceWorkerClients.cpp
+++ b/dom/workers/ServiceWorkerClients.cpp
@@ -670,17 +670,17 @@ ServiceWorkerClients::Get(const nsAStrin
     PromiseWorkerProxy::Create(workerPrivate, promise);
   if (!promiseProxy) {
     promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
     return promise.forget();
   }
 
   RefPtr<GetRunnable> r =
     new GetRunnable(promiseProxy, aClientId);
-  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
+  MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 ServiceWorkerClients::MatchAll(const ClientQueryOptions& aOptions,
                                ErrorResult& aRv)
 {
   WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
@@ -706,17 +706,17 @@ ServiceWorkerClients::MatchAll(const Cli
     promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
     return promise.forget();
   }
 
   RefPtr<MatchAllRunnable> r =
     new MatchAllRunnable(promiseProxy,
                          NS_ConvertUTF16toUTF8(scope),
                          aOptions.mIncludeUncontrolled);
-  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
+  MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 ServiceWorkerClients::OpenWindow(const nsAString& aUrl,
                                  ErrorResult& aRv)
 {
   WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
@@ -747,17 +747,17 @@ ServiceWorkerClients::OpenWindow(const n
     return nullptr;
   }
 
   nsString scope;
   mWorkerScope->GetScope(scope);
 
   RefPtr<OpenWindowRunnable> r = new OpenWindowRunnable(promiseProxy,
                                                           aUrl, scope);
-  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
+  MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 ServiceWorkerClients::Claim(ErrorResult& aRv)
 {
   WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
@@ -776,11 +776,11 @@ ServiceWorkerClients::Claim(ErrorResult&
   }
 
   nsString scope;
   mWorkerScope->GetScope(scope);
 
   RefPtr<ClaimRunnable> runnable =
     new ClaimRunnable(promiseProxy, NS_ConvertUTF16toUTF8(scope));
 
-  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
+  MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(runnable.forget()));
   return promise.forget();
 }
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -389,17 +389,23 @@ void RespondWithCopyComplete(void* aClos
                                       NS_ERROR_INTERCEPTION_FAILED);
   } else {
     event = new FinishResponse(data->mInterceptedChannel,
                                data->mInternalResponse,
                                data->mWorkerChannelInfo,
                                data->mScriptSpec,
                                data->mResponseURLSpec);
   }
-  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(event));
+  // In theory this can happen after the worker thread is terminated.
+  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+  if (worker) {
+    MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(event.forget()));
+  } else {
+    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(event.forget()));
+  }
 }
 
 namespace {
 
 void
 ExtractErrorValues(JSContext* aCx, JS::Handle<JS::Value> aValue,
                   nsACString& aSourceSpecOut, uint32_t *aLineOut,
                   uint32_t *aColumnOut, nsString& aMessageOut)
@@ -719,17 +725,23 @@ RespondWithHandler::RejectedCallback(JSC
   CancelRequest(NS_ERROR_INTERCEPTION_FAILED);
 }
 
 void
 RespondWithHandler::CancelRequest(nsresult aStatus)
 {
   nsCOMPtr<nsIRunnable> runnable =
     new CancelChannelRunnable(mInterceptedChannel, mRegistration, aStatus);
-  NS_DispatchToMainThread(runnable);
+  // Note, this may run off the worker thread during worker termination.
+  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
+  if (worker) {
+    MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(runnable.forget()));
+  } else {
+    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable.forget()));
+  }
   mRequestWasHandled = true;
 }
 
 } // namespace
 
 void
 FetchEvent::RespondWith(JSContext* aCx, Promise& aArg, ErrorResult& aRv)
 {
@@ -852,18 +864,18 @@ public:
 
     // only use the extracted location if we found one
     if (!spec.IsEmpty()) {
       mSourceSpec = spec;
       mLine = line;
       mColumn = column;
     }
 
-    MOZ_ALWAYS_SUCCEEDS(
-      NS_DispatchToMainThread(NewRunnableMethod(this, &WaitUntilHandler::ReportOnMainThread)));
+    MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(
+        NewRunnableMethod(this, &WaitUntilHandler::ReportOnMainThread)));
   }
 
   void
   ReportOnMainThread()
   {
     AssertIsOnMainThread();
     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
 
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -207,17 +207,17 @@ public:
 private:
   void
   Done(bool aResult)
   {
 #ifdef DEBUG
     mDone = true;
 #endif
     mCallback->SetResult(aResult);
-    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(mCallback));
+    MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(mCallback));
   }
 };
 
 } // anonymous namespace
 
 nsresult
 ServiceWorkerPrivate::CheckScriptEvaluation(LifeCycleEventCallback* aCallback)
 {
@@ -490,17 +490,17 @@ public:
     MOZ_ASSERT(aRegistration);
   }
 
   void
   PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
   {
     nsCOMPtr<nsIRunnable> runnable =
       new RegistrationUpdateRunnable(mRegistration, true /* time check */);
-    NS_DispatchToMainThread(runnable.forget());
+    aWorkerPrivate->DispatchToMainThread(runnable.forget());
 
     ExtendableEventWorkerRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
   }
 };
 
 /*
  * Fires 'install' event on the ServiceWorkerGlobalScope. Modifies busy count
  * since it fires the event. This is ok since there can't be nested
@@ -530,17 +530,17 @@ public:
     MOZ_ASSERT(aWorkerPrivate);
     return DispatchLifecycleEvent(aCx, aWorkerPrivate);
   }
 
   nsresult
   Cancel() override
   {
     mCallback->SetResult(false);
-    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(mCallback));
+    MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(mCallback));
 
     return WorkerRunnable::Cancel();
   }
 
 private:
   bool
   DispatchLifecycleEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
 
@@ -626,17 +626,17 @@ public:
     mWorkerPrivate->AssertIsOnWorkerThread();
 
     if (mDone) {
       return;
     }
     mDone = true;
 
     mCallback->SetResult(aResult);
-    nsresult rv = NS_DispatchToMainThread(mCallback);
+    nsresult rv = mWorkerPrivate->DispatchToMainThread(mCallback);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       NS_RUNTIMEABORT("Failed to dispatch life cycle event handler.");
     }
 
     ReleaseWorker();
   }
 
   void
@@ -757,28 +757,29 @@ public:
 
   void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
   {
     Report(nsIPushErrorReporter::DELIVERY_UNHANDLED_REJECTION);
   }
 
   void Report(uint16_t aReason = nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR)
   {
+    WorkerPrivate* workerPrivate = mWorkerPrivate;
     mWorkerPrivate->AssertIsOnWorkerThread();
     mWorkerPrivate = nullptr;
 
     if (NS_WARN_IF(aReason > nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR) ||
         mMessageId.IsEmpty()) {
       return;
     }
     nsCOMPtr<nsIRunnable> runnable =
       NewRunnableMethod<uint16_t>(this,
         &PushErrorReporter::ReportOnMainThread, aReason);
     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-      NS_DispatchToMainThread(runnable.forget())));
+      workerPrivate->DispatchToMainThread(runnable.forget())));
   }
 
   void ReportOnMainThread(uint16_t aReason)
   {
     AssertIsOnMainThread();
     nsCOMPtr<nsIPushErrorReporter> reporter =
       do_GetService("@mozilla.org/push/Service;1");
     if (reporter) {
@@ -1415,17 +1416,17 @@ public:
     MOZ_ASSERT(aWorkerPrivate);
     return DispatchFetchEvent(aCx, aWorkerPrivate);
   }
 
   nsresult
   Cancel() override
   {
     nsCOMPtr<nsIRunnable> runnable = new ResumeRequest(mInterceptedChannel);
-    if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
+    if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable))) {
       NS_WARNING("Failed to resume channel on FetchEventRunnable::Cancel()!\n");
     }
     WorkerRunnable::Cancel();
     return NS_OK;
   }
 
 private:
   ~FetchEventRunnable() {}
@@ -1529,17 +1530,17 @@ private:
       }
 
       if (!runnable) {
         runnable = new CancelChannelRunnable(mInterceptedChannel,
                                              mRegistration,
                                              NS_ERROR_INTERCEPTION_FAILED);
       }
 
-      MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
+      MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(runnable.forget()));
     }
 
     RefPtr<Promise> waitUntilPromise = event->GetPromise();
     if (waitUntilPromise) {
       KeepAliveHandler::CreateAndAttachToPromise(mKeepAliveToken,
                                                  waitUntilPromise);
     }
 
--- a/dom/workers/ServiceWorkerRegistration.cpp
+++ b/dom/workers/ServiceWorkerRegistration.cpp
@@ -1105,17 +1105,17 @@ ServiceWorkerRegistrationWorkerThread::U
 
   RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, promise);
   if (!proxy) {
     aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
     return nullptr;
   }
 
   RefPtr<UpdateRunnable> r = new UpdateRunnable(proxy, mScope);
-  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
+  MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget()));
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 ServiceWorkerRegistrationWorkerThread::Unregister(ErrorResult& aRv)
 {
   WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
@@ -1137,17 +1137,17 @@ ServiceWorkerRegistrationWorkerThread::U
 
   RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, promise);
   if (!proxy) {
     aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
     return nullptr;
   }
 
   RefPtr<StartUnregisterRunnable> r = new StartUnregisterRunnable(proxy, mScope);
-  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
+  MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget()));
 
   return promise.forget();
 }
 
 class StartListeningRunnable final : public Runnable
 {
   RefPtr<WorkerListener> mListener;
 public:
@@ -1175,17 +1175,17 @@ ServiceWorkerRegistrationWorkerThread::I
   if (!HoldWorker(worker, Closing)) {
     mListener = nullptr;
     NS_WARNING("Could not add feature");
     return;
   }
 
   RefPtr<StartListeningRunnable> r =
     new StartListeningRunnable(mListener);
-  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
+  MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget()));
 }
 
 class AsyncStopListeningRunnable final : public Runnable
 {
   RefPtr<WorkerListener> mListener;
 public:
   explicit AsyncStopListeningRunnable(WorkerListener* aListener)
     : mListener(aListener)
@@ -1233,17 +1233,17 @@ ServiceWorkerRegistrationWorkerThread::R
   mWorkerPrivate->AssertIsOnWorkerThread();
   ReleaseWorker();
 
   mListener->ClearRegistration();
 
   if (aReason == RegistrationIsGoingAway) {
     RefPtr<AsyncStopListeningRunnable> r =
       new AsyncStopListeningRunnable(mListener);
-    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
+    MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(r.forget()));
   } else if (aReason == WorkerIsGoingAway) {
     RefPtr<SyncStopListeningRunnable> r =
       new SyncStopListeningRunnable(mWorkerPrivate, mListener);
     ErrorResult rv;
     r->Dispatch(rv);
     if (rv.Failed()) {
       NS_ERROR("Failed to dispatch stop listening runnable!");
       // And now what?
--- a/dom/workers/ServiceWorkerWindowClient.cpp
+++ b/dom/workers/ServiceWorkerWindowClient.cpp
@@ -178,17 +178,17 @@ ServiceWorkerWindowClient::Focus(ErrorRe
   }
 
   if (workerPrivate->GlobalScope()->WindowInteractionAllowed()) {
     RefPtr<PromiseWorkerProxy> promiseProxy =
       PromiseWorkerProxy::Create(workerPrivate, promise);
     if (promiseProxy) {
       RefPtr<ClientFocusRunnable> r = new ClientFocusRunnable(mWindowId,
                                                               promiseProxy);
-      MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
+      MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
     } else {
       promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
     }
 
   } else {
     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
   }
 
@@ -511,15 +511,15 @@ ServiceWorkerWindowClient::Navigate(cons
     return promise.forget();
   }
 
   RefPtr<PromiseWorkerProxy> promiseProxy =
     PromiseWorkerProxy::Create(workerPrivate, promise);
   if (promiseProxy) {
     RefPtr<ClientNavigateRunnable> r =
       new ClientNavigateRunnable(mWindowId, aUrl, promiseProxy);
-    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
+    MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
   } else {
     promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
   }
 
   return promise.forget();
 }
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -65,16 +65,17 @@
 #include "mozilla/dom/SimpleGlobalObject.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/StructuredCloneHolder.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/WorkerBinding.h"
 #include "mozilla/dom/WorkerDebuggerGlobalScopeBinding.h"
 #include "mozilla/dom/WorkerGlobalScopeBinding.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/TaskQueue.h"
 #include "mozilla/TimelineConsumers.h"
 #include "mozilla/WorkerTimelineMarker.h"
 #include "nsAlgorithm.h"
 #include "nsContentUtils.h"
 #include "nsCycleCollector.h"
 #include "nsError.h"
 #include "nsDOMJSUtils.h"
 #include "nsHostObjectProtocolHandler.h"
@@ -396,17 +397,17 @@ private:
     nsCOMPtr<nsILoadGroup> loadGroupToCancel;
     mFinishedWorker->ForgetOverridenLoadGroup(loadGroupToCancel);
 
     nsTArray<nsCOMPtr<nsISupports>> doomed;
     mFinishedWorker->ForgetMainThreadObjects(doomed);
 
     RefPtr<MainThreadReleaseRunnable> runnable =
       new MainThreadReleaseRunnable(doomed, loadGroupToCancel);
-    if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
+    if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))) {
       NS_WARNING("Failed to dispatch, going to leak!");
     }
 
     RuntimeService* runtime = RuntimeService::GetService();
     NS_ASSERTION(runtime, "This should never be null!");
 
     mFinishedWorker->DisableDebugger();
 
@@ -3852,17 +3853,17 @@ WorkerDebugger::Close()
 
 void
 WorkerDebugger::PostMessageToDebugger(const nsAString& aMessage)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   RefPtr<PostDebuggerMessageRunnable> runnable =
     new PostDebuggerMessageRunnable(this, aMessage);
-  if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
+  if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))) {
     NS_WARNING("Failed to post message to debugger on main thread!");
   }
 }
 
 void
 WorkerDebugger::PostMessageToDebuggerOnMainThread(const nsAString& aMessage)
 {
   AssertIsOnMainThread();
@@ -3877,17 +3878,17 @@ void
 WorkerDebugger::ReportErrorToDebugger(const nsAString& aFilename,
                                       uint32_t aLineno,
                                       const nsAString& aMessage)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   RefPtr<ReportDebuggerErrorRunnable> runnable =
     new ReportDebuggerErrorRunnable(this, aFilename, aLineno, aMessage);
-  if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
+  if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))) {
     NS_WARNING("Failed to report error to debugger on main thread!");
   }
 }
 
 void
 WorkerDebugger::ReportErrorToDebuggerOnMainThread(const nsAString& aFilename,
                                                   uint32_t aLineno,
                                                   const nsAString& aMessage)
@@ -3910,16 +3911,17 @@ WorkerPrivate::WorkerPrivate(WorkerPriva
   : WorkerPrivateParent<WorkerPrivate>(aParent, aScriptURL,
                                        aIsChromeWorker, aWorkerType,
                                        aWorkerName, aLoadInfo)
   , mDebuggerRegistered(false)
   , mDebugger(nullptr)
   , mJSContext(nullptr)
   , mPRThread(nullptr)
   , mDebuggerEventLoopLevel(0)
+  , mMainThreadEventTarget(do_GetMainThread())
   , mErrorHandlerRecursionCount(0)
   , mNextTimeoutId(1)
   , mStatus(Pending)
   , mFrozen(false)
   , mTimerRunning(false)
   , mRunningExpiredTimeouts(false)
   , mPendingEventQueueClearing(false)
   , mMemoryReporterRunning(false)
@@ -3938,16 +3940,39 @@ WorkerPrivate::WorkerPrivate(WorkerPriva
     aParent->GetAllPreferences(mPreferences);
     mOnLine = aParent->OnLine();
   }
   else {
     AssertIsOnMainThread();
     RuntimeService::GetDefaultPreferences(mPreferences);
     mOnLine = !NS_IsOffline() && !NS_IsAppOffline(aLoadInfo.mPrincipal);
   }
+
+  nsCOMPtr<nsIEventTarget> target;
+
+  if (aParent) {
+    target = aParent->MainThreadEventTarget();
+  }
+
+  // TODO: If we have a window, try to use its MainThreadTaskQueue as the
+  //       target for our sub-queue.
+
+  if (!target) {
+    nsCOMPtr<nsIThread> mainThread;
+    NS_GetMainThread(getter_AddRefs(mainThread));
+    MOZ_DIAGNOSTIC_ASSERT(mainThread);
+    target = mainThread;
+  }
+
+  // Throttle events to the main thread using a TaskQueue specific to this
+  // worker thread.
+  mMainThreadTaskQueue = new TaskQueue(target.forget());
+
+  // Expose our task queue as the worker's main thread nsIEventTarget.
+  mMainThreadEventTarget = mMainThreadTaskQueue->WrapAsEventTarget();
 }
 
 WorkerPrivate::~WorkerPrivate()
 {
 }
 
 // static
 already_AddRefed<WorkerPrivate>
@@ -4477,16 +4502,24 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
 
         {
           MutexAutoLock lock(mMutex);
 
           mStatus = Dead;
           mJSContext = nullptr;
         }
 
+        // Shutdown the main thread TaskQueue and wait for it to drain.  Make
+        // sure to clear our references first, however, so that new runnables
+        // are not dispatched into the closing TaskQueue.
+        mMainThreadEventTarget = do_GetMainThread();
+        RefPtr<TaskQueue> taskQueue = mMainThreadTaskQueue.forget();
+        taskQueue->BeginShutdown();
+        taskQueue->AwaitShutdownAndIdle();
+
         // After mStatus is set to Dead there can be no more
         // WorkerControlRunnables so no need to lock here.
         if (!mControlQueue.IsEmpty()) {
           WorkerControlRunnable* runnable;
           while (mControlQueue.Pop(runnable)) {
             runnable->Cancel();
             runnable->Release();
           }
@@ -4542,16 +4575,24 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
       }
     }
 
     if (!debuggerRunnablesPending && !normalRunnablesPending) {
       // Both the debugger event queue and the normal event queue has been
       // exhausted, cancel the periodic GC timer and schedule the idle GC timer.
       SetGCTimerMode(IdleTimer);
     }
+
+    // If the worker thread is spamming the main thread faster than it can
+    // process the work, then pause the worker thread until the MT catches
+    // up.
+    if (mMainThreadTaskQueue &&
+        mMainThreadTaskQueue->ImpreciseLengthForHeuristics() > 5000) {
+      mMainThreadTaskQueue->AwaitIdle();
+    }
   }
 
   MOZ_CRASH("Shouldn't get here!");
 }
 
 void
 WorkerPrivate::OnProcessNextEvent()
 {
@@ -4584,17 +4625,37 @@ WorkerPrivate::MaybeDispatchLoadFailedRu
 {
   AssertIsOnWorkerThread();
 
   nsCOMPtr<nsIRunnable> runnable = StealLoadFailedAsyncRunnable();
   if (!runnable) {
     return;
   }
 
-  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable.forget()));
+  MOZ_ALWAYS_SUCCEEDS(DispatchToMainThread(runnable.forget()));
+}
+
+nsIEventTarget*
+WorkerPrivate::MainThreadEventTarget()
+{
+  return mMainThreadEventTarget;
+}
+
+nsresult
+WorkerPrivate::DispatchToMainThread(nsIRunnable* aRunnable, uint32_t aFlags)
+{
+  nsCOMPtr<nsIRunnable> r = aRunnable;
+  return DispatchToMainThread(r.forget(), aFlags);
+}
+
+nsresult
+WorkerPrivate::DispatchToMainThread(already_AddRefed<nsIRunnable> aRunnable,
+                                    uint32_t aFlags)
+{
+  return mMainThreadEventTarget->Dispatch(Move(aRunnable), aFlags);
 }
 
 void
 WorkerPrivate::InitializeGCTimers()
 {
   AssertIsOnWorkerThread();
 
   // We need a timer for GC. The basic plan is to run a non-shrinking GC
@@ -4785,17 +4846,17 @@ WorkerPrivate::ScheduleDeletion(WorkerRa
       new WorkerFinishedRunnable(parent, this);
     if (!runnable->Dispatch()) {
       NS_WARNING("Failed to dispatch runnable!");
     }
   }
   else {
     RefPtr<TopLevelWorkerFinishedRunnable> runnable =
       new TopLevelWorkerFinishedRunnable(this);
-    if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
+    if (NS_FAILED(DispatchToMainThread(runnable.forget()))) {
       NS_WARNING("Failed to dispatch runnable!");
     }
   }
 }
 
 bool
 WorkerPrivate::BlockAndCollectRuntimeStats(JS::RuntimeStats* aRtStats,
                                            bool aAnonymize)
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -54,16 +54,17 @@ class nsITimer;
 class nsIURI;
 template<class T> class nsMainThreadPtrHandle;
 
 namespace JS {
 struct RuntimeStats;
 } // namespace JS
 
 namespace mozilla {
+class TaskQueue;
 namespace dom {
 class Function;
 class MessagePort;
 class MessagePortIdentifier;
 class PromiseNativeHandler;
 class StructuredCloneHolder;
 class WorkerDebuggerGlobalScope;
 class WorkerGlobalScope;
@@ -912,16 +913,18 @@ class WorkerPrivate : public WorkerPriva
 
   // Things touched on worker thread only.
   RefPtr<WorkerGlobalScope> mScope;
   RefPtr<WorkerDebuggerGlobalScope> mDebuggerScope;
   nsTArray<ParentType*> mChildWorkers;
   nsTObserverArray<WorkerHolder*> mHolders;
   nsTArray<nsAutoPtr<TimeoutInfo>> mTimeouts;
   uint32_t mDebuggerEventLoopLevel;
+  RefPtr<TaskQueue> mMainThreadTaskQueue;
+  nsCOMPtr<nsIEventTarget> mMainThreadEventTarget;
 
   struct SyncLoopInfo
   {
     explicit SyncLoopInfo(EventTarget* aEventTarget);
 
     RefPtr<EventTarget> mEventTarget;
     bool mCompleted;
     bool mResult;
@@ -1336,16 +1339,30 @@ public:
   {
     AssertIsOnWorkerThread();
     return mWorkerScriptExecutedSuccessfully;
   }
 
   void
   MaybeDispatchLoadFailedRunnable();
 
+  // Get the event target to use when dispatching to the main thread
+  // from this Worker thread.  This may be the main thread itself or
+  // a TaskQueue throttling runnables to the main thread.
+  nsIEventTarget*
+  MainThreadEventTarget();
+
+  nsresult
+  DispatchToMainThread(nsIRunnable* aRunnable,
+                       uint32_t aFlags = NS_DISPATCH_NORMAL);
+
+  nsresult
+  DispatchToMainThread(already_AddRefed<nsIRunnable> aRunnable,
+                       uint32_t aFlags = NS_DISPATCH_NORMAL);
+
 private:
   WorkerPrivate(WorkerPrivate* aParent,
                 const nsAString& aScriptURL, bool aIsChromeWorker,
                 WorkerType aWorkerType, const nsACString& aSharedWorkerName,
                 WorkerLoadInfo& aLoadInfo);
 
   bool
   MayContinueRunning()
--- a/dom/workers/WorkerRunnable.cpp
+++ b/dom/workers/WorkerRunnable.cpp
@@ -10,16 +10,17 @@
 #include "nsIEventTarget.h"
 #include "nsIGlobalObject.h"
 #include "nsIRunnable.h"
 #include "nsThreadUtils.h"
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/TaskQueue.h"
 #include "mozilla/Telemetry.h"
 
 #include "js/RootingAPI.h"
 #include "js/Value.h"
 
 #include "WorkerPrivate.h"
 #include "WorkerScope.h"
 
@@ -113,20 +114,17 @@ WorkerRunnable::DispatchInternal()
   }
 
   MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
 
   if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) {
     return NS_SUCCEEDED(parent->Dispatch(runnable.forget()));
   }
 
-  nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
-  MOZ_ASSERT(mainThread);
-
-  return NS_SUCCEEDED(mainThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL));
+  return NS_SUCCEEDED(mWorkerPrivate->DispatchToMainThread(runnable.forget()));
 }
 
 void
 WorkerRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
                              bool aDispatchResult)
 {
   MOZ_ASSERT(aWorkerPrivate);
 
@@ -552,20 +550,17 @@ WorkerControlRunnable::DispatchInternal(
   if (mBehavior == WorkerThreadUnchangedBusyCount) {
     return NS_SUCCEEDED(mWorkerPrivate->DispatchControlRunnable(runnable.forget()));
   }
 
   if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) {
     return NS_SUCCEEDED(parent->DispatchControlRunnable(runnable.forget()));
   }
 
-  nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
-  MOZ_ASSERT(mainThread);
-
-  return NS_SUCCEEDED(mainThread->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL));
+  return NS_SUCCEEDED(mWorkerPrivate->DispatchToMainThread(runnable.forget()));
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(WorkerControlRunnable, WorkerRunnable)
 
 WorkerMainThreadRunnable::WorkerMainThreadRunnable(WorkerPrivate* aWorkerPrivate,
                                                    const nsACString& aTelemetryKey)
 : mWorkerPrivate(aWorkerPrivate)
 , mTelemetryKey(aTelemetryKey)
@@ -578,20 +573,18 @@ WorkerMainThreadRunnable::Dispatch(Error
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   TimeStamp startTime = TimeStamp::NowLoRes();
 
   AutoSyncLoopHolder syncLoop(mWorkerPrivate);
 
   mSyncLoopTarget = syncLoop.EventTarget();
-  RefPtr<WorkerMainThreadRunnable> runnable(this);
 
-  DebugOnly<nsresult> rv =
-    NS_DispatchToMainThread(runnable.forget(), NS_DISPATCH_NORMAL);
+  DebugOnly<nsresult> rv = mWorkerPrivate->DispatchToMainThread(this);
   MOZ_ASSERT(NS_SUCCEEDED(rv),
              "Should only fail after xpcom-shutdown-threads and we're gone by then");
 
   if (!syncLoop.Run()) {
     aRv.ThrowUncatchableException();
   }
 
   // Telemetry is apparently not threadsafe
@@ -677,17 +670,17 @@ WorkerProxyToMainThreadRunnable::Dispatc
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
   if (NS_WARN_IF(!HoldWorker())) {
     RunBackOnWorkerThread();
     return false;
   }
 
-  if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(this)))) {
+  if (NS_WARN_IF(NS_FAILED(mWorkerPrivate->DispatchToMainThread(this)))) {
     ReleaseWorker();
     RunBackOnWorkerThread();
     return false;
   }
 
   return true;
 }
 
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -692,17 +692,17 @@ ServiceWorkerGlobalScope::SkipWaiting(Er
     promise->MaybeResolveWithUndefined();
     return promise.forget();
   }
 
   RefPtr<WorkerScopeSkipWaitingRunnable> runnable =
     new WorkerScopeSkipWaitingRunnable(promiseProxy,
                                        NS_ConvertUTF16toUTF8(mScope));
 
-  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
+  MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(runnable.forget()));
   return promise.forget();
 }
 
 bool
 ServiceWorkerGlobalScope::OpenWindowEnabled(JSContext* aCx, JSObject* aObj)
 {
   WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(worker);
--- a/dom/workers/test/test_consoleAndBlobs.html
+++ b/dom/workers/test/test_consoleAndBlobs.html
@@ -17,22 +17,24 @@
     SpecialPowers.addObserver(this, "console-api-log-event", false);
   }
 
   var order = 0;
   consoleListener.prototype  = {
     observe: function(aSubject, aTopic, aData) {
       ok(true, "Something has been received");
       is(aTopic, "console-api-log-event");
-      SpecialPowers.removeObserver(this, "console-api-log-event");
 
       var obj = aSubject.wrappedJSObject;
-      is(obj.arguments[0].size, 3, "The size is correct");
-      is(obj.arguments[0].type, 'foo/bar', "The type is correct");
-      SimpleTest.finish();
+      if (obj.arguments[0] && obj.arguments[0].msg === 'consoleAndBlobs') {
+        SpecialPowers.removeObserver(this, "console-api-log-event");
+        is(obj.arguments[0].blob.size, 3, "The size is correct");
+        is(obj.arguments[0].blob.type, 'foo/bar', "The type is correct");
+        SimpleTest.finish();
+      }
     }
   }
 
   var cl = new consoleListener();
 
   new Worker('worker_consoleAndBlobs.js');
   SimpleTest.waitForExplicitFinish();
 
--- a/dom/workers/test/worker_consoleAndBlobs.js
+++ b/dom/workers/test/worker_consoleAndBlobs.js
@@ -1,8 +1,8 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 "use strict";
 
 var b = new Blob(['123'], { type: 'foo/bar'});
-console.log(b);
+console.log({ msg: 'consoleAndBlobs', blob: b });
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -164,16 +164,17 @@ XMLHttpRequestMainThread::XMLHttpRequest
   : mResponseBodyDecodedPos(0),
     mResponseType(XMLHttpRequestResponseType::_empty),
     mRequestObserver(nullptr),
     mState(State::unsent),
     mFlagSynchronous(false), mFlagAborted(false), mFlagParseBody(false),
     mFlagSyncLooping(false), mFlagBackgroundRequest(false),
     mFlagHadUploadListenersOnSend(false), mFlagACwithCredentials(false),
     mFlagTimedOut(false), mFlagDeleted(false), mFlagSend(false),
+    mSendExtraLoadingEvents(Preferences::GetBool("dom.fire_extra_xhr_loading_readystatechanges", true)),
     mUploadTransferred(0), mUploadTotal(0), mUploadComplete(true),
     mProgressSinceLastProgressEvent(false),
     mRequestSentTime(0), mTimeoutMilliseconds(0),
     mErrorLoad(false), mErrorParsingXML(false),
     mWaitingForOnStopRequest(false),
     mProgressTimerIsActive(false),
     mIsHtml(false),
     mWarnAboutSyncHtml(false),
@@ -1726,17 +1727,19 @@ XMLHttpRequestMainThread::OnDataAvailabl
     }
 
     ChangeState(State::loading);
     return request->Cancel(NS_OK);
   }
 
   mDataAvailable += totalRead;
 
-  ChangeState(State::loading);
+  if (mState == State::headers_received || mSendExtraLoadingEvents) {
+    ChangeState(State::loading);
+  }
 
   if (!mFlagSynchronous && !mProgressTimerIsActive) {
     StartProgressEventTimer();
   }
 
   return NS_OK;
 }
 
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -698,16 +698,24 @@ protected:
   bool mFlagDeleted;
 
   // The XHR2 spec's send() flag. Set when the XHR begins uploading, until it
   // finishes downloading (or an error/abort has occurred during either phase).
   // Used to guard against the user trying to alter headers/etc when it's too
   // late, and ensure the XHR only handles one in-flight request at once.
   bool mFlagSend;
 
+  // Before ProgressEvents were a thing, multiple readystatechange events were
+  // fired during the loading state to give sites a way to monitor XHR progress.
+  // The XHR spec now has proper progress events and dictates that only one
+  // "loading" readystatechange should be fired per send. However, it's possible
+  // that some content still relies on this old behavior, so we're keeping it
+  // (behind a preference) for now. See bug 918719.
+  bool mSendExtraLoadingEvents;
+
   RefPtr<XMLHttpRequestUpload> mUpload;
   int64_t mUploadTransferred;
   int64_t mUploadTotal;
   bool mUploadComplete;
   bool mProgressSinceLastProgressEvent;
 
   // Timeout support
   PRTime mRequestSentTime;
--- a/dom/xhr/XMLHttpRequestWorker.cpp
+++ b/dom/xhr/XMLHttpRequestWorker.cpp
@@ -1607,17 +1607,17 @@ XMLHttpRequestWorker::ReleaseProxy(Relea
   if (mProxy) {
     if (aType == XHRIsGoingAway) {
       // We're in a GC finalizer, so we can't do a sync call here (and we don't
       // need to).
       RefPtr<AsyncTeardownRunnable> runnable =
         new AsyncTeardownRunnable(mProxy);
       mProxy = nullptr;
 
-      if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
+      if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))) {
         NS_ERROR("Failed to dispatch teardown runnable!");
       }
     } else {
       // This isn't necessary if the worker is going away or the XHR is going
       // away.
       if (aType == Default) {
         // Don't let any more events run.
         mProxy->mOuterEventStreamId++;
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -341,18 +341,20 @@ GLLibraryEGL::EnsureInitialized(bool for
 
             MOZ_ASSERT(false, "d3dcompiler DLL loading failed.");
         } while (false);
 
         LoadLibraryForEGLOnWindows(NS_LITERAL_STRING("libGLESv2.dll"));
 
         mEGLLibrary = LoadLibraryForEGLOnWindows(NS_LITERAL_STRING("libEGL.dll"));
 
-        if (!mEGLLibrary)
+        if (!mEGLLibrary) {
+            *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_LOAD");
             return false;
+        }
     }
 
 #else // !Windows
 
     // On non-Windows (Android) we use system copies of libEGL. We look for
     // the APITrace lib, libEGL.so, and libEGL.so.1 in that order.
 
 #if defined(ANDROID)
@@ -367,16 +369,17 @@ GLLibraryEGL::EnsureInitialized(bool for
 #if defined(XP_UNIX)
     if (!mEGLLibrary) {
         mEGLLibrary = PR_LoadLibrary("libEGL.so.1");
     }
 #endif
 
     if (!mEGLLibrary) {
         NS_WARNING("Couldn't load EGL LIB.");
+        *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_LOAD_2");
         return false;
     }
 
 #endif // !Windows
 
 #define SYMBOL(name) \
 { (PRFuncPtr*) &mSymbols.f##name, { "egl" #name, nullptr } }
 
@@ -407,16 +410,17 @@ GLLibraryEGL::EnsureInitialized(bool for
         SYMBOL(BindTexImage),
         SYMBOL(ReleaseTexImage),
         SYMBOL(QuerySurface),
         { nullptr, { nullptr } }
     };
 
     if (!GLLibraryLoader::LoadSymbols(mEGLLibrary, &earlySymbols[0])) {
         NS_WARNING("Couldn't find required entry points in EGL library (early init)");
+        *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_SYM");
         return false;
     }
 
     GLLibraryLoader::SymLoadStruct optionalSymbols[] = {
         // On Android 4.3 and up, certain features like ANDROID_native_fence_sync
         // can only be queried by using a special eglQueryString.
         { (PRFuncPtr*) &mSymbols.fQueryStringImplementationANDROID,
           { "_Z35eglQueryStringImplementationANDROIDPvi", nullptr } },
--- a/gfx/ipc/GPUProcessManager.cpp
+++ b/gfx/ipc/GPUProcessManager.cpp
@@ -146,17 +146,17 @@ GPUProcessManager::EnsureGPUReady()
   if (mGPUChild) {
     mGPUChild->EnsureGPUReady();
   }
 }
 
 void
 GPUProcessManager::EnsureImageBridgeChild()
 {
-  if (ImageBridgeChild::IsCreated()) {
+  if (ImageBridgeChild::GetSingleton()) {
     return;
   }
 
   EnsureGPUReady();
 
   if (!mGPUChild) {
     ImageBridgeChild::InitSameProcess();
     return;
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -145,17 +145,19 @@ ImageContainer::ImageContainer(uint64_t 
 {
   MOZ_ASSERT(mAsyncContainerID != sInvalidAsyncContainerId);
 }
 
 ImageContainer::~ImageContainer()
 {
   if (mIPDLChild) {
     mIPDLChild->ForgetImageContainer();
-    ImageBridgeChild::DispatchReleaseImageContainer(mIPDLChild);
+    if (RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton()) {
+      imageBridge->ReleaseImageContainer(mIPDLChild);
+    }
   }
 }
 
 RefPtr<PlanarYCbCrImage>
 ImageContainer::CreatePlanarYCbCrImage()
 {
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   if (mImageClient && mImageClient->AsImageClientSingle()) {
@@ -264,28 +266,32 @@ ImageContainer::ClearImagesFromImageBrid
 }
 
 void
 ImageContainer::SetCurrentImages(const nsTArray<NonOwningImage>& aImages)
 {
   MOZ_ASSERT(!aImages.IsEmpty());
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   if (mImageClient) {
-    ImageBridgeChild::DispatchImageClientUpdate(mImageClient, this);
+    if (RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton()) {
+      imageBridge->UpdateImageClient(mImageClient, this);
+    }
   }
   SetCurrentImageInternal(aImages);
 }
 
 void
 ImageContainer::ClearAllImages()
 {
   if (mImageClient) {
     // Let ImageClient release all TextureClients. This doesn't return
     // until ImageBridge has called ClearCurrentImageFromImageBridge.
-    ImageBridgeChild::FlushAllImages(mImageClient, this);
+    if (RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton()) {
+      imageBridge->FlushAllImages(mImageClient, this);
+    }
     return;
   }
 
   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
   SetCurrentImageInternal(nsTArray<NonOwningImage>());
 }
 
 void
--- a/gfx/layers/ipc/CompositableForwarder.h
+++ b/gfx/layers/ipc/CompositableForwarder.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_LAYERS_COMPOSITABLEFORWARDER
 #define MOZILLA_LAYERS_COMPOSITABLEFORWARDER
 
 #include <stdint.h>                     // for int32_t, uint64_t
 #include "gfxTypes.h"
 #include "mozilla/Attributes.h"         // for override
+#include "mozilla/UniquePtr.h"
 #include "mozilla/layers/CompositableClient.h"  // for CompositableClient
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/ISurfaceAllocator.h"  // for ISurfaceAllocator
 #include "mozilla/layers/LayersTypes.h"  // for LayersBackend
 #include "mozilla/layers/TextureClient.h"  // for TextureClient
 #include "mozilla/layers/TextureForwarder.h"  // for TextureForwarder
 #include "nsRegion.h"                   // for nsIntRegion
 #include "mozilla/gfx/Rect.h"
@@ -74,19 +75,20 @@ public:
  * additionally forward modifications of the Layer tree).
  * ImageBridgeChild is another CompositableForwarder.
  */
 class CompositableForwarder : public TextureForwarder
 {
 public:
 
   CompositableForwarder()
-    : mActiveResourceTracker(1000, "CompositableForwarder")
-    , mSerial(++sSerialCounter)
-  {}
+    : mSerial(++sSerialCounter)
+  {
+    mActiveResourceTracker = MakeUnique<ActiveResourceTracker>(1000, "CompositableForwarder");
+  }
 
   /**
    * Setup the IPDL actor for aCompositable to be part of layers
    * transactions.
    */
   virtual void Connect(CompositableClient* aCompositable,
                        ImageContainer* aImageContainer = nullptr) = 0;
 
@@ -202,26 +204,26 @@ public:
     return mTextureFactoryIdentifier.mSupportsPartialUploads;
   }
 
   const TextureFactoryIdentifier& GetTextureFactoryIdentifier() const
   {
     return mTextureFactoryIdentifier;
   }
 
-  ActiveResourceTracker& GetActiveResourceTracker() { return mActiveResourceTracker; }
+  ActiveResourceTracker& GetActiveResourceTracker() { return *mActiveResourceTracker.get(); }
 
 protected:
   TextureFactoryIdentifier mTextureFactoryIdentifier;
 
   nsTArray<RefPtr<TextureClient> > mTexturesToRemove;
   nsTArray<RefPtr<CompositableClient>> mCompositableClientsToRemove;
   RefPtr<SyncObject> mSyncObject;
 
-  ActiveResourceTracker mActiveResourceTracker;
+  UniquePtr<ActiveResourceTracker> mActiveResourceTracker;
 
   const int32_t mSerial;
   static mozilla::Atomic<int32_t> sSerialCounter;
 };
 
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -249,17 +249,16 @@ CompositorBridgeChild::ChildProcessHasCo
 }
 
 PLayerTransactionChild*
 CompositorBridgeChild::AllocPLayerTransactionChild(const nsTArray<LayersBackend>& aBackendHints,
                                                    const uint64_t& aId,
                                                    TextureFactoryIdentifier*,
                                                    bool*)
 {
-  MOZ_ASSERT(mCanSend);
   LayerTransactionChild* c = new LayerTransactionChild(aId);
   c->AddIPDLReference();
   return c;
 }
 
 bool
 CompositorBridgeChild::DeallocPLayerTransactionChild(PLayerTransactionChild* actor)
 {
@@ -689,142 +688,127 @@ CompositorBridgeChild::CancelNotifyAfter
   if (tabChild == aTabChild) {
     mWeakTabChild = nullptr;
   }
 }
 
 bool
 CompositorBridgeChild::SendWillClose()
 {
-  MOZ_ASSERT(mCanSend);
-  if (!mCanSend) {
-    return true;
-  }
+  MOZ_RELEASE_ASSERT(mCanSend);
   return PCompositorBridgeChild::SendWillClose();
 }
 
 bool
 CompositorBridgeChild::SendPause()
 {
-  MOZ_ASSERT(mCanSend);
   if (!mCanSend) {
-    return true;
+    return false;
   }
   return PCompositorBridgeChild::SendPause();
 }
 
 bool
 CompositorBridgeChild::SendResume()
 {
-  MOZ_ASSERT(mCanSend);
   if (!mCanSend) {
-    return true;
+    return false;
   }
   return PCompositorBridgeChild::SendResume();
 }
 
 bool
 CompositorBridgeChild::SendNotifyChildCreated(const uint64_t& id)
 {
-  MOZ_ASSERT(mCanSend);
   if (!mCanSend) {
-    return true;
+    return false;
   }
   return PCompositorBridgeChild::SendNotifyChildCreated(id);
 }
 
 bool
 CompositorBridgeChild::SendAdoptChild(const uint64_t& id)
 {
-  MOZ_ASSERT(mCanSend);
   if (!mCanSend) {
-    return true;
+    return false;
   }
   return PCompositorBridgeChild::SendAdoptChild(id);
 }
 
 bool
 CompositorBridgeChild::SendMakeSnapshot(const SurfaceDescriptor& inSnapshot, const gfx::IntRect& dirtyRect)
 {
-  MOZ_ASSERT(mCanSend);
   if (!mCanSend) {
-    return true;
+    return false;
   }
   return PCompositorBridgeChild::SendMakeSnapshot(inSnapshot, dirtyRect);
 }
 
 bool
 CompositorBridgeChild::SendFlushRendering()
 {
-  MOZ_ASSERT(mCanSend);
   if (!mCanSend) {
-    return true;
+    return false;
   }
   return PCompositorBridgeChild::SendFlushRendering();
 }
 
 bool
 CompositorBridgeChild::SendStartFrameTimeRecording(const int32_t& bufferSize, uint32_t* startIndex)
 {
-  MOZ_ASSERT(mCanSend);
   if (!mCanSend) {
-    return true;
+    return false;
   }
   return PCompositorBridgeChild::SendStartFrameTimeRecording(bufferSize, startIndex);
 }
 
 bool
 CompositorBridgeChild::SendStopFrameTimeRecording(const uint32_t& startIndex, nsTArray<float>* intervals)
 {
-  MOZ_ASSERT(mCanSend);
   if (!mCanSend) {
-    return true;
+    return false;
   }
   return PCompositorBridgeChild::SendStopFrameTimeRecording(startIndex, intervals);
 }
 
 bool
 CompositorBridgeChild::SendNotifyRegionInvalidated(const nsIntRegion& region)
 {
-  MOZ_ASSERT(mCanSend);
   if (!mCanSend) {
-    return true;
+    return false;
   }
   return PCompositorBridgeChild::SendNotifyRegionInvalidated(region);
 }
 
 bool
 CompositorBridgeChild::SendRequestNotifyAfterRemotePaint()
 {
-  MOZ_ASSERT(mCanSend);
   if (!mCanSend) {
-    return true;
+    return false;
   }
   return PCompositorBridgeChild::SendRequestNotifyAfterRemotePaint();
 }
 
 bool
 CompositorBridgeChild::SendClearApproximatelyVisibleRegions(uint64_t aLayersId,
                                                             uint32_t aPresShellId)
 {
-  MOZ_ASSERT(mCanSend);
   if (!mCanSend) {
-    return true;
+    return false;
   }
   return PCompositorBridgeChild::SendClearApproximatelyVisibleRegions(aLayersId,
                                                                 aPresShellId);
 }
 
 bool
 CompositorBridgeChild::SendNotifyApproximatelyVisibleRegion(const ScrollableLayerGuid& aGuid,
                                                             const CSSIntRegion& aRegion)
 {
-  MOZ_ASSERT(mCanSend);
   if (!mCanSend) {
-    return true;
+    return false;
   }
   return PCompositorBridgeChild::SendNotifyApproximatelyVisibleRegion(aGuid, aRegion);
 }
 
 PTextureChild*
 CompositorBridgeChild::AllocPTextureChild(const SurfaceDescriptor&,
                                           const LayersBackend&,
                                           const TextureFlags&,
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -28,21 +28,24 @@
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/layers/ISurfaceAllocator.h"  // for ISurfaceAllocator
 #include "mozilla/layers/ImageClient.h"  // for ImageClient
 #include "mozilla/layers/ImageContainerChild.h"
 #include "mozilla/layers/LayersMessages.h"  // for CompositableOperation
 #include "mozilla/layers/PCompositableChild.h"  // for PCompositableChild
 #include "mozilla/layers/TextureClient.h"  // for TextureClient
 #include "mozilla/mozalloc.h"           // for operator new, etc
+#include "mtransport/runnable_utils.h"
+#include "nsContentUtils.h"
 #include "nsISupportsImpl.h"            // for ImageContainer::AddRef, etc
 #include "nsTArray.h"                   // for AutoTArray, nsTArray, etc
 #include "nsTArrayForwardDeclare.h"     // for AutoTArray
 #include "nsThreadUtils.h"              // for NS_IsMainThread
 #include "nsXULAppAPI.h"                // for XRE_GetIOMessageLoop
+#include "mozilla/StaticMutex.h"
 #include "mozilla/StaticPtr.h"          // for StaticRefPtr
 #include "mozilla/layers/TextureClient.h"
 
 namespace mozilla {
 namespace ipc {
 class Shmem;
 } // namespace ipc
 
@@ -161,19 +164,16 @@ struct CompositableTransaction
 };
 
 struct AutoEndTransaction {
   explicit AutoEndTransaction(CompositableTransaction* aTxn) : mTxn(aTxn) {}
   ~AutoEndTransaction() { mTxn->End(); }
   CompositableTransaction* mTxn;
 };
 
-/* static */
-Atomic<bool> ImageBridgeChild::sIsShutDown(false);
-
 void
 ImageBridgeChild::UseTextures(CompositableClient* aCompositable,
                               const nsTArray<TimedTextureClient>& aTextures)
 {
   MOZ_ASSERT(aCompositable);
   MOZ_ASSERT(aCompositable->GetIPDLActor());
   MOZ_ASSERT(aCompositable->IsConnected());
 
@@ -386,16 +386,17 @@ ImageBridgeChild::CancelWaitForRecycle(u
   RefPtr<TextureClient> client = mTexturesWaitingRecycled.Get(aTextureId);
   if (!client) {
     return;
   }
   mTexturesWaitingRecycled.Remove(aTextureId);
 }
 
 // Singleton
+static StaticMutex sImageBridgeSingletonLock;
 static StaticRefPtr<ImageBridgeChild> sImageBridgeChildSingleton;
 static Thread *sImageBridgeChildThread = nullptr;
 
 void
 ImageBridgeChild::FallbackDestroyActors() {
   if (mTxn && !mTxn->mDestroyedActors.IsEmpty()) {
     mTxn->FallbackDestroyActors();
   }
@@ -445,127 +446,141 @@ public:
   }
 
 private:
   SynchronousTask* mTask;
   ReentrantMonitorAutoEnter mAutoEnter;
 };
 
 // dispatched function
-static void ImageBridgeShutdownStep1(SynchronousTask* aTask)
+void
+ImageBridgeChild::ShutdownStep1(SynchronousTask* aTask)
 {
   AutoCompleteTask complete(aTask);
 
   MOZ_ASSERT(InImageBridgeChildThread(),
              "Should be in ImageBridgeChild thread.");
 
   MediaSystemResourceManager::Shutdown();
 
-  if (sImageBridgeChildSingleton) {
-    // Force all managed protocols to shut themselves down cleanly
-    InfallibleTArray<PCompositableChild*> compositables;
-    sImageBridgeChildSingleton->ManagedPCompositableChild(compositables);
-    for (int i = compositables.Length() - 1; i >= 0; --i) {
-      auto compositable = CompositableClient::FromIPDLActor(compositables[i]);
-      if (compositable) {
-        compositable->Destroy();
-      }
+  // Force all managed protocols to shut themselves down cleanly
+  InfallibleTArray<PCompositableChild*> compositables;
+  ManagedPCompositableChild(compositables);
+  for (int i = compositables.Length() - 1; i >= 0; --i) {
+    auto compositable = CompositableClient::FromIPDLActor(compositables[i]);
+    if (compositable) {
+      compositable->Destroy();
     }
-    InfallibleTArray<PTextureChild*> textures;
-    sImageBridgeChildSingleton->ManagedPTextureChild(textures);
-    for (int i = textures.Length() - 1; i >= 0; --i) {
-      RefPtr<TextureClient> client = TextureClient::AsTextureClient(textures[i]);
-      if (client) {
-        client->Destroy();
-      }
+  }
+  InfallibleTArray<PTextureChild*> textures;
+  ManagedPTextureChild(textures);
+  for (int i = textures.Length() - 1; i >= 0; --i) {
+    RefPtr<TextureClient> client = TextureClient::AsTextureClient(textures[i]);
+    if (client) {
+      client->Destroy();
     }
-    sImageBridgeChildSingleton->FallbackDestroyActors();
+  }
+  FallbackDestroyActors();
 
-    sImageBridgeChildSingleton->SendWillClose();
-    sImageBridgeChildSingleton->MarkShutDown();
-    // From now on, no message can be sent through the image bridge from the
-    // client side except the final Stop message.
-  }
+  SendWillClose();
+  MarkShutDown();
+
+  // From now on, no message can be sent through the image bridge from the
+  // client side except the final Stop message.
 }
 
 // dispatched function
-static void
-ImageBridgeShutdownStep2(SynchronousTask* aTask)
+void
+ImageBridgeChild::ShutdownStep2(SynchronousTask* aTask)
 {
   AutoCompleteTask complete(aTask);
 
   MOZ_ASSERT(InImageBridgeChildThread(),
              "Should be in ImageBridgeChild thread.");
 
-  sImageBridgeChildSingleton->Close();
+  if (!mCalledClose) {
+    Close();
+    mCalledClose = true;
+  }
+}
+
+void
+ImageBridgeChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+  mCanSend = false;
+  mCalledClose = true;
 }
 
-/* static */ void
-CreateImageClientSync(SynchronousTask* aTask,
-                      RefPtr<ImageBridgeChild> aChild,
-                      RefPtr<ImageClient>* result,
-                      CompositableType aType,
-                      ImageContainer* aImageContainer,
-                      ImageContainerChild* aContainerChild)
+void
+ImageBridgeChild::DeallocPImageBridgeChild()
+{
+  this->Release();
+}
+
+void
+ImageBridgeChild::CreateImageClientSync(SynchronousTask* aTask,
+                                        RefPtr<ImageClient>* result,
+                                        CompositableType aType,
+                                        ImageContainer* aImageContainer,
+                                        ImageContainerChild* aContainerChild)
 {
   AutoCompleteTask complete(aTask);
-  *result = aChild->CreateImageClientNow(aType, aImageContainer, aContainerChild);
+  *result = CreateImageClientNow(aType, aImageContainer, aContainerChild);
 }
 
 // dispatched function
-static void CreateCanvasClientSync(SynchronousTask* aTask,
-                                   CanvasClient::CanvasClientType aType,
-                                   TextureFlags aFlags,
-                                   RefPtr<CanvasClient>* const outResult)
+void
+ImageBridgeChild::CreateCanvasClientSync(SynchronousTask* aTask,
+                                         CanvasClient::CanvasClientType aType,
+                                         TextureFlags aFlags,
+                                         RefPtr<CanvasClient>* const outResult)
 {
   AutoCompleteTask complete(aTask);
-  *outResult = sImageBridgeChildSingleton->CreateCanvasClientNow(aType, aFlags);
-}
-
-static void ConnectImageBridge(ImageBridgeChild * child, ImageBridgeParent * parent)
-{
-  MessageLoop *parentMsgLoop = parent->GetMessageLoop();
-  ipc::MessageChannel *parentChannel = parent->GetIPCChannel();
-  child->Open(parentChannel, parentMsgLoop, mozilla::ipc::ChildSide);
+  *outResult = CreateCanvasClientNow(aType, aFlags);
 }
 
 ImageBridgeChild::ImageBridgeChild()
-  : mShuttingDown(false)
+  : mCanSend(false)
+  , mCalledClose(false)
   , mFwdTransactionId(0)
 #ifdef MOZ_WIDGET_GONK
   , mWaitingFenceHandleMutex("ImageBridgeChild::mWaitingFenceHandleMutex")
 #endif
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mTxn = new CompositableTransaction();
+  mShutdownObserver = new ShutdownObserver(this);
 }
 
 ImageBridgeChild::~ImageBridgeChild()
 {
+  // We should have already removed this in WillShutdown, since it must be
+  // removed on the main thread.
+  MOZ_ASSERT(!mShutdownObserver);
+
   delete mTxn;
 }
 
 void
 ImageBridgeChild::MarkShutDown()
 {
-  MOZ_ASSERT(!mShuttingDown);
   mTexturesWaitingRecycled.Clear();
   mTrackersHolder.DestroyAsyncTransactionTrackersHolder();
 
-  mShuttingDown = true;
+  mCanSend = false;
 }
 
 void
 ImageBridgeChild::Connect(CompositableClient* aCompositable,
                           ImageContainer* aImageContainer)
 {
   MOZ_ASSERT(aCompositable);
-  MOZ_ASSERT(!mShuttingDown);
   MOZ_ASSERT(InImageBridgeChildThread());
+  MOZ_ASSERT(CanSend());
 
   uint64_t id = 0;
 
   PImageContainerChild* imageContainerChild = nullptr;
   if (aImageContainer)
     imageContainerChild = aImageContainer->GetPImageContainerChild();
 
   PCompositableChild* child =
@@ -574,265 +589,255 @@ ImageBridgeChild::Connect(CompositableCl
   MOZ_ASSERT(child);
   aCompositable->InitIPDLActor(child, id);
 }
 
 PCompositableChild*
 ImageBridgeChild::AllocPCompositableChild(const TextureInfo& aInfo,
                                           PImageContainerChild* aChild, uint64_t* aID)
 {
-  MOZ_ASSERT(!mShuttingDown);
+  MOZ_ASSERT(CanSend());
   return AsyncCompositableChild::CreateActor();
 }
 
 bool
 ImageBridgeChild::DeallocPCompositableChild(PCompositableChild* aActor)
 {
   AsyncCompositableChild::DestroyActor(aActor);
   return true;
 }
 
 
 Thread* ImageBridgeChild::GetThread() const
 {
   return sImageBridgeChildThread;
 }
 
-ImageBridgeChild* ImageBridgeChild::GetSingleton()
+/* static */ RefPtr<ImageBridgeChild>
+ImageBridgeChild::GetSingleton()
 {
+  StaticMutexAutoLock lock(sImageBridgeSingletonLock);
   return sImageBridgeChildSingleton;
 }
 
-bool ImageBridgeChild::IsCreated()
-{
-  return GetSingleton() != nullptr;
-}
-
-static void
-ReleaseImageContainerNow(RefPtr<ImageBridgeChild> aBridge, RefPtr<ImageContainerChild> aChild)
-{
-  MOZ_ASSERT(InImageBridgeChildThread());
-
-  aChild->SendAsyncDelete();
-}
-
-// static
-void ImageBridgeChild::DispatchReleaseImageContainer(ImageContainerChild* aChild)
+void
+ImageBridgeChild::ReleaseImageContainer(RefPtr<ImageContainerChild> aChild)
 {
   if (!aChild) {
     return;
   }
 
-  RefPtr<ImageBridgeChild> bridge = GetSingleton();
-  if (!bridge) {
+  if (!InImageBridgeChildThread()) {
+    RefPtr<Runnable> runnable = WrapRunnable(
+      RefPtr<ImageBridgeChild>(this),
+      &ImageBridgeChild::ReleaseImageContainer,
+      aChild);
+    GetMessageLoop()->PostTask(runnable.forget());
     return;
   }
 
-  RefPtr<ImageContainerChild> child(aChild);
-  bridge->GetMessageLoop()->PostTask(
-    NewRunnableFunction(&ReleaseImageContainerNow, bridge, child));
+  aChild->SendAsyncDelete();
 }
 
-static void ReleaseTextureClientNow(TextureClient* aClient)
+void
+ImageBridgeChild::ReleaseTextureClientNow(TextureClient* aClient)
 {
   MOZ_ASSERT(InImageBridgeChildThread());
   RELEASE_MANUALLY(aClient);
 }
 
-// static
-void ImageBridgeChild::DispatchReleaseTextureClient(TextureClient* aClient)
+/* static */ void
+ImageBridgeChild::DispatchReleaseTextureClient(TextureClient* aClient)
 {
   if (!aClient) {
     return;
   }
 
-  if (!IsCreated()) {
+  RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
+  if (!imageBridge) {
     // TextureClient::Release should normally happen in the ImageBridgeChild
     // thread because it usually generate some IPDL messages.
     // However, if we take this branch it means that the ImageBridgeChild
     // has already shut down, along with the TextureChild, which means no
     // message will be sent and it is safe to run this code from any thread.
     MOZ_ASSERT(aClient->GetIPDLActor() == nullptr);
     RELEASE_MANUALLY(aClient);
     return;
   }
 
-  sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
-    NewRunnableFunction(&ReleaseTextureClientNow, aClient));
+  RefPtr<Runnable> runnable = WrapRunnable(
+    imageBridge,
+    &ImageBridgeChild::ReleaseTextureClientNow,
+    aClient);
+  imageBridge->GetMessageLoop()->PostTask(runnable.forget());
 }
 
-static void
-UpdateImageClientNow(RefPtr<ImageClient> aClient, RefPtr<ImageContainer>&& aContainer)
+void
+ImageBridgeChild::UpdateImageClient(RefPtr<ImageClient> aClient, RefPtr<ImageContainer> aContainer)
 {
-  if (!ImageBridgeChild::IsCreated() || ImageBridgeChild::IsShutDown()) {
-    NS_WARNING("Something is holding on to graphics resources after the shutdown"
-               "of the graphics subsystem!");
+  if (!aClient || !aContainer) {
     return;
   }
-  MOZ_ASSERT(aClient);
-  MOZ_ASSERT(aContainer);
+
+  if (!InImageBridgeChildThread()) {
+    RefPtr<Runnable> runnable = WrapRunnable(
+      RefPtr<ImageBridgeChild>(this),
+      &ImageBridgeChild::UpdateImageClient,
+      aClient,
+      aContainer);
+    GetMessageLoop()->PostTask(runnable.forget());
+    return;
+  }
+
+  if (!CanSend()) {
+    return;
+  }
 
   // If the client has become disconnected before this event was dispatched,
   // early return now.
   if (!aClient->IsConnected()) {
     return;
   }
 
-  sImageBridgeChildSingleton->BeginTransaction();
+  BeginTransaction();
   aClient->UpdateImage(aContainer, Layer::CONTENT_OPAQUE);
-  sImageBridgeChildSingleton->EndTransaction();
+  EndTransaction();
 }
 
-// static
-void ImageBridgeChild::DispatchImageClientUpdate(ImageClient* aClient,
-                                                 ImageContainer* aContainer)
-{
-  if (!ImageBridgeChild::IsCreated() || ImageBridgeChild::IsShutDown()) {
-    NS_WARNING("Something is holding on to graphics resources after the shutdown"
-               "of the graphics subsystem!");
-    return;
-  }
-  if (!aClient || !aContainer || !IsCreated()) {
-    return;
-  }
-
-  if (InImageBridgeChildThread()) {
-    UpdateImageClientNow(aClient, aContainer);
-    return;
-  }
-
-  sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
-    NewRunnableFunction(&UpdateImageClientNow,
-      RefPtr<ImageClient>(aClient), RefPtr<ImageContainer>(aContainer)));
-}
-
-static void
-UpdateAsyncCanvasRendererSync(SynchronousTask* aTask, AsyncCanvasRenderer* aWrapper)
+void
+ImageBridgeChild::UpdateAsyncCanvasRendererSync(SynchronousTask* aTask, AsyncCanvasRenderer* aWrapper)
 {
   AutoCompleteTask complete(aTask);
 
-  ImageBridgeChild::UpdateAsyncCanvasRendererNow(aWrapper);
+  UpdateAsyncCanvasRendererNow(aWrapper);
 }
 
-// static
-void ImageBridgeChild::UpdateAsyncCanvasRenderer(AsyncCanvasRenderer* aWrapper)
+void
+ImageBridgeChild::UpdateAsyncCanvasRenderer(AsyncCanvasRenderer* aWrapper)
 {
   aWrapper->GetCanvasClient()->UpdateAsync(aWrapper);
 
   if (InImageBridgeChildThread()) {
     UpdateAsyncCanvasRendererNow(aWrapper);
     return;
   }
 
   SynchronousTask task("UpdateAsyncCanvasRenderer Lock");
 
-  sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
-    NewRunnableFunction(&UpdateAsyncCanvasRendererSync, &task, aWrapper));
+  RefPtr<Runnable> runnable = WrapRunnable(
+    RefPtr<ImageBridgeChild>(this),
+    &ImageBridgeChild::UpdateAsyncCanvasRendererSync,
+    &task,
+    aWrapper);
+  GetMessageLoop()->PostTask(runnable.forget());
 
   task.Wait();
 }
 
-// static
-void ImageBridgeChild::UpdateAsyncCanvasRendererNow(AsyncCanvasRenderer* aWrapper)
+void
+ImageBridgeChild::UpdateAsyncCanvasRendererNow(AsyncCanvasRenderer* aWrapper)
 {
   MOZ_ASSERT(aWrapper);
-  sImageBridgeChildSingleton->BeginTransaction();
+
+  if (!CanSend()) {
+    return;
+  }
+
+  BeginTransaction();
   aWrapper->GetCanvasClient()->Updated();
-  sImageBridgeChildSingleton->EndTransaction();
+  EndTransaction();
 }
 
-static void
-FlushAllImagesSync(SynchronousTask* aTask,
-                   ImageClient* aClient,
-                   ImageContainer* aContainer,
-                   RefPtr<AsyncTransactionWaiter>&& aWaiter)
+void
+ImageBridgeChild::FlushAllImagesSync(SynchronousTask* aTask,
+                                     ImageClient* aClient,
+                                     ImageContainer* aContainer,
+                                     RefPtr<AsyncTransactionWaiter> aWaiter)
 {
 #ifdef MOZ_WIDGET_GONK
   MOZ_ASSERT(aWaiter);
 #else
   MOZ_ASSERT(!aWaiter);
 #endif
 
   AutoCompleteTask complete(aTask);
 
-  if (!ImageBridgeChild::IsCreated() || ImageBridgeChild::IsShutDown()) {
-    // How sad. If we get into this branch it means that the ImageBridge
-    // got destroyed between the time we ImageBridgeChild::FlushAllImage
-    // was called on some thread, and the time this function was proxied
-    // to the ImageBridge thread. ImageBridge gets destroyed way to late
-    // in the shutdown of gecko for this to be happening for a good reason.
-    NS_WARNING("Something is holding on to graphics resources after the shutdown"
-               "of the graphics subsystem!");
+  if (!CanSend()) {
 #ifdef MOZ_WIDGET_GONK
     aWaiter->DecrementWaitCount();
 #endif
     return;
   }
+
   MOZ_ASSERT(aClient);
-  sImageBridgeChildSingleton->BeginTransaction();
+  BeginTransaction();
   if (aContainer) {
     aContainer->ClearImagesFromImageBridge();
   }
   aClient->FlushAllImages(aWaiter);
-  sImageBridgeChildSingleton->EndTransaction();
+  EndTransaction();
   // This decrement is balanced by the increment in FlushAllImages.
   // If any AsyncTransactionTrackers were created by FlushAllImages and attached
   // to aWaiter, aWaiter will not complete until those trackers all complete.
   // Otherwise, aWaiter will be ready to complete now.
 #ifdef MOZ_WIDGET_GONK
   aWaiter->DecrementWaitCount();
 #endif
 }
 
-// static
-void ImageBridgeChild::FlushAllImages(ImageClient* aClient,
-                                      ImageContainer* aContainer)
+void
+ImageBridgeChild::FlushAllImages(ImageClient* aClient, ImageContainer* aContainer)
 {
-  if (!IsCreated() || IsShutDown()) {
-    return;
-  }
   MOZ_ASSERT(aClient);
-  MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown);
   MOZ_ASSERT(!InImageBridgeChildThread());
+
   if (InImageBridgeChildThread()) {
     NS_ERROR("ImageBridgeChild::FlushAllImages() is called on ImageBridge thread.");
     return;
   }
 
   SynchronousTask task("FlushAllImages Lock");
 
   RefPtr<AsyncTransactionWaiter> waiter;
 #ifdef MOZ_WIDGET_GONK
   waiter = new AsyncTransactionWaiter();
   // This increment is balanced by the decrement in FlushAllImagesSync
   waiter->IncrementWaitCount();
 #endif
-  sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
-    NewRunnableFunction(&FlushAllImagesSync, &task, aClient, aContainer, waiter));
+
+  // RefPtrs on arguments are not needed since this dispatches synchronously.
+  RefPtr<Runnable> runnable = WrapRunnable(
+    RefPtr<ImageBridgeChild>(this),
+    &ImageBridgeChild::FlushAllImagesSync,
+    &task,
+    aClient,
+    aContainer,
+    waiter);
+  GetMessageLoop()->PostTask(runnable.forget());
 
   task.Wait();
 
 #ifdef MOZ_WIDGET_GONK
   waiter->WaitComplete();
 #endif
 }
 
 void
 ImageBridgeChild::BeginTransaction()
 {
-  MOZ_ASSERT(!mShuttingDown);
+  MOZ_ASSERT(CanSend());
   MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?");
   UpdateFwdTransactionId();
   mTxn->Begin();
 }
 
 void
 ImageBridgeChild::EndTransaction()
 {
-  MOZ_ASSERT(!mShuttingDown);
+  MOZ_ASSERT(CanSend());
   MOZ_ASSERT(!mTxn->Finished(), "forgot BeginTransaction?");
 
   AutoEndTransaction _(mTxn);
 
   if (mTxn->IsEmpty()) {
     return;
   }
 
@@ -867,183 +872,231 @@ ImageBridgeChild::EndTransaction()
     NS_RUNTIMEABORT("not reached");
   }
 }
 
 void
 ImageBridgeChild::SendImageBridgeThreadId()
 {
 #ifdef MOZ_WIDGET_GONK
-  SendImageBridgeThreadId(gettid());
+  PImageBridgeChild::SendImageBridgeThreadId(gettid());
 #endif
 }
 
-static void CallSendImageBridgeThreadId(ImageBridgeChild* aImageBridgeChild)
-{
-  MOZ_ASSERT(InImageBridgeChildThread());
-  aImageBridgeChild->SendImageBridgeThreadId();
-}
-
 bool
 ImageBridgeChild::InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   gfxPlatform::GetPlatform();
 
   sImageBridgeChildThread = new ImageBridgeThread();
   if (!sImageBridgeChildThread->Start()) {
     return false;
   }
 
-  sImageBridgeChildSingleton = new ImageBridgeChild();
+  RefPtr<ImageBridgeChild> child = new ImageBridgeChild();
 
-  MessageLoop* loop = sImageBridgeChildSingleton->GetMessageLoop();
+  RefPtr<Runnable> runnable = NewRunnableMethod<Endpoint<PImageBridgeChild>&&>(
+    child,
+    &ImageBridgeChild::Bind,
+    Move(aEndpoint));
+  child->GetMessageLoop()->PostTask(runnable.forget());
 
-  loop->PostTask(NewRunnableMethod<Endpoint<PImageBridgeChild>&&>(
-    sImageBridgeChildSingleton, &ImageBridgeChild::Bind, Move(aEndpoint)));
-  loop->PostTask(NewRunnableFunction(
-    CallSendImageBridgeThreadId, sImageBridgeChildSingleton.get()));
+  // Assign this after so other threads can't post messages before we connect to IPDL.
+  {
+    StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+    sImageBridgeChildSingleton = child;
+  }
 
-  return sImageBridgeChildSingleton;
+  return true;
 }
 
 void
 ImageBridgeChild::Bind(Endpoint<PImageBridgeChild>&& aEndpoint)
 {
-  aEndpoint.Bind(this);
+  if (!aEndpoint.Bind(this)) {
+    return;
+  }
+
+  // This reference is dropped in DeallocPImageBridgeChild.
+  this->AddRef();
+
+  mCanSend = true;
+  SendImageBridgeThreadId();
+}
+
+void
+ImageBridgeChild::BindSameProcess(RefPtr<ImageBridgeParent> aParent)
+{
+  MessageLoop *parentMsgLoop = aParent->GetMessageLoop();
+  ipc::MessageChannel *parentChannel = aParent->GetIPCChannel();
+  Open(parentChannel, parentMsgLoop, mozilla::ipc::ChildSide);
+
+  // This reference is dropped in DeallocPImageBridgeChild.
+  this->AddRef();
+
+  mCanSend = true;
+  SendImageBridgeThreadId();
 }
 
 void ImageBridgeChild::ShutDown()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  sIsShutDown = true;
+  if (RefPtr<ImageBridgeChild> child = GetSingleton()) {
+    child->WillShutdown();
 
-  if (ImageBridgeChild::IsCreated()) {
-    MOZ_ASSERT(!sImageBridgeChildSingleton->mShuttingDown);
+    StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+    sImageBridgeChildSingleton = nullptr;
+  }
 
-    {
-      SynchronousTask task("ImageBridge ShutdownStep1 lock");
+  delete sImageBridgeChildThread;
+  sImageBridgeChildThread = nullptr;
+}
 
-      sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
-                      NewRunnableFunction(&ImageBridgeShutdownStep1, &task));
-
-      task.Wait();
-    }
+void
+ImageBridgeChild::WillShutdown()
+{
+  {
+    SynchronousTask task("ImageBridge ShutdownStep1 lock");
 
-    {
-      SynchronousTask task("ImageBridge ShutdownStep2 lock");
+    RefPtr<Runnable> runnable = WrapRunnable(
+      RefPtr<ImageBridgeChild>(this),
+      &ImageBridgeChild::ShutdownStep1,
+      &task);
+    GetMessageLoop()->PostTask(runnable.forget());
 
-      sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
-                      NewRunnableFunction(&ImageBridgeShutdownStep2, &task));
+    task.Wait();
+  }
+
+  {
+    SynchronousTask task("ImageBridge ShutdownStep2 lock");
 
-      task.Wait();
-    }
+    RefPtr<Runnable> runnable = WrapRunnable(
+      RefPtr<ImageBridgeChild>(this),
+      &ImageBridgeChild::ShutdownStep2,
+      &task);
+    GetMessageLoop()->PostTask(runnable.forget());
 
-    sImageBridgeChildSingleton = nullptr;
+    task.Wait();
+  }
 
-    delete sImageBridgeChildThread;
-    sImageBridgeChildThread = nullptr;
+  if (mShutdownObserver) {
+    mShutdownObserver->Unregister();
+    mShutdownObserver = nullptr;
   }
 }
 
 void
 ImageBridgeChild::InitSameProcess()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on the main Thread!");
 
   MOZ_ASSERT(!sImageBridgeChildSingleton);
   MOZ_ASSERT(!sImageBridgeChildThread);
 
   sImageBridgeChildThread = new ImageBridgeThread();
   if (!sImageBridgeChildThread->IsRunning()) {
     sImageBridgeChildThread->Start();
   }
 
-  sImageBridgeChildSingleton = new ImageBridgeChild();
+  RefPtr<ImageBridgeChild> child = new ImageBridgeChild();
   RefPtr<ImageBridgeParent> parent = ImageBridgeParent::CreateSameProcess();
 
-  sImageBridgeChildSingleton->ConnectAsync(parent);
-  sImageBridgeChildSingleton->GetMessageLoop()->PostTask(
-    NewRunnableFunction(CallSendImageBridgeThreadId,
-                        sImageBridgeChildSingleton.get()));
+  RefPtr<Runnable> runnable = WrapRunnable(
+    child,
+    &ImageBridgeChild::BindSameProcess,
+    parent);
+  child->GetMessageLoop()->PostTask(runnable.forget());
+
+  // Assign this after so other threads can't post messages before we connect to IPDL.
+  {
+    StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+    sImageBridgeChildSingleton = child;
+  }
 }
 
 /* static */ void
 ImageBridgeChild::InitWithGPUProcess(Endpoint<PImageBridgeChild>&& aEndpoint)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!sImageBridgeChildSingleton);
   MOZ_ASSERT(!sImageBridgeChildThread);
 
   sImageBridgeChildThread = new ImageBridgeThread();
   if (!sImageBridgeChildThread->IsRunning()) {
     sImageBridgeChildThread->Start();
   }
 
-  sImageBridgeChildSingleton = new ImageBridgeChild();
+  RefPtr<ImageBridgeChild> child = new ImageBridgeChild();
+
+  MessageLoop* loop = child->GetMessageLoop();
+  loop->PostTask(NewRunnableMethod<Endpoint<PImageBridgeChild>&&>(
+    child, &ImageBridgeChild::Bind, Move(aEndpoint)));
 
-  MessageLoop* loop = sImageBridgeChildSingleton->GetMessageLoop();
-  loop->PostTask(NewRunnableMethod<Endpoint<PImageBridgeChild>&&>(
-    sImageBridgeChildSingleton, &ImageBridgeChild::Bind, Move(aEndpoint)));
+  // Assign this after so other threads can't post messages before we connect to IPDL.
+  {
+    StaticMutexAutoLock lock(sImageBridgeSingletonLock);
+    sImageBridgeChildSingleton = child;
+  }
 }
 
 bool InImageBridgeChildThread()
 {
-  return ImageBridgeChild::IsCreated() &&
+  return sImageBridgeChildThread &&
     sImageBridgeChildThread->thread_id() == PlatformThread::CurrentId();
 }
 
 MessageLoop * ImageBridgeChild::GetMessageLoop() const
 {
   return sImageBridgeChildThread ? sImageBridgeChildThread->message_loop() : nullptr;
 }
 
-void ImageBridgeChild::ConnectAsync(ImageBridgeParent* aParent)
-{
-  GetMessageLoop()->PostTask(NewRunnableFunction(&ConnectImageBridge,
-                                                 this, aParent));
-}
-
-void
+/* static */ void
 ImageBridgeChild::IdentifyCompositorTextureHost(const TextureFactoryIdentifier& aIdentifier)
 {
-  if (sImageBridgeChildSingleton) {
-    sImageBridgeChildSingleton->IdentifyTextureHost(aIdentifier);
+  if (RefPtr<ImageBridgeChild> child = GetSingleton()) {
+    child->IdentifyTextureHost(aIdentifier);
   }
 }
 
 RefPtr<ImageClient>
 ImageBridgeChild::CreateImageClient(CompositableType aType,
                                     ImageContainer* aImageContainer,
                                     ImageContainerChild* aContainerChild)
 {
   if (InImageBridgeChildThread()) {
     return CreateImageClientNow(aType, aImageContainer, aContainerChild);
   }
 
   SynchronousTask task("CreateImageClient Lock");
 
   RefPtr<ImageClient> result = nullptr;
-  GetMessageLoop()->PostTask(
-      NewRunnableFunction(&CreateImageClientSync, &task, this, &result, aType,
-                          aImageContainer, aContainerChild));
+
+  RefPtr<Runnable> runnable = WrapRunnable(
+    RefPtr<ImageBridgeChild>(this),
+    &ImageBridgeChild::CreateImageClientSync,
+    &task,
+    &result,
+    aType,
+    aImageContainer,
+    aContainerChild);
+  GetMessageLoop()->PostTask(runnable.forget());
 
   task.Wait();
 
   return result;
 }
 
 RefPtr<ImageClient>
 ImageBridgeChild::CreateImageClientNow(CompositableType aType,
                                        ImageContainer* aImageContainer,
                                        ImageContainerChild* aContainerChild)
 {
-  MOZ_ASSERT(!mShuttingDown);
   MOZ_ASSERT(InImageBridgeChildThread());
 
   if (aImageContainer) {
     SendPImageContainerConstructor(aContainerChild);
     aContainerChild->RegisterWithIPDL();
   }
 
   RefPtr<ImageClient> client = ImageClient::CreateImageClient(aType, this, TextureFlags::NO_FLAGS);
@@ -1059,19 +1112,26 @@ ImageBridgeChild::CreateCanvasClient(Can
                                      TextureFlags aFlag)
 {
   if (InImageBridgeChildThread()) {
     return CreateCanvasClientNow(aType, aFlag);
   }
 
   SynchronousTask task("CreateCanvasClient Lock");
 
+  // RefPtrs on arguments are not needed since this dispatches synchronously.
   RefPtr<CanvasClient> result = nullptr;
-  GetMessageLoop()->PostTask(NewRunnableFunction(&CreateCanvasClientSync,
-                                 &task, aType, aFlag, &result));
+  RefPtr<Runnable> runnable = WrapRunnable(
+    RefPtr<ImageBridgeChild>(this),
+    &ImageBridgeChild::CreateCanvasClientSync,
+    &task,
+    aType,
+    aFlag,
+    &result);
+  GetMessageLoop()->PostTask(runnable.forget());
 
   task.Wait();
 
   return result.forget();
 }
 
 already_AddRefed<CanvasClient>
 ImageBridgeChild::CreateCanvasClientNow(CanvasClient::CanvasClientType aType,
@@ -1086,51 +1146,50 @@ ImageBridgeChild::CreateCanvasClientNow(
   return client.forget();
 }
 
 bool
 ImageBridgeChild::AllocUnsafeShmem(size_t aSize,
                                    ipc::SharedMemory::SharedMemoryType aType,
                                    ipc::Shmem* aShmem)
 {
-  MOZ_ASSERT(!mShuttingDown);
-  if (InImageBridgeChildThread()) {
-    return PImageBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem);
-  } else {
+  if (!InImageBridgeChildThread()) {
     return DispatchAllocShmemInternal(aSize, aType, aShmem, true); // true: unsafe
   }
+
+  MOZ_ASSERT(CanSend());
+  return PImageBridgeChild::AllocUnsafeShmem(aSize, aType, aShmem);
 }
 
 bool
 ImageBridgeChild::AllocShmem(size_t aSize,
                              ipc::SharedMemory::SharedMemoryType aType,
                              ipc::Shmem* aShmem)
 {
-  MOZ_ASSERT(!mShuttingDown);
-  if (InImageBridgeChildThread()) {
-    return PImageBridgeChild::AllocShmem(aSize, aType,
-                                         aShmem);
-  } else {
+  if (!InImageBridgeChildThread()) {
     return DispatchAllocShmemInternal(aSize, aType, aShmem, false); // false: unsafe
   }
+
+  MOZ_ASSERT(CanSend());
+  return PImageBridgeChild::AllocShmem(aSize, aType, aShmem);
 }
 
 // NewRunnableFunction accepts a limited number of parameters so we need a
 // struct here
 struct AllocShmemParams {
   RefPtr<ISurfaceAllocator> mAllocator;
   size_t mSize;
   ipc::SharedMemory::SharedMemoryType mType;
   ipc::Shmem* mShmem;
   bool mUnsafe;
   bool mSuccess;
 };
 
-static void
-ProxyAllocShmemNow(SynchronousTask* aTask, AllocShmemParams* aParams)
+void
+ImageBridgeChild::ProxyAllocShmemNow(SynchronousTask* aTask, AllocShmemParams* aParams)
 {
   AutoCompleteTask complete(aTask);
 
   MOZ_ASSERT(aParams);
 
   auto shmAllocator = aParams->mAllocator->AsShmemAllocator();
   if (aParams->mUnsafe) {
     aParams->mSuccess = shmAllocator->AllocUnsafeShmem(aParams->mSize,
@@ -1150,72 +1209,81 @@ ImageBridgeChild::DispatchAllocShmemInte
                                              bool aUnsafe)
 {
   SynchronousTask task("AllocatorProxy alloc");
 
   AllocShmemParams params = {
     this, aSize, aType, aShmem, aUnsafe, true
   };
 
-  GetMessageLoop()->PostTask(NewRunnableFunction(&ProxyAllocShmemNow,
-                                                 &task, &params));
+  RefPtr<Runnable> runnable = WrapRunnable(
+    RefPtr<ImageBridgeChild>(this),
+    &ImageBridgeChild::ProxyAllocShmemNow,
+    &task,
+    &params);
+  GetMessageLoop()->PostTask(runnable.forget());
 
   task.Wait();
 
   return params.mSuccess;
 }
 
-static void ProxyDeallocShmemNow(SynchronousTask* aTask,
-                                 ISurfaceAllocator* aAllocator,
-                                 ipc::Shmem* aShmem)
+void
+ImageBridgeChild::ProxyDeallocShmemNow(SynchronousTask* aTask,
+                                       ISurfaceAllocator* aAllocator,
+                                       ipc::Shmem* aShmem)
 {
   AutoCompleteTask complete(aTask);
 
   MOZ_ASSERT(aShmem);
 
   aAllocator->AsShmemAllocator()->DeallocShmem(*aShmem);
 }
 
 void
 ImageBridgeChild::DeallocShmem(ipc::Shmem& aShmem)
 {
   if (InImageBridgeChildThread()) {
     PImageBridgeChild::DeallocShmem(aShmem);
-  } else {
-    SynchronousTask task("AllocatorProxy Dealloc");
+    return;
+  }
+
+  SynchronousTask task("AllocatorProxy Dealloc");
 
-    GetMessageLoop()->PostTask(NewRunnableFunction(&ProxyDeallocShmemNow,
-                                                   &task,
-                                                   this,
-                                                   &aShmem));
+  RefPtr<Runnable> runnable = WrapRunnable(
+    RefPtr<ImageBridgeChild>(this),
+    &ImageBridgeChild::ProxyDeallocShmemNow,
+    &task,
+    this,
+    &aShmem);
+  GetMessageLoop()->PostTask(runnable.forget());
 
-    task.Wait();
-  }
+  task.Wait();
 }
 
 PTextureChild*
 ImageBridgeChild::AllocPTextureChild(const SurfaceDescriptor&,
                                      const LayersBackend&,
                                      const TextureFlags&,
                                      const uint64_t& aSerial)
 {
-  MOZ_ASSERT(!mShuttingDown);
+  MOZ_ASSERT(CanSend());
   return TextureClient::CreateIPDLActor();
 }
 
 bool
 ImageBridgeChild::DeallocPTextureChild(PTextureChild* actor)
 {
   return TextureClient::DestroyIPDLActor(actor);
 }
 
 PMediaSystemResourceManagerChild*
 ImageBridgeChild::AllocPMediaSystemResourceManagerChild()
 {
-  MOZ_ASSERT(!mShuttingDown);
+  MOZ_ASSERT(CanSend());
   return new mozilla::media::MediaSystemResourceManagerChild();
 }
 
 bool
 ImageBridgeChild::DeallocPMediaSystemResourceManagerChild(PMediaSystemResourceManagerChild* aActor)
 {
   MOZ_ASSERT(aActor);
   delete static_cast<mozilla::media::MediaSystemResourceManagerChild*>(aActor);
@@ -1305,17 +1373,17 @@ ImageBridgeChild::RecvDidComposite(Infal
 }
 
 PTextureChild*
 ImageBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData,
                                 LayersBackend aLayersBackend,
                                 TextureFlags aFlags,
                                 uint64_t aSerial)
 {
-  MOZ_ASSERT(!mShuttingDown);
+  MOZ_ASSERT(CanSend());
   return SendPTextureConstructor(aSharedData, aLayersBackend, aFlags, aSerial);
 }
 
 static bool
 IBCAddOpDestroy(CompositableTransaction* aTxn, const OpDestroy& op, bool synchronously)
 {
   if (aTxn->Finished()) {
     return false;
@@ -1342,17 +1410,17 @@ ImageBridgeChild::DestroyInTransaction(P
   return IBCAddOpDestroy(mTxn, OpDestroy(aCompositable), synchronously);
 }
 
 
 void
 ImageBridgeChild::RemoveTextureFromCompositable(CompositableClient* aCompositable,
                                                 TextureClient* aTexture)
 {
-  MOZ_ASSERT(!mShuttingDown);
+  MOZ_ASSERT(CanSend());
   MOZ_ASSERT(aTexture);
   MOZ_ASSERT(aTexture->IsSharedWithCompositor());
   MOZ_ASSERT(aCompositable->IsConnected());
   if (!aTexture || !aTexture->IsSharedWithCompositor() || !aCompositable->IsConnected()) {
     return;
   }
 
   CompositableOperation op(
@@ -1366,17 +1434,17 @@ ImageBridgeChild::RemoveTextureFromCompo
   }
 }
 
 void
 ImageBridgeChild::RemoveTextureFromCompositableAsync(AsyncTransactionTracker* aAsyncTransactionTracker,
                                                      CompositableClient* aCompositable,
                                                      TextureClient* aTexture)
 {
-  MOZ_ASSERT(!mShuttingDown);
+  MOZ_ASSERT(CanSend());
   MOZ_ASSERT(aTexture);
   MOZ_ASSERT(aTexture->IsSharedWithCompositor());
   MOZ_ASSERT(aCompositable->IsConnected());
   if (!aTexture || !aTexture->IsSharedWithCompositor() || !aCompositable->IsConnected()) {
     return;
   }
 
   CompositableOperation op(
@@ -1392,30 +1460,66 @@ ImageBridgeChild::RemoveTextureFromCompo
   mTrackersHolder.HoldUntilComplete(aAsyncTransactionTracker);
 }
 
 bool ImageBridgeChild::IsSameProcess() const
 {
   return OtherPid() == base::GetCurrentProcId();
 }
 
-static void
-DestroyCompositableNow(RefPtr<ImageBridgeChild> aImageBridge,
-                       RefPtr<CompositableChild> aCompositable)
-{
-  aImageBridge->Destroy(aCompositable);
-}
-
 void
 ImageBridgeChild::Destroy(CompositableChild* aCompositable)
 {
   if (!InImageBridgeChildThread()) {
-    RefPtr<ImageBridgeChild> self = this;
-    RefPtr<CompositableChild> compositable = aCompositable;
-    GetMessageLoop()->PostTask(
-      NewRunnableFunction(&DestroyCompositableNow, self, compositable));
+    RefPtr<Runnable> runnable = WrapRunnable(
+      RefPtr<ImageBridgeChild>(this),
+      &ImageBridgeChild::Destroy,
+      RefPtr<CompositableChild>(aCompositable));
+    GetMessageLoop()->PostTask(runnable.forget());
     return;
   }
   CompositableForwarder::Destroy(aCompositable);
 }
 
+bool
+ImageBridgeChild::CanSend() const
+{
+  MOZ_ASSERT(InImageBridgeChildThread());
+  return mCanSend;
+}
+
+void
+ImageBridgeChild::OnXPCOMShutdown()
+{
+  // This uses nsIObserverService, so it must be cleaned up. Other threads may
+  // hold references to ImageBridgeChild and we may actually be destroyed well
+  // after XPCOM shutdown.
+  mActiveResourceTracker = nullptr;
+}
+
+NS_IMPL_ISUPPORTS(ImageBridgeChild::ShutdownObserver, nsIObserver);
+
+ImageBridgeChild::ShutdownObserver::ShutdownObserver(ImageBridgeChild* aImageBridge)
+ : mImageBridge(aImageBridge)
+{
+  nsContentUtils::RegisterShutdownObserver(this);
+}
+
+void
+ImageBridgeChild::ShutdownObserver::Unregister()
+{
+  nsContentUtils::UnregisterShutdownObserver(this);
+  mImageBridge = nullptr;
+}
+
+NS_IMETHODIMP
+ImageBridgeChild::ShutdownObserver::Observe(nsISupports* aSubject,
+                                            const char* aTopic,
+                                            const char16_t* aData)
+{
+  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+    mImageBridge->OnXPCOMShutdown();
+  }
+  return NS_OK;
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ipc/ImageBridgeChild.h
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -14,16 +14,17 @@
 #include "mozilla/ipc/SharedMemory.h"   // for SharedMemory, etc
 #include "mozilla/layers/AsyncTransactionTracker.h" // for AsyncTransactionTrackerHolder
 #include "mozilla/layers/CanvasClient.h"
 #include "mozilla/layers/CompositableForwarder.h"
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/PImageBridgeChild.h"
 #include "mozilla/Mutex.h"
 #include "nsDebug.h"                    // for NS_RUNTIMEABORT
+#include "nsIObserver.h"
 #include "nsRegion.h"                   // for nsIntRegion
 #include "mozilla/gfx/Rect.h"
 
 class MessageLoop;
 
 namespace base {
 class Thread;
 } // namespace base
@@ -40,16 +41,18 @@ class AsyncTransactionTracker;
 class ImageClient;
 class ImageContainer;
 class ImageContainerChild;
 class ImageBridgeParent;
 class CompositableClient;
 struct CompositableTransaction;
 class Image;
 class TextureClient;
+class SynchronousTask;
+struct AllocShmemParams;
 
 /**
  * Returns true if the current thread is the ImageBrdigeChild's thread.
  *
  * Can be called from any thread.
  */
 bool InImageBridgeChildThread();
 
@@ -104,16 +107,17 @@ bool InImageBridgeChildThread();
  * not used at all (except for the very first transaction that provides the
  * CompositableHost with an AsyncID).
  */
 class ImageBridgeChild final : public PImageBridgeChild
                              , public CompositableForwarder
                              , public ShmemAllocator
 {
   friend class ImageContainer;
+
   typedef InfallibleTArray<AsyncParentMessageData> AsyncParentMessageArray;
 public:
 
   virtual ShmemAllocator* AsShmemAllocator() override { return this; }
 
   /**
    * Creates the image bridge with a dedicated thread for ImageBridgeChild.
    *
@@ -130,46 +134,22 @@ public:
    * ImageBridge's thread.
    *
    * If you don't want to destroy the thread, call DestroyBridge directly
    * instead.
    */
   static void ShutDown();
 
   /**
-   * Returns true if the singleton has been created.
-   *
-   * Can be called from any thread.
-   */
-  static bool IsCreated();
-  /**
-   * Returns true if the singleton's ShutDown() was called.
-   *
-   * Can be called from any thread.
-   */
-  static bool IsShutDown()
-  {
-    return sIsShutDown;
-  }
-
-  /**
    * returns the singleton instance.
    *
    * can be called from any thread.
    */
-  static ImageBridgeChild* GetSingleton();
-
+  static RefPtr<ImageBridgeChild> GetSingleton();
 
-  /**
-   * Dispatches a task to the ImageBridgeChild thread to do the connection
-   */
-  void ConnectAsync(ImageBridgeParent* aParent);
-
-  using PImageBridgeChild::SendImageBridgeThreadId;
-  void SendImageBridgeThreadId();
 
   static void IdentifyCompositorTextureHost(const TextureFactoryIdentifier& aIdentifier);
 
   void BeginTransaction();
   void EndTransaction();
 
   /**
    * Returns the ImageBridgeChild's thread.
@@ -228,31 +208,64 @@ public:
   // Create an ImageClient from the ImageBridge thread.
   RefPtr<ImageClient> CreateImageClientNow(
     CompositableType aType,
     ImageContainer* aImageContainer,
     ImageContainerChild* aContainerChild);
 
   already_AddRefed<CanvasClient> CreateCanvasClient(CanvasClient::CanvasClientType aType,
                                                     TextureFlags aFlag);
-  already_AddRefed<CanvasClient> CreateCanvasClientNow(CanvasClient::CanvasClientType aType,
-                                                       TextureFlags aFlag);
-
-  static void DispatchReleaseImageContainer(ImageContainerChild* aChild);
+  void ReleaseImageContainer(RefPtr<ImageContainerChild> aChild);
+  void UpdateAsyncCanvasRenderer(AsyncCanvasRenderer* aClient);
+  void UpdateImageClient(RefPtr<ImageClient> aClient, RefPtr<ImageContainer> aContainer);
   static void DispatchReleaseTextureClient(TextureClient* aClient);
-  static void DispatchImageClientUpdate(ImageClient* aClient, ImageContainer* aContainer);
-
-  static void UpdateAsyncCanvasRenderer(AsyncCanvasRenderer* aClient);
-  static void UpdateAsyncCanvasRendererNow(AsyncCanvasRenderer* aClient);
 
   /**
    * Flush all Images sent to CompositableHost.
    */
-  static void FlushAllImages(ImageClient* aClient, ImageContainer* aContainer);
+  void FlushAllImages(ImageClient* aClient, ImageContainer* aContainer);
+
+private:
+  // Helpers for dispatching.
+  already_AddRefed<CanvasClient> CreateCanvasClientNow(
+    CanvasClient::CanvasClientType aType,
+    TextureFlags aFlags);
+  void CreateCanvasClientSync(
+    SynchronousTask* aTask,
+    CanvasClient::CanvasClientType aType,
+    TextureFlags aFlags,
+    RefPtr<CanvasClient>* const outResult);
+
+  void CreateImageClientSync(
+    SynchronousTask* aTask,
+    RefPtr<ImageClient>* result,
+    CompositableType aType,
+    ImageContainer* aImageContainer,
+    ImageContainerChild* aContainerChild);
 
+  void ReleaseTextureClientNow(TextureClient* aClient);
+
+  void UpdateAsyncCanvasRendererNow(AsyncCanvasRenderer* aClient);
+  void UpdateAsyncCanvasRendererSync(
+    SynchronousTask* aTask,
+    AsyncCanvasRenderer* aWrapper);
+
+  void FlushAllImagesSync(
+    SynchronousTask* aTask,
+    ImageClient* aClient,
+    ImageContainer* aContainer,
+    RefPtr<AsyncTransactionWaiter> aWaiter);
+
+  void ProxyAllocShmemNow(SynchronousTask* aTask, AllocShmemParams* aParams);
+  void ProxyDeallocShmemNow(
+    SynchronousTask* aTask,
+    ISurfaceAllocator* aAllocator,
+    Shmem* aShmem);
+
+public:
   // CompositableForwarder
 
   virtual void Connect(CompositableClient* aCompositable,
                        ImageContainer* aImageContainer) override;
 
   virtual bool UsesImageBridge() const override { return true; }
 
   /**
@@ -349,34 +362,63 @@ public:
 
   virtual void UpdateFwdTransactionId() override { ++mFwdTransactionId; }
   virtual uint64_t GetFwdTransactionId() override { return mFwdTransactionId; }
 
   bool InForwarderThread() override {
     return InImageBridgeChildThread();
   }
 
-
-  void MarkShutDown();
-
-  void FallbackDestroyActors();
-
 protected:
   ImageBridgeChild();
   bool DispatchAllocShmemInternal(size_t aSize,
                                   SharedMemory::SharedMemoryType aType,
                                   Shmem* aShmem,
                                   bool aUnsafe);
 
   void Bind(Endpoint<PImageBridgeChild>&& aEndpoint);
+  void BindSameProcess(RefPtr<ImageBridgeParent> aParent);
+
+  void SendImageBridgeThreadId();
+
+  void WillShutdown();
+  void ShutdownStep1(SynchronousTask* aTask);
+  void ShutdownStep2(SynchronousTask* aTask);
+  void MarkShutDown();
+  void FallbackDestroyActors();
+
+  void ActorDestroy(ActorDestroyReason aWhy) override;
+  void DeallocPImageBridgeChild() override;
+
+  bool CanSend() const;
+
+private:
+  class ShutdownObserver final : public nsIObserver
+  {
+  public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIOBSERVER
+
+    explicit ShutdownObserver(ImageBridgeChild* aImageBridge);
+    void Unregister();
+
+  private:
+    ~ShutdownObserver() {};
+
+    ImageBridgeChild* mImageBridge;
+  };
+  friend class ShutdownObserver;
+
+  void OnXPCOMShutdown();
 
 private:
   CompositableTransaction* mTxn;
-  Atomic<bool> mShuttingDown;
-  static Atomic<bool> sIsShutDown;
+
+  bool mCanSend;
+  bool mCalledClose;
 
   /**
    * Transaction id of CompositableForwarder.
    * It is incrementaed by UpdateFwdTransactionId() in each BeginTransaction() call.
    */
   uint64_t mFwdTransactionId;
 
   /**
@@ -386,14 +428,16 @@ private:
   nsDataHashtable<nsUint64HashKey, RefPtr<TextureClient> > mTexturesWaitingRecycled;
 
   AsyncTransactionTrackersHolder mTrackersHolder;
 
 #ifdef MOZ_WIDGET_GONK
   Mutex mWaitingFenceHandleMutex;
   nsDataHashtable<nsUint64HashKey, RefPtr<TextureClient> > mTexturesWaitingFenceHandle;
 #endif
+
+  RefPtr<ShutdownObserver> mShutdownObserver;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -1669,16 +1669,17 @@ dateTimeFormat_toSource(JSContext* cx, u
 
 static const JSFunctionSpec dateTimeFormat_static_methods[] = {
     JS_SELF_HOSTED_FN("supportedLocalesOf", "Intl_DateTimeFormat_supportedLocalesOf", 1, 0),
     JS_FS_END
 };
 
 static const JSFunctionSpec dateTimeFormat_methods[] = {
     JS_SELF_HOSTED_FN("resolvedOptions", "Intl_DateTimeFormat_resolvedOptions", 0, 0),
+    JS_SELF_HOSTED_FN("formatToParts", "Intl_DateTimeFormat_formatToParts", 0, 0),
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str, dateTimeFormat_toSource, 0, 0),
 #endif
     JS_FS_END
 };
 
 /**
  * DateTimeFormat constructor.
@@ -1806,33 +1807,16 @@ CreateDateTimeFormatPrototype(JSContext*
     }
     if (!DefineProperty(cx, proto, cx->names().format, UndefinedHandleValue,
                         JS_DATA_TO_FUNC_PTR(JSGetterOp, &getter.toObject()),
                         nullptr, JSPROP_GETTER | JSPROP_SHARED))
     {
         return nullptr;
     }
 
-    // If the still-experimental DateTimeFormat.prototype.formatToParts method
-    // is enabled, also add it.
-    if (cx->compartment()->creationOptions().experimentalDateTimeFormatFormatToPartsEnabled()) {
-        RootedValue ftp(cx);
-        HandlePropertyName name = cx->names().formatToParts;
-        if (!GlobalObject::getSelfHostedFunction(cx, cx->global(),
-                    cx->names().DateTimeFormatFormatToParts,
-                    name,
-                    0, &ftp))
-        {
-            return nullptr;
-        }
-
-        if (!DefineProperty(cx, proto, cx->names().formatToParts, ftp, nullptr, nullptr, 0))
-            return nullptr;
-    }
-
     RootedValue options(cx);
     if (!CreateDefaultOptions(cx, &options))
         return nullptr;
 
     // 12.2.1 and 12.3
     if (!IntlInitialize(cx, proto, cx->names().InitializeDateTimeFormat, UndefinedHandleValue,
                         options))
     {
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -12031,18 +12031,20 @@ IonBuilder::loadUnboxedValue(MDefinition
         break;
 
       case JSVAL_TYPE_STRING:
         load = MLoadUnboxedString::New(alloc(), elements, index, elementsOffset);
         break;
 
       case JSVAL_TYPE_OBJECT: {
         MLoadUnboxedObjectOrNull::NullBehavior nullBehavior;
-        if (types->hasType(TypeSet::NullType()) || barrier != BarrierKind::NoBarrier)
+        if (types->hasType(TypeSet::NullType()))
             nullBehavior = MLoadUnboxedObjectOrNull::HandleNull;
+        else if (barrier != BarrierKind::NoBarrier)
+            nullBehavior = MLoadUnboxedObjectOrNull::BailOnNull;
         else
             nullBehavior = MLoadUnboxedObjectOrNull::NullNotPossible;
         load = MLoadUnboxedObjectOrNull::New(alloc(), elements, index, nullBehavior,
                                              elementsOffset);
         break;
       }
 
       default:
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2140,17 +2140,16 @@ class JS_PUBLIC_API(CompartmentCreationO
   public:
     CompartmentCreationOptions()
       : addonId_(nullptr),
         traceGlobal_(nullptr),
         invisibleToDebugger_(false),
         mergeable_(false),
         preserveJitCode_(false),
         cloneSingletons_(false),
-        experimentalDateTimeFormatFormatToPartsEnabled_(false),
         sharedMemoryAndAtomics_(false),
         secureContext_(false)
     {
         zone_.spec = JS::FreshZone;
     }
 
     // A null add-on ID means that the compartment is not associated with an
     // add-on.
@@ -2205,33 +2204,16 @@ class JS_PUBLIC_API(CompartmentCreationO
     }
 
     bool cloneSingletons() const { return cloneSingletons_; }
     CompartmentCreationOptions& setCloneSingletons(bool flag) {
         cloneSingletons_ = flag;
         return *this;
     }
 
-    // ECMA-402 is considering adding a "formatToParts" DateTimeFormat method,
-    // that exposes not just a formatted string but its ordered subcomponents.
-    // The method, its semantics, and its name are all well short of being
-    // finalized, so for now it's exposed *only* if requested.
-    //
-    // Until "formatToParts" is included in a final specification edition, it's
-    // subject to change or removal at any time.  Do *not* rely on it in
-    // mission-critical code that can't be changed if ECMA-402 decides not to
-    // accept the method in its current form.
-    bool experimentalDateTimeFormatFormatToPartsEnabled() const {
-        return experimentalDateTimeFormatFormatToPartsEnabled_;
-    }
-    CompartmentCreationOptions& setExperimentalDateTimeFormatFormatToPartsEnabled(bool flag) {
-        experimentalDateTimeFormatFormatToPartsEnabled_ = flag;
-        return *this;
-    }
-
     bool getSharedMemoryAndAtomicsEnabled() const;
     CompartmentCreationOptions& setSharedMemoryAndAtomicsEnabled(bool flag);
 
     // This flag doesn't affect JS engine behavior.  It is used by Gecko to
     // mark whether content windows and workers are "Secure Context"s. See
     // https://w3c.github.io/webappsec-secure-contexts/
     // https://bugzilla.mozilla.org/show_bug.cgi?id=1162772#c34
     bool secureContext() const { return secureContext_; }
@@ -2246,17 +2228,16 @@ class JS_PUBLIC_API(CompartmentCreationO
     union {
         ZoneSpecifier spec;
         void* pointer; // js::Zone* is not exposed in the API.
     } zone_;
     bool invisibleToDebugger_;
     bool mergeable_;
     bool preserveJitCode_;
     bool cloneSingletons_;
-    bool experimentalDateTimeFormatFormatToPartsEnabled_;
     bool sharedMemoryAndAtomics_;
     bool secureContext_;
 };
 
 /**
  * CompartmentBehaviors specifies behaviors of a compartment that can be
  * changed after the compartment's been created.
  */
--- a/js/src/jsautokw.py
+++ b/js/src/jsautokw.py
@@ -1,22 +1,213 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-from __future__ import print_function
+import re
+import sys
+
+def read_keyword_list(filename):
+    macro_pat = re.compile(r"^\s*macro\(([^,]+), *[^,]+, *[^\)]+\)\s*\\?$")
+
+    keyword_list = []
+    index = 0
+    with open(filename, 'r') as f:
+        for line in f:
+            m = macro_pat.search(line)
+            if m:
+                keyword_list.append((index, m.group(1)))
+                index += 1
+
+    assert(len(keyword_list) != 0)
+
+    return keyword_list
+
+def line(opt, s):
+    opt['output'].write('{}{}\n'.format('    ' * opt['indent_level'], s))
+
+def indent(opt):
+    opt['indent_level'] += 1
+
+def dedent(opt):
+    opt['indent_level'] -= 1
+
+def span_and_count_at(keyword_list, column):
+    assert(len(keyword_list) != 0);
+
+    chars_dict = {}
+    for index, keyword in keyword_list:
+        chars_dict[ord(keyword[column])] = True
+
+    chars = sorted(chars_dict.keys())
+    return chars[-1] - chars[0] + 1, len(chars)
+
+def optimal_switch_column(opt, keyword_list, columns, unprocessed_columns):
+    assert(len(keyword_list) != 0);
+    assert(unprocessed_columns != 0);
+
+    min_count = 0
+    min_span = 0
+    min_count_index = 0
+    min_span_index = 0
+
+    for index in range(0, unprocessed_columns):
+        span, count = span_and_count_at(keyword_list, columns[index])
+        assert(span != 0)
 
-import os
-import sys
-import subprocess
+        if span == 1:
+            assert(count == 1)
+            return 1, True
+
+        assert(count != 1)
+        if index == 0 or min_span > span:
+            min_span = span
+            min_span_index = index
+
+        if index == 0 or min_count > count:
+            min_count = count
+            min_count_index = index
+
+    if min_count <= opt['use_if_threshold']:
+        return min_count_index, True
+
+    return min_span_index, False
+
+def split_list_per_column(keyword_list, column):
+    assert(len(keyword_list) != 0);
+
+    column_dict = {}
+    for item in keyword_list:
+        index, keyword = item
+        per_column = column_dict.setdefault(keyword[column], [])
+        per_column.append(item)
+
+    return sorted(column_dict.items(), key=lambda (char, keyword): ord(char))
+
+def generate_letter_switch(opt, unprocessed_columns, keyword_list,
+                           columns=None):
+    assert(len(keyword_list) != 0);
+
+    if not columns:
+        columns = range(0, unprocessed_columns)
+
+    if len(keyword_list) == 1:
+        index, keyword = keyword_list[0]
+
+        if unprocessed_columns == 0:
+            line(opt, 'JSKW_GOT_MATCH({}) /* {} */'.format(index, keyword))
+            return
+
+        if unprocessed_columns > opt['char_tail_test_threshold']:
+            line(opt, 'JSKW_TEST_GUESS({}) /* {} */'.format(index, keyword))
+            return
+
+        conds = []
+        for column in columns[0:unprocessed_columns]:
+            quoted = repr(keyword[column])
+            conds.append('JSKW_AT({})=={}'.format(column, quoted))
 
-def main(output, exe):
-    # moz.build passes in the exe name without any path, so to run it we need to
-    # prepend the './'
-    run_exe = exe if os.path.isabs(exe) else './%s' % exe
+        line(opt, 'if ({}) {{'.format(' && '.join(conds)))
+
+        indent(opt)
+        line(opt, 'JSKW_GOT_MATCH({}) /* {} */'.format(index, keyword))
+        dedent(opt)
+
+        line(opt, '}')
+        line(opt, 'JSKW_NO_MATCH()')
+        return
+
+    assert(unprocessed_columns != 0);
+
+    optimal_column_index, use_if = optimal_switch_column(opt, keyword_list,
+                                                         columns,
+                                                         unprocessed_columns)
+    optimal_column = columns[optimal_column_index]
+
+    # Make a copy to avoid breaking passed list.
+    columns = columns[:]
+    columns[optimal_column_index] = columns[unprocessed_columns - 1]
+
+    list_per_column = split_list_per_column(keyword_list, optimal_column)
+
+    if not use_if:
+        line(opt, 'switch (JSKW_AT({})) {{'.format(optimal_column))
+
+    for char, keyword_list_per_column in list_per_column:
+        quoted = repr(char)
+        if use_if:
+            line(opt, 'if (JSKW_AT({}) == {}) {{'.format(optimal_column,
+                                                         quoted))
+        else:
+            line(opt, '  case {}:'.format(quoted))
+
+        indent(opt)
+        generate_letter_switch(opt, unprocessed_columns - 1,
+                               keyword_list_per_column, columns)
+        dedent(opt)
+
+        if use_if:
+            line(opt, '}')
+
+    if not use_if:
+        line(opt, '}')
+
+    line(opt, 'JSKW_NO_MATCH()')
+
+def split_list_per_length(keyword_list):
+    assert(len(keyword_list) != 0);
 
-    # Use universal_newlines so everything is '\n', which gets converted to
-    # '\r\n' when writing out the file in Windows.
-    data = subprocess.check_output([run_exe], universal_newlines=True)
-    output.write(data)
+    length_dict = {}
+    for item in keyword_list:
+        index, keyword = item
+        per_length = length_dict.setdefault(len(keyword), [])
+        per_length.append(item)
+
+    return sorted(length_dict.items(), key=lambda (length, keyword): length)
+
+def generate_switch(opt, keyword_list):
+    assert(len(keyword_list) != 0);
+
+    line(opt, '/*')
+    line(opt, ' * Generating switch for the list of {} entries:'.format(len(keyword_list)))
+    for index, keyword in keyword_list:
+        line(opt, ' * {}'.format(keyword))
+    line(opt, ' */')
+
+    list_per_length = split_list_per_length(keyword_list)
+
+    use_if = False
+    if len(list_per_length) < opt['use_if_threshold']:
+        use_if = True
+
+    if not use_if:
+        line(opt, 'switch (JSKW_LENGTH()) {')
+
+    for length, keyword_list_per_length in list_per_length:
+        if use_if:
+            line(opt, 'if (JSKW_LENGTH() == {}) {{'.format(length))
+        else:
+            line(opt, '  case {}:'.format(length))
+
+        indent(opt)
+        generate_letter_switch(opt, length, keyword_list_per_length)
+        dedent(opt)
+
+        if use_if:
+            line(opt, '}')
+
+    if not use_if:
+        line(opt, '}')
+    line(opt, 'JSKW_NO_MATCH()')
+
+def main(output, keywords_h):
+    keyword_list = read_keyword_list(keywords_h)
+
+    opt = {
+        'indent_level': 1,
+        'use_if_threshold': 3,
+        'char_tail_test_threshold': 4,
+        'output': output
+    }
+    generate_switch(opt, keyword_list)
 
 if __name__ == '__main__':
     main(sys.stdout, *sys.argv[1:])
deleted file mode 100644
--- a/js/src/jskwgen.cpp
+++ /dev/null
@@ -1,425 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * 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 <assert.h>
-#include <ctype.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "vm/Keywords.h"
-
-static const char * const keyword_list[] = {
-#define KEYWORD_STRING(keyword, name, type) #keyword,
-    FOR_EACH_JAVASCRIPT_KEYWORD(KEYWORD_STRING)
-#undef KEYWORD_STRING
-};
-
-struct gen_opt {
-    FILE* output;                       /* output file for generated source */
-    unsigned use_if_threshold;          /* max number of choices to generate
-                                           "if" selector instead of "switch" */
-    unsigned char_tail_test_threshold;  /* max number of unprocessed columns
-                                           to use inlined char compare
-                                           for remaining chars and not generic
-                                           string compare code */
-    unsigned indent_level;              /* current source identation level */
-};
-
-static unsigned column_to_compare;
-
-static int
-length_comparator(const void* a, const void* b)
-{
-    const char* str1 = keyword_list[*(unsigned*)a];
-    const char* str2 = keyword_list[*(unsigned*)b];
-    return (int)strlen(str1) - (int)strlen(str2);
-}
-
-static int
-column_comparator(const void* a, const void* b)
-{
-    const char* str1 = keyword_list[*(unsigned*)a];
-    const char* str2 = keyword_list[*(unsigned*)b];
-    return (int)str1[column_to_compare] - (int)str2[column_to_compare];
-}
-
-static unsigned
-count_different_lengths(unsigned indexes[], unsigned nelem)
-{
-    unsigned nlength, current_length, i, l;
-
-    current_length = 0;
-    nlength = 0;
-    for (i = 0; i != nelem; ++i) {
-        l = (unsigned)strlen(keyword_list[indexes[i]]);
-        assert(l != 0);
-        if (current_length != l) {
-            ++nlength;
-            current_length = l;
-        }
-    }
-    return nlength;
-}
-
-static void
-find_char_span_and_count(unsigned indexes[], unsigned nelem, unsigned column,
-                         unsigned* span_result, unsigned* count_result)
-{
-    unsigned i, count;
-    unsigned char c, prev, minc, maxc;
-
-    assert(nelem != 0);
-    minc = maxc = prev = (unsigned char)keyword_list[indexes[0]][column];
-    count = 1;
-    for (i = 1; i != nelem; ++i) {
-        c = (unsigned char)keyword_list[indexes[i]][column];
-        if (prev != c) {
-            prev = c;
-            ++count;
-            if (minc > c) {
-                minc = c;
-            } else if (maxc < c) {
-                maxc = c;
-            }
-        }
-    }
-
-    *span_result = maxc - minc + 1;
-    *count_result = count;
-}
-
-static unsigned
-find_optimal_switch_column(struct gen_opt* opt,
-                           unsigned indexes[], unsigned nelem,
-                           unsigned columns[], unsigned unprocessed_columns,
-                           int* use_if_result)
-{
-    unsigned i;
-    unsigned span, min_span, min_span_index;
-    unsigned nchar, min_nchar, min_nchar_index;
-
-    assert(unprocessed_columns != 0);
-    i = 0;
-    min_nchar = min_span = (unsigned)-1;
-    min_nchar_index = min_span_index = 0;
-    do {
-        column_to_compare = columns[i];
-        qsort(indexes, nelem, sizeof(indexes[0]), column_comparator);
-        find_char_span_and_count(indexes, nelem, column_to_compare,
-                                 &span, &nchar);
-        assert(span != 0);
-        if (span == 1) {
-            assert(nchar == 1);
-            *use_if_result = 1;
-            return 1;
-        }
-        assert(nchar != 1);
-        if (min_span > span) {
-            min_span = span;
-            min_span_index = i;
-        }
-        if (min_nchar > nchar) {
-            min_nchar = nchar;
-            min_nchar_index = i;
-        }
-    } while (++i != unprocessed_columns);
-
-    if (min_nchar <= opt->use_if_threshold) {
-        *use_if_result = 1;
-        i = min_nchar_index;
-    } else {
-        *use_if_result = 0;
-        i = min_span_index;
-    }
-
-    /*
-     * Restore order corresponding to i if it was destroyed by
-     * subsequent sort.
-     */
-    if (i != unprocessed_columns - 1) {
-        column_to_compare = columns[i];
-        qsort(indexes, nelem, sizeof(indexes[0]), column_comparator);
-    }
-
-    return i;
-}
-
-
-static void
-p(struct gen_opt* opt, const char* format, ...)
-{
-    va_list ap;
-
-    va_start(ap, format);
-    vfprintf(opt->output, format, ap);
-    va_end(ap);
-}
-
-/* Size for '\xxx' where xxx is octal escape */
-#define MIN_QUOTED_CHAR_BUFFER 7
-
-static char*
-qchar(char c, char* quoted_buffer)
-{
-    char* s;
-
-    s = quoted_buffer;
-    *s++ = '\'';
-    switch (c) {
-      case '\n': c = 'n'; goto one_char_escape;
-      case '\r': c = 'r'; goto one_char_escape;
-      case '\t': c = 't'; goto one_char_escape;
-      case '\f': c = 't'; goto one_char_escape;
-      case '\0': c = '0'; goto one_char_escape;
-      case '\'': goto one_char_escape;
-      one_char_escape:
-        *s++ = '\\';
-        break;
-      default:
-        if (!isprint(c)) {
-            *s++ = '\\';
-            *s++ = (char)('0' + (0x3 & (((unsigned char)c) >> 6)));
-            *s++ = (char)('0' + (0x7 & (((unsigned char)c) >> 3)));
-            c = (char)('0' + (0x7 & ((unsigned char)c)));
-        }
-    }
-    *s++ = c;
-    *s++ = '\'';
-    *s = '\0';
-    assert(s + 1 <= quoted_buffer + MIN_QUOTED_CHAR_BUFFER);
-    return quoted_buffer;
-}
-
-static void
-nl(struct gen_opt* opt)
-{
-    putc('\n', opt->output);
-}
-
-static void
-indent(struct gen_opt* opt)
-{
-    unsigned n = opt->indent_level;
-    while (n != 0) {
-        --n;
-        fputs("    ", opt->output);
-    }
-}
-
-static void
-line(struct gen_opt* opt, const char* format, ...)
-{
-    va_list ap;
-
-    indent(opt);
-    va_start(ap, format);
-    vfprintf(opt->output, format, ap);
-    va_end(ap);
-    nl(opt);
-}
-
-static void
-generate_letter_switch_r(struct gen_opt* opt,
-                         unsigned indexes[], unsigned nelem,
-                         unsigned columns[], unsigned unprocessed_columns)
-{
-    char qbuf[MIN_QUOTED_CHAR_BUFFER];
-
-    assert(nelem != 0);
-    if (nelem == 1) {
-        unsigned kw_index = indexes[0];
-        const char* keyword = keyword_list[kw_index];
-
-        if (unprocessed_columns == 0) {
-            line(opt, "JSKW_GOT_MATCH(%u) /* %s */", kw_index, keyword);
-        } else if (unprocessed_columns > opt->char_tail_test_threshold) {
-            line(opt, "JSKW_TEST_GUESS(%u) /* %s */", kw_index, keyword);
-        } else {
-            unsigned i, column;
-
-            indent(opt); p(opt, "if (");
-            for (i = 0; i != unprocessed_columns; ++i) {
-                column = columns[i];
-                qchar(keyword[column], qbuf);
-                p(opt, "%sJSKW_AT(%u)==%s", (i == 0) ? "" : " && ",
-                  column, qbuf);
-            }
-            p(opt, ") {"); nl(opt);
-            ++opt->indent_level;
-            line(opt, "JSKW_GOT_MATCH(%u) /* %s */", kw_index, keyword);
-            --opt->indent_level;
-            line(opt, "}");
-            line(opt, "JSKW_NO_MATCH()");
-        }
-    } else {
-        unsigned optimal_column_index, optimal_column;
-        unsigned i;
-        int use_if;
-        char current;
-
-        assert(unprocessed_columns != 0);
-        optimal_column_index = find_optimal_switch_column(opt, indexes, nelem,
-                                                          columns,
-                                                          unprocessed_columns,
-                                                          &use_if);
-        optimal_column = columns[optimal_column_index];
-        columns[optimal_column_index] = columns[unprocessed_columns - 1];
-
-        if (!use_if)
-            line(opt, "switch (JSKW_AT(%u)) {", optimal_column);
-
-        current = keyword_list[indexes[0]][optimal_column];
-        for (i = 0; i != nelem;) {
-            unsigned same_char_begin = i;
-            char next = current;
-
-            for (++i; i != nelem; ++i) {
-                next = keyword_list[indexes[i]][optimal_column];
-                if (next != current)
-                    break;
-            }
-            qchar(current, qbuf);
-            if (use_if) {
-                line(opt, "if (JSKW_AT(%u) == %s) {", optimal_column, qbuf);
-            } else {
-                line(opt, "  case %s:", qbuf);
-            }
-            ++opt->indent_level;
-            generate_letter_switch_r(opt, indexes + same_char_begin,
-                                     i - same_char_begin,
-                                     columns, unprocessed_columns - 1);
-            --opt->indent_level;
-            if (use_if) {
-                line(opt, "}");
-            }
-            current = next;
-        }
-
-        if (!use_if) {
-            line(opt, "}");
-        }
-
-        columns[optimal_column_index] = optimal_column;
-
-        line(opt, "JSKW_NO_MATCH()");
-    }
-}
-
-static void
-generate_letter_switch(struct gen_opt* opt,
-                       unsigned indexes[], unsigned nelem,
-                       unsigned current_length)
-{
-    unsigned* columns;
-    unsigned i;
-
-    columns = (unsigned*) malloc(sizeof(columns[0]) * current_length);
-    if (!columns) {
-        perror("malloc");
-        exit(EXIT_FAILURE);
-    }
-    for (i = 0; i != current_length; ++i) {
-        columns[i] = i;
-    }
-    generate_letter_switch_r(opt, indexes, nelem, columns, current_length);
-    free(columns);
-}
-
-
-static void
-generate_switch(struct gen_opt* opt)
-{
-    unsigned* indexes;
-    unsigned nlength;
-    unsigned i, current;
-    int use_if;
-    unsigned nelem;
-
-    nelem = sizeof(keyword_list)/sizeof(keyword_list[0]);
-
-    line(opt, "/*");
-    line(opt, " * Generating switch for the list of %u entries:", nelem);
-    for (i = 0; i != nelem; ++i) {
-        line(opt, " * %s", keyword_list[i]);
-    }
-    line(opt, " */");
-
-    indexes = (unsigned*) malloc(sizeof(indexes[0]) * nelem);
-    if (!indexes) {
-        perror("malloc");
-        exit(EXIT_FAILURE);
-    }
-    for (i = 0; i != nelem; ++i)
-        indexes[i] = i;
-    qsort(indexes, nelem, sizeof(indexes[i]), length_comparator);
-    nlength = count_different_lengths(indexes, nelem);
-
-    use_if = (nlength <= opt->use_if_threshold);
-
-    if (!use_if)
-        line(opt, "switch (JSKW_LENGTH()) {");
-
-    current = (unsigned)strlen(keyword_list[indexes[0]]);
-    for (i = 0; i != nelem;) {
-        unsigned same_length_begin = i;
-        unsigned next = current;
-
-        for (++i; i != nelem; ++i) {
-            next = (unsigned)strlen(keyword_list[indexes[i]]);
-            if (next != current)
-                break;
-        }
-        if (use_if) {
-            line(opt, "if (JSKW_LENGTH() == %u) {", current);
-        } else {
-            line(opt, "  case %u:", current);
-        }
-        ++opt->indent_level;
-        generate_letter_switch(opt, indexes + same_length_begin,
-                               i - same_length_begin,
-                               current);
-        --opt->indent_level;
-        if (use_if) {
-            line(opt, "}");
-        }
-        current = next;
-    }
-    if (!use_if)
-        line(opt, "}");
-    line(opt, "JSKW_NO_MATCH()");
-    free(indexes);
-}
-
-int main(int argc, char** argv)
-{
-    struct gen_opt opt;
-
-    if (argc < 2) {
-        opt.output = stdout;
-    } else {
-        opt.output = fopen(argv[1], "w");
-        if (!opt.output) {
-            perror("fopen");
-            exit(EXIT_FAILURE);
-        }
-    }
-    opt.indent_level = 1;
-    opt.use_if_threshold = 3;
-    opt.char_tail_test_threshold = 4;
-
-    generate_switch(&opt);
-
-    if (opt.output != stdout) {
-        if (fclose(opt.output)) {
-            perror("fclose");
-            exit(EXIT_FAILURE);
-        }
-    }
-
-    return EXIT_SUCCESS;
-}
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -604,25 +604,21 @@ if CONFIG['HAVE_LINUX_PERF_EVENT_H']:
     ]
     if CONFIG['LINUX_HEADERS_INCLUDES']:
         SOURCES['perf/pm_linux.cpp'].flags += [CONFIG['LINUX_HEADERS_INCLUDES']]
 else:
     SOURCES += [
         'perf/pm_stub.cpp'
     ]
 
-HostSimplePrograms([
-    'host_jskwgen',
-])
-
 GENERATED_FILES += ['jsautokw.h']
 jsautokw = GENERATED_FILES['jsautokw.h']
 jsautokw.script = 'jsautokw.py'
 jsautokw.inputs += [
-    '!host_jskwgen%s' % CONFIG['HOST_BIN_SUFFIX'],
+    'vm/Keywords.h'
 ]
 
 # JavaScript must be built shared, even for static builds, as it is used by
 # other modules which are always built shared. Failure to do so results in
 # the js code getting copied into xpinstall and jsd as well as mozilla-bin,
 # and then the static data cells used for locking no longer work.
 #
 # In fact, we now build both a static and a shared library, as the
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4455,21 +4455,16 @@ NewGlobal(JSContext* cx, unsigned argc, 
         if (v.isBoolean())
             creationOptions.setInvisibleToDebugger(v.toBoolean());
 
         if (!JS_GetProperty(cx, opts, "cloneSingletons", &v))
             return false;
         if (v.isBoolean())
             creationOptions.setCloneSingletons(v.toBoolean());
 
-        if (!JS_GetProperty(cx, opts, "experimentalDateTimeFormatFormatToPartsEnabled", &v))
-            return false;
-        if (v.isBoolean())
-            creationOptions.setExperimentalDateTimeFormatFormatToPartsEnabled(v.toBoolean());
-
         if (!JS_GetProperty(cx, opts, "sameZoneAs", &v))
             return false;
         if (v.isObject())
             creationOptions.setSameZoneAs(UncheckedUnwrap(&v.toObject()));
 
         if (!JS_GetProperty(cx, opts, "disableLazyParsing", &v))
             return false;
         if (v.isBoolean())
--- a/js/src/tests/Intl/DateTimeFormat/formatToParts.js
+++ b/js/src/tests/Intl/DateTimeFormat/formatToParts.js
@@ -1,9 +1,9 @@
-// |reftest| skip-if(!this.hasOwnProperty("Intl")||!this.newGlobal||!newGlobal({experimentalDateTimeFormatFormatToPartsEnabled:true}).Intl.DateTimeFormat().formatToParts)
+// |reftest| skip-if(!this.hasOwnProperty("Intl"))
 // Any copyright is dedicated to the Public Domain.
 // http://creativecommons.org/licenses/publicdomain/
 
 // Tests the format function with a diverse set of locales and options.
 // Always use UTC to avoid dependencies on test environment.
 
 /*
  * Return true if A is equal to B, where equality on arrays and objects
@@ -42,65 +42,59 @@ function deepEqual(a, b) {
 function composeDate(parts) {
   return parts.map(({value}) => value)
               .reduce((string, part) => string + part);
 }
 
 var format;
 var date = Date.UTC(2012, 11, 17, 3, 0, 42);
 
-// The experimental formatToParts method is only exposed if specifically
-// requested.  Perform all tests using DateTimeFormat instances from a global
-// object with this method enabled.
-var DateTimeFormat =
-  newGlobal({experimentalDateTimeFormatFormatToPartsEnabled:true}).Intl.DateTimeFormat;
-
 // Locale en-US; default options.
-format = new DateTimeFormat("en-us", {timeZone: "UTC"});
+format = new Intl.DateTimeFormat("en-us", {timeZone: "UTC"});
 assertEq(deepEqual(format.formatToParts(date), [
   { type: 'month', value: '12' },
   { type: 'literal', value: '/' },
   { type: 'day', value: '17' },
   { type: 'literal', value: '/' },
   { type: 'year', value: '2012' }
 ]), true);
 
 // Just date
-format = new DateTimeFormat("en-us", {
+format = new Intl.DateTimeFormat("en-us", {
   year: 'numeric',
   month: 'numeric',
   day: 'numeric',
   timeZone: "UTC"});
 assertEq(deepEqual(format.formatToParts(date), [
   { type: 'month', value: '12' },
   { type: 'literal', value: '/' },
   { type: 'day', value: '17' },
   { type: 'literal', value: '/' },
   { type: 'year', value: '2012' }
 ]), true);
 assertEq(composeDate(format.formatToParts(date)), format.format(date));
 
 // Just time in hour24
-format = new DateTimeFormat("en-us", {
+format = new Intl.DateTimeFormat("en-us", {
   hour: 'numeric',
   minute: 'numeric',
   second: 'numeric',
   hour12: false,
   timeZone: "UTC"});
 assertEq(deepEqual(format.formatToParts(date), [
   { type: 'hour', value: '03' },
   { type: 'literal', value: ':' },
   { type: 'minute', value: '00' },
   { type: 'literal', value: ':' },
   { type: 'second', value: '42' }
 ]), true);
 assertEq(composeDate(format.formatToParts(date)), format.format(date));
 
 // Just time in hour12
-format = new DateTimeFormat("en-us", {
+format = new Intl.DateTimeFormat("en-us", {
   hour: 'numeric',
   minute: 'numeric',
   second: 'numeric',
   hour12: true,
   timeZone: "UTC"});
 assertEq(deepEqual(format.formatToParts(date), [
   { type: 'hour', value: '3' },
   { type: 'literal', value: ':' },
@@ -108,47 +102,47 @@ assertEq(deepEqual(format.formatToParts(
   { type: 'literal', value: ':' },
   { type: 'second', value: '42' },
   { type: 'literal', value: ' ' },
   { type: 'dayPeriod', value: 'AM' }
 ]), true);
 assertEq(composeDate(format.formatToParts(date)), format.format(date));
 
 // Just month.
-format = new DateTimeFormat("en-us", {
+format = new Intl.DateTimeFormat("en-us", {
   month: "narrow",
   timeZone: "UTC"});
 assertEq(deepEqual(format.formatToParts(date), [
   { type: 'month', value: 'D' }
 ]), true);
 assertEq(composeDate(format.formatToParts(date)), format.format(date));
 
 // Just weekday.
-format = new DateTimeFormat("en-us", {
+format = new Intl.DateTimeFormat("en-us", {
   weekday: "narrow",
   timeZone: "UTC"});
 assertEq(deepEqual(format.formatToParts(date), [
   { type: 'weekday', value: 'M' }
 ]), true);
 assertEq(composeDate(format.formatToParts(date)), format.format(date));
 
 // Year and era.
-format = new DateTimeFormat("en-us", {
+format = new Intl.DateTimeFormat("en-us", {
   year: "numeric",
   era: "short",
   timeZone: "UTC"});
 assertEq(deepEqual(format.formatToParts(date), [
   { type: 'year', value: '2012' },
   { type: 'literal', value: ' ' },
   { type: 'era', value: 'AD' }
 ]), true);
 assertEq(composeDate(format.formatToParts(date)), format.format(date));
 
 // Time and date
-format = new DateTimeFormat("en-us", {
+format = new Intl.DateTimeFormat("en-us", {
   weekday: 'long',
   year: 'numeric',
   month: 'numeric',
   day: 'numeric',
   hour: 'numeric',
   minute: 'numeric',
   second: 'numeric',
   hour12: true,
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -110,17 +110,16 @@
     macro(flags, flags, "flags") \
     macro(float32, float32, "float32") \
     macro(Float32x4, Float32x4, "Float32x4") \
     macro(float64, float64, "float64") \
     macro(Float64x2, Float64x2, "Float64x2") \
     macro(forceInterpreter, forceInterpreter, "forceInterpreter") \
     macro(forEach, forEach, "forEach") \
     macro(format, format, "format") \
-    macro(formatToParts, formatToParts, "formatToParts") \
     macro(frame, frame, "frame") \
     macro(from, from, "from") \
     macro(fulfilled, fulfilled, "fulfilled") \
     macro(fulfillHandler, fulfillHandler, "fulfillHandler") \
     macro(futexNotEqual, futexNotEqual, "not-equal") \
     macro(futexOK, futexOK, "ok") \
     macro(futexTimedOut, futexTimedOut, "timed-out") \
     macro(gcCycleNumber, gcCycleNumber, "gcCycleNumber") \
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -434,22 +434,16 @@ InitGlobalObjectOptions(JS::CompartmentO
     bool shouldDiscardSystemSource = ShouldDiscardSystemSource();
     bool extraWarningsForSystemJS = ExtraWarningsForSystemJS();
 
     bool isSystem = nsContentUtils::IsSystemPrincipal(aPrincipal);
 
     if (isSystem) {
         // Make sure [SecureContext] APIs are visible:
         aOptions.creationOptions().setSecureContext(true);
-
-#if 0 // TODO: Reenable in Bug 1288653
-        // Enable the ECMA-402 experimental formatToParts in any chrome page
-        aOptions.creationOptions()
-                .setExperimentalDateTimeFormatFormatToPartsEnabled(true);
-#endif
     }
 
     if (shouldDiscardSystemSource) {
         bool discardSource = isSystem;
 
         aOptions.behaviors().setDiscardSource(discardSource);
     }
 
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -115,21 +115,25 @@ RenderFrameParent::Init(nsFrameLoader* a
   mAsyncPanZoomEnabled = lm && lm->AsyncPanZoomEnabled();
 
   TabParent* browser = TabParent::GetFrom(mFrameLoader);
   if (XRE_IsParentProcess()) {
     // Our remote frame will push layers updates to the compositor,
     // and we'll keep an indirect reference to that tree.
     browser->Manager()->AsContentParent()->AllocateLayerTreeId(browser, &mLayersId);
     if (lm && lm->AsClientLayerManager()) {
-      lm->AsClientLayerManager()->GetRemoteRenderer()->SendNotifyChildCreated(mLayersId);
+      if (!lm->AsClientLayerManager()->GetRemoteRenderer()->SendNotifyChildCreated(mLayersId)) {
+        return false;
+      }
     }
   } else if (XRE_IsContentProcess()) {
     ContentChild::GetSingleton()->SendAllocateLayerTreeId(browser->Manager()->ChildID(), browser->GetTabId(), &mLayersId);
-    CompositorBridgeChild::Get()->SendNotifyChildCreated(mLayersId);
+    if (!CompositorBridgeChild::Get()->SendNotifyChildCreated(mLayersId)) {
+      return false;
+    }
   }
 
   mInitted = true;
   return true;
 }
 
 bool
 RenderFrameParent::IsInitted()
--- a/layout/reftests/w3c-css/submitted/masking/reftest.list
+++ b/layout/reftests/w3c-css/submitted/masking/reftest.list
@@ -81,26 +81,26 @@ fails == mask-origin-2.html mask-origin-
 default-preferences pref(layout.css.clip-path-shapes.enabled,true)
 
 fuzzy-if(winWidget,1,21) == clip-path-contentBox-1a.html clip-path-geometryBox-1-ref.html
 fuzzy-if(winWidget,1,21) == clip-path-contentBox-1b.html clip-path-geometryBox-1-ref.html
 fuzzy-if(winWidget,1,21) == clip-path-contentBox-1c.html clip-path-geometryBox-1-ref.html
 fuzzy-if(winWidget,1,21) == clip-path-paddingBox-1a.html clip-path-geometryBox-1-ref.html
 fuzzy-if(winWidget,1,21) == clip-path-paddingBox-1b.html clip-path-geometryBox-1-ref.html
 fuzzy-if(winWidget,1,21) == clip-path-paddingBox-1c.html clip-path-geometryBox-1-ref.html
-fuzzy(64,311) == clip-path-borderBox-1a.html clip-path-geometryBox-1-ref.html
+fuzzy(64,370) == clip-path-borderBox-1a.html clip-path-geometryBox-1-ref.html
 fuzzy-if(winWidget,1,21) == clip-path-borderBox-1b.html clip-path-geometryBox-1-ref.html
-fuzzy(64,311) == clip-path-borderBox-1c.html clip-path-geometryBox-1-ref.html
+fuzzy(64,370) == clip-path-borderBox-1c.html clip-path-geometryBox-1-ref.html
 fuzzy-if(winWidget,1,21) == clip-path-marginBox-1a.html clip-path-geometryBox-1-ref.html
-fuzzy(64,311) == clip-path-fillBox-1a.html clip-path-geometryBox-1-ref.html
-fuzzy(64,311) == clip-path-strokeBox-1a.html clip-path-geometryBox-1-ref.html
-fuzzy(64,311) == clip-path-strokeBox-1b.html clip-path-geometryBox-1-ref.html
+fuzzy(64,370) == clip-path-fillBox-1a.html clip-path-geometryBox-1-ref.html
+fuzzy(64,370) == clip-path-strokeBox-1a.html clip-path-geometryBox-1-ref.html
+fuzzy(64,370) == clip-path-strokeBox-1b.html clip-path-geometryBox-1-ref.html
 fuzzy-if(winWidget,1,21) == clip-path-viewBox-1a.html clip-path-geometryBox-1-ref.html
 fuzzy-if(winWidget,1,21) == clip-path-viewBox-1b.html clip-path-geometryBox-1-ref.html
-fuzzy(64,311) == clip-path-viewBox-1c.html clip-path-geometryBox-1-ref.html
-fuzzy-if(winWidget,1,98) == clip-path-geometryBox-2.html clip-path-geometryBox-2-ref.html
+fuzzy(64,370) == clip-path-viewBox-1c.html clip-path-geometryBox-1-ref.html
+fuzzy-if(winWidget,9,98) == clip-path-geometryBox-2.html clip-path-geometryBox-2-ref.html
 
 default-preferences
 
 # mask with opacity test cases
 fuzzy(1,5000) == mask-opacity-1a.html mask-opacity-1-ref.html
 fuzzy(1,5000) == mask-opacity-1b.html mask-opacity-1-ref.html
 fuzzy(1,5000) == mask-opacity-1c.html mask-opacity-1-ref.html
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -762,23 +762,17 @@ pref("gfx.font_rendering.directwrite.use
 #endif
 
 pref("gfx.font_rendering.opentype_svg.enabled", true);
 
 #ifdef XP_WIN
 // comma separated list of backends to use in order of preference
 // e.g., pref("gfx.canvas.azure.backends", "direct2d,skia,cairo");
 pref("gfx.canvas.azure.backends", "direct2d1.1,skia,cairo");
-
-#ifdef NIGHTLY_BUILD
-pref("gfx.content.azure.backends", "direct2d1.1,skia,cairo");
-#else
 pref("gfx.content.azure.backends", "direct2d1.1,cairo");
-#endif
-
 #else
 #ifdef XP_MACOSX
 pref("gfx.content.azure.backends", "skia");
 pref("gfx.canvas.azure.backends", "skia");
 // Accelerated cg canvas where available (10.7+)
 pref("gfx.canvas.azure.accelerated", true);
 #else
 pref("gfx.canvas.azure.backends", "skia");
@@ -5083,16 +5077,20 @@ pref("dom.voicemail.enabled", false);
 #endif
 // Numeric default service id for Voice Mail API calls with |serviceId|
 // parameter omitted.
 pref("dom.voicemail.defaultServiceId", 0);
 
 // Enable mapped array buffer by default.
 pref("dom.mapped_arraybuffer.enabled", true);
 
+// Whether to send more than one "loading" readystatechange during XHRs to
+// simulate progress events for sites still not using modern progress events.
+pref("dom.fire_extra_xhr_loading_readystatechanges", false);
+
 // The tables used for Safebrowsing phishing and malware checks.
 pref("urlclassifier.malwareTable", "goog-malware-shavar,goog-unwanted-shavar,test-malware-simple,test-unwanted-simple");
 
 #ifdef MOZILLA_OFFICIAL
 // In the official build, we are allowed to use google's private
 // phishing list "goog-phish-shavar". See Bug 1288840.
 pref("urlclassifier.phishTable", "goog-phish-shavar,test-phish-simple");
 #else
--- a/netwerk/base/nsSocketTransportService2.cpp
+++ b/netwerk/base/nsSocketTransportService2.cpp
@@ -49,16 +49,18 @@ Atomic<PRThread*, Relaxed> gSocketThread
 #define KEEPALIVE_PROBE_COUNT_PREF "network.tcp.keepalive.probe_count"
 #define SOCKET_LIMIT_TARGET 1000U
 #define SOCKET_LIMIT_MIN      50U
 #define BLIP_INTERVAL_PREF "network.activity.blipIntervalMilliseconds"
 #define MAX_TIME_BETWEEN_TWO_POLLS "network.sts.max_time_for_events_between_two_polls"
 #define TELEMETRY_PREF "toolkit.telemetry.enabled"
 #define MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN "network.sts.max_time_for_pr_close_during_shutdown"
 
+#define REPAIR_POLLABLE_EVENT_TIME 10
+
 uint32_t nsSocketTransportService::gMaxCount;
 PRCallOnceType nsSocketTransportService::gMaxCountInitOnce;
 
 //-----------------------------------------------------------------------------
 // ctor/dtor (called on the main/UI thread by the service manager)
 
 nsSocketTransportService::nsSocketTransportService()
     : mThread(nullptr)
@@ -80,16 +82,19 @@ nsSocketTransportService::nsSocketTransp
     , mKeepaliveProbeCount(kDefaultTCPKeepCount)
     , mKeepaliveEnabledPref(false)
     , mServingPendingQueue(false)
     , mMaxTimePerPollIter(100)
     , mTelemetryEnabledPref(false)
     , mMaxTimeForPrClosePref(PR_SecondsToInterval(5))
     , mSleepPhase(false)
     , mProbedMaxCount(false)
+#if defined(XP_WIN)
+    , mPolling(false)
+#endif
 {
     NS_ASSERTION(NS_IsMainThread(), "wrong thread");
 
     PR_CallOnce(&gMaxCountInitOnce, DiscoverMaxCount);
     mActiveList = (SocketContext *)
         moz_xmalloc(sizeof(SocketContext) * mActiveListSize);
     mIdleList = (SocketContext *)
         moz_xmalloc(sizeof(SocketContext) * mIdleListSize);
@@ -756,16 +761,23 @@ nsSocketTransportService::OnDispatchedEv
     if (PR_GetCurrentThread() == gSocketThread) {
         // this check is redundant to one done inside ::Signal(), but
         // we can do it here and skip obtaining the lock - given that
         // this is a relatively common occurance its worth the
         // redundant code
         SOCKET_LOG(("OnDispatchedEvent Same Thread Skip Signal\n"));
         return NS_OK;
     }
+#else
+    if (gIOService->IsNetTearingDown()) {
+        // Poll can hang sometimes. If we are in shutdown, we are going to
+        // start a watchdog. If we do not exit poll within
+        // REPAIR_POLLABLE_EVENT_TIME signal a pollable event again.
+        StartPollWatchdog();
+    }
 #endif
 
     MutexAutoLock lock(mLock);
     if (mPollableEvent) {
         mPollableEvent->Signal();
     }
     return NS_OK;
 }
@@ -1063,17 +1075,23 @@ nsSocketTransportService::DoPollIteratio
 #endif
 
     // Measures seconds spent while blocked on PR_Poll
     uint32_t pollInterval = 0;
     int32_t n = 0;
     *pollDuration = 0;
     if (!gIOService->IsNetTearingDown()) {
         // Let's not do polling during shutdown.
+#if defined(XP_WIN)
+        StartPolling();
+#endif
         n = Poll(&pollInterval, pollDuration);
+#if defined(XP_WIN)
+        EndPolling();
+#endif
     }
 
     if (n < 0) {
         SOCKET_LOG(("  PR_Poll error [%d] os error [%d]\n", PR_GetError(),
                     PR_GetOSError()));
     }
     else {
         //
@@ -1304,16 +1322,23 @@ nsSocketTransportService::Observe(nsISup
     }
 
     if (!strcmp(topic, NS_TIMER_CALLBACK_TOPIC)) {
         nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
         if (timer == mAfterWakeUpTimer) {
             mAfterWakeUpTimer = nullptr;
             mSleepPhase = false;
         }
+
+#if defined(XP_WIN)
+        if (timer == mPollRepairTimer) {
+            DoPollRepair();
+        }
+#endif
+
     } else if (!strcmp(topic, NS_WIDGET_SLEEP_OBSERVER_TOPIC)) {
         mSleepPhase = true;
         if (mAfterWakeUpTimer) {
             mAfterWakeUpTimer->Cancel();
             mAfterWakeUpTimer = nullptr;
         }
     } else if (!strcmp(topic, NS_WIDGET_WAKE_OBSERVER_TOPIC)) {
         if (mSleepPhase && !mAfterWakeUpTimer) {
@@ -1526,10 +1551,56 @@ nsSocketTransportService::GetSocketConne
 {
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
     for (uint32_t i = 0; i < mActiveCount; i++)
         AnalyzeConnection(data, &mActiveList[i], true);
     for (uint32_t i = 0; i < mIdleCount; i++)
         AnalyzeConnection(data, &mIdleList[i], false);
 }
 
+#if defined(XP_WIN)
+void
+nsSocketTransportService::StartPollWatchdog()
+{
+    MutexAutoLock lock(mLock);
+
+    // Poll can hang sometimes. If we are in shutdown, we are going to start a
+    // watchdog. If we do not exit poll within REPAIR_POLLABLE_EVENT_TIME
+    // signal a pollable event again.
+    MOZ_ASSERT(gIOService->IsNetTearingDown());
+    if (mPolling && !mPollRepairTimer) {
+        mPollRepairTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+        mPollRepairTimer->Init(this, REPAIR_POLLABLE_EVENT_TIME,
+                               nsITimer::TYPE_REPEATING_SLACK);
+    }
+}
+
+void
+nsSocketTransportService::DoPollRepair()
+{
+    MutexAutoLock lock(mLock);
+    if (mPolling && mPollableEvent) {
+        mPollableEvent->Signal();
+    } else if (mPollRepairTimer) {
+        mPollRepairTimer->Cancel();
+    }
+}
+
+void
+nsSocketTransportService::StartPolling()
+{
+    MutexAutoLock lock(mLock);
+    mPolling = true;
+}
+
+void
+nsSocketTransportService::EndPolling()
+{
+    MutexAutoLock lock(mLock);
+    mPolling = false;
+    if (mPollRepairTimer) {
+        mPollRepairTimer->Cancel();
+    }
+}
+#endif
+
 } // namespace net
 } // namespace mozilla
--- a/netwerk/base/nsSocketTransportService2.h
+++ b/netwerk/base/nsSocketTransportService2.h
@@ -255,16 +255,25 @@ private:
                            SocketContext *context, bool aActive);
 
     void ClosePrivateConnections();
     void DetachSocketWithGuard(bool aGuardLocals,
                                SocketContext *socketList,
                                int32_t index);
 
     void MarkTheLastElementOfPendingQueue();
+
+#if defined(XP_WIN)
+    Atomic<bool> mPolling;
+    nsCOMPtr<nsITimer> mPollRepairTimer;
+    void StartPollWatchdog();
+    void DoPollRepair();
+    void StartPolling();
+    void EndPolling();
+#endif
 };
 
 extern nsSocketTransportService *gSocketTransportService;
 extern Atomic<PRThread*, Relaxed> gSocketThread;
 
 } // namespace net
 } // namespace mozilla
 
--- a/netwerk/base/nsURLParsers.cpp
+++ b/netwerk/base/nsURLParsers.cpp
@@ -492,16 +492,22 @@ nsAuthURLParser::ParseAuthority(const ch
                            usernamePos, usernameLen,
                            passwordPos, passwordLen);
         if (NS_FAILED(rv)) return rv;
         rv = ParseServerInfo(p + 1, authLen - (p - auth + 1),
                              hostnamePos, hostnameLen,
                              port);
         if (NS_FAILED(rv)) return rv;
         OFFSET_RESULT(hostname, p + 1 - auth);
+
+        // malformed if has a username or password
+        // but no host info, such as: http://u:p@/
+        if ((usernamePos || passwordPos) && (!hostnamePos || !*hostnameLen)) {
+            return NS_ERROR_MALFORMED_URI;
+        }
     }
     else {
         // auth = <server-info>
         SET_RESULT(username, 0, -1);
         SET_RESULT(password, 0, -1);
         rv = ParseServerInfo(auth, authLen,
                              hostnamePos, hostnameLen,
                              port);
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -8,16 +8,20 @@
 #include "HttpLog.h"
 
 // Log on level :5, instead of default :4.
 #undef LOG
 #define LOG(args) LOG5(args)
 #undef LOG_ENABLED
 #define LOG_ENABLED() LOG5_ENABLED()
 
+#define TLS_EARLY_DATA_NOT_AVAILABLE 0
+#define TLS_EARLY_DATA_AVAILABLE_BUT_NOT_USED 1
+#define TLS_EARLY_DATA_AVAILABLE_AND_USED 2
+
 #include "ASpdySession.h"
 #include "mozilla/ChaosMode.h"
 #include "mozilla/Telemetry.h"
 #include "nsHttpConnection.h"
 #include "nsHttpHandler.h"
 #include "nsHttpPipeline.h"
 #include "nsHttpRequestHead.h"
 #include "nsHttpResponseHead.h"
@@ -77,16 +81,17 @@ nsHttpConnection::nsHttpConnection()
     , mLastHttpResponseVersion(NS_HTTP_VERSION_1_1)
     , mTransactionCaps(0)
     , mResponseTimeoutEnabled(false)
     , mTCPKeepaliveConfig(kTCPKeepaliveDisabled)
     , mForceSendPending(false)
     , m0RTTChecked(false)
     , mWaitingFor0RTTResponse(false)
     , mContentBytesWritten0RTT(0)
+    , mEarlyDataNegotiated(false)
 {
     LOG(("Creating nsHttpConnection @%p\n", this));
 
     // the default timeout is for when this connection has not yet processed a
     // transaction
     static const PRIntervalTime k5Sec = PR_SecondsToInterval(5);
     mIdleTimeout =
         (k5Sec < gHttpHandler->IdleTimeout()) ? k5Sec : gHttpHandler->IdleTimeout();
@@ -336,29 +341,31 @@ nsHttpConnection::EnsureNPNComplete(nsre
             if (rv == NS_ERROR_NOT_CONNECTED) {
                 rvEarlyAlpn = ssl->GetAlpnEarlySelection(earlyNegotiatedNPN);
             }
         }
 
         if (NS_FAILED(rvEarlyAlpn)) {
             LOG(("nsHttpConnection::EnsureNPNComplete %p - "
                  "early selected alpn not available", this));
+            mEarlyDataNegotiated = false;
         } else {
             LOG(("nsHttpConnection::EnsureNPNComplete %p -"
                  "early selected alpn: %s", this, earlyNegotiatedNPN.get()));
             uint32_t infoIndex;
             const SpdyInformation *info = gHttpHandler->SpdyInfo();
             // We are doing 0RTT only with Http/1 right now!
             if (NS_FAILED(info->GetNPNIndex(earlyNegotiatedNPN, &infoIndex))) {
                 // Check if early-data is allowed for this transaction.
                 if (mTransaction->Do0RTT()) {
                     LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - We "
                          "can do 0RTT!", this));
                     mWaitingFor0RTTResponse = true;
                 }
+                mEarlyDataNegotiated = true;
             }
         }
     }
 
     if (rv == NS_ERROR_NOT_CONNECTED) {
         if (mWaitingFor0RTTResponse) {
             aOut0RTTWriteHandshakeValue = mTransaction->ReadSegments(this,
                 nsIOService::gDefaultSegmentSize, &aOut0RTTBytesWritten);
@@ -381,28 +388,48 @@ nsHttpConnection::EnsureNPNComplete(nsre
 
     if (NS_SUCCEEDED(rv)) {
         LOG(("nsHttpConnection::EnsureNPNComplete %p [%s] negotiated to '%s'%s\n",
              this, mConnInfo->HashKey().get(), negotiatedNPN.get(),
              mTLSFilter ? " [Double Tunnel]" : ""));
 
         bool ealyDataAccepted = false;
         if (mWaitingFor0RTTResponse) {
-            mWaitingFor0RTTResponse = false;
             // Check if early data has been accepted.
             rv = ssl->GetEarlyDataAccepted(&ealyDataAccepted);
             LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - early data "
                  "that was sent during 0RTT %s been accepted.",
                  this, ealyDataAccepted ? "has" : "has not"));
+
             if (NS_FAILED(rv) ||
                 NS_FAILED(mTransaction->Finish0RTT(!ealyDataAccepted))) {
                 mTransaction->Close(NS_ERROR_NET_RESET);
                 goto npnComplete;
             }
         }
+
+        int16_t tlsVersion;
+        ssl->GetSSLVersionUsed(&tlsVersion);
+        // Send the 0RTT telemetry only for tls1.3
+        if (tlsVersion > nsISSLSocketControl::TLS_VERSION_1_2) {
+            Telemetry::Accumulate(Telemetry::TLS_EARLY_DATA_NEGOTIATED,
+                (!mEarlyDataNegotiated) ? TLS_EARLY_DATA_NOT_AVAILABLE
+                    : ((mWaitingFor0RTTResponse) ? TLS_EARLY_DATA_AVAILABLE_AND_USED
+                                                 : TLS_EARLY_DATA_AVAILABLE_BUT_NOT_USED));
+            if (mWaitingFor0RTTResponse) {
+                Telemetry::Accumulate(Telemetry::TLS_EARLY_DATA_ACCEPTED,
+                                      ealyDataAccepted);
+            }
+            if (ealyDataAccepted) {
+                Telemetry::Accumulate(Telemetry::TLS_EARLY_DATA_BYTES_WRITTEN,
+                                      mContentBytesWritten0RTT);
+            }
+        }
+        mWaitingFor0RTTResponse = false;
+
         if (!ealyDataAccepted) {
             uint32_t infoIndex;
             const SpdyInformation *info = gHttpHandler->SpdyInfo();
             if (NS_SUCCEEDED(info->GetNPNIndex(negotiatedNPN, &infoIndex))) {
                 StartSpdy(info->Version[infoIndex]);
             }
         } else {
           LOG(("nsHttpConnection::EnsureNPNComplete [this=%p] - %d bytes "
--- a/netwerk/protocol/http/nsHttpConnection.h
+++ b/netwerk/protocol/http/nsHttpConnection.h
@@ -364,14 +364,15 @@ private:
                                                   // checked.
     bool                            mWaitingFor0RTTResponse; // We have are
                                                              // sending 0RTT
                                                              // data and we
                                                              // are waiting
                                                              // for the end of
                                                              // the handsake.
     int64_t                        mContentBytesWritten0RTT;
+    bool                           mEarlyDataNegotiated; //Only used for telemetry
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // nsHttpConnection_h__
--- a/netwerk/test/unit/test_URIs.js
+++ b/netwerk/test/unit/test_URIs.js
@@ -112,24 +112,16 @@ var gTests = [
     prePath: "ftp://foo:@ftp.mozilla.org:100",
     port:    100,
     username: "foo",
     password: "",
     path:    "/pub/mozilla.org/README",
     ref:     "",
     nsIURL:  true, nsINestedURI: false },
   //Bug 706249
-  { spec:    "http:x:@",
-    scheme:  "http",
-    prePath: "http://x:@",
-    username: "x",
-    password: "",
-    path:    "",
-    ref:     "",
-    nsIURL:  true, nsINestedURI: false },
   { spec:    "gopher://mozilla.org/",
     scheme:  "gopher",
     prePath: "gopher:",
     path:    "//mozilla.org/",
     ref:     "",
     nsIURL:  false, nsINestedURI: false },
   { spec:    "http://www.example.com/",
     scheme:  "http",
--- a/netwerk/test/unit/test_standardurl.js
+++ b/netwerk/test/unit/test_standardurl.js
@@ -332,16 +332,18 @@ add_test(function test_backslashReplacem
 
   run_next_test();
 });
 
 add_test(function test_authority_host()
 {
   Assert.throws(() => { stringToURL("http:"); }, "TYPE_AUTHORITY should have host");
   Assert.throws(() => { stringToURL("http:///"); }, "TYPE_AUTHORITY should have host");
+  Assert.throws(() => { stringToURL("http://u:p@/"); }, "User or password without host is not allowed");
+  Assert.throws(() => { stringToURL("http:@/"); }, "Must have a host");
 
   run_next_test();
 });
 
 add_test(function test_trim_C0_and_space()
 {
   var url = stringToURL("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f http://example.com/ \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f ");
   do_check_eq(url.spec, "http://example.com/");
--- a/security/manager/pki/resources/content/certManager.xul
+++ b/security/manager/pki/resources/content/certManager.xul
@@ -8,24 +8,24 @@
 <?xul-overlay href="chrome://pippki/content/MineOverlay.xul"?>
 <?xul-overlay href="chrome://pippki/content/OthersOverlay.xul"?>
 <?xul-overlay href="chrome://pippki/content/WebSitesOverlay.xul"?>
 <?xul-overlay href="chrome://pippki/content/CAOverlay.xul"?>
 <?xul-overlay href="chrome://pippki/content/OrphanOverlay.xul"?>
 
 <!DOCTYPE dialog SYSTEM "chrome://pippki/locale/certManager.dtd">
 
-<dialog id="certmanager" 
+<dialog id="certmanager"
 	windowtype="mozilla:certmanager"
-	xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" 
+	xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         title="&certmgr.title;"
         onload="LoadCerts();"
         onunload="DeregisterSmartCardObservers();"
         buttons="accept"
-        style="width: 48em; height: 32em;"
+        style="width: 63em; height: 32em;"
         persist="screenX screenY width height">
 
   <stringbundle id="pippki_bundle" src="chrome://pippki/locale/pippki.properties"/>
 
   <script type="application/javascript" src="chrome://pippki/content/pippki.js"/>
   <script type="application/javascript" src="chrome://pippki/content/certManager.js"/>
 
   <vbox flex="1">
--- a/taskcluster/taskgraph/transforms/tests/make_task_description.py
+++ b/taskcluster/taskgraph/transforms/tests/make_task_description.py
@@ -183,18 +183,24 @@ def docker_worker_setup(config, test, ta
             'mount-point': '/home/worker/tooltool-cache',
         })
         taskdesc['scopes'].extend([
             'docker-worker:relengapi-proxy:tooltool.download.internal',
             'docker-worker:relengapi-proxy:tooltool.download.public',
         ])
 
     # assemble the command line
+    command = [
+        '/home/worker/bin/run-task',
+        # The workspace cache/volume is default owned by root:root.
+        '--chown', '/home/worker/workspace',
+        '--',
+        '/home/worker/bin/test-linux.sh',
+    ]
 
-    command = ["bash", "/home/worker/bin/test.sh"]
     if mozharness.get('no-read-buildbot-config'):
         command.append("--no-read-buildbot-config")
     command.extend([
         {"task-reference": "--installer-url=" + installer_url},
         {"task-reference": "--test-packages-url=" + test_packages_url},
     ])
     command.extend(mozharness.get('extra-options', []))
 
--- a/testing/docker/desktop-test/Dockerfile
+++ b/testing/docker/desktop-test/Dockerfile
@@ -37,17 +37,16 @@ ENV           USER          worker
 ENV           LOGNAME       worker
 ENV           HOSTNAME      taskcluster-worker
 ENV           LANG          en_US.UTF-8
 ENV           LC_ALL        en_US.UTF-8
 
 # Add utilities and configuration
 COPY           dot-files/config              /home/worker/.config
 COPY           dot-files/pulse               /home/worker/.pulse
-COPY           bin                           /home/worker/bin
 RUN            chmod +x bin/*
 # TODO: remove this when buildbot is gone
 COPY           buildprops.json               /home/worker/buildprops.json
 COPY           tc-vcs-config.yml /etc/taskcluster-vcs.yml
 
 # TODO: remove
 ADD            https://raw.githubusercontent.com/taskcluster/buildbot-step/master/buildbot_step /home/worker/bin/buildbot_step
 RUN chmod u+x /home/worker/bin/buildbot_step
deleted file mode 100644
--- a/testing/docker/desktop-test/bin/test.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#! /bin/bash -vex
-
-set -x -e
-
-: GECKO_HEAD_REPOSITORY         ${GECKO_HEAD_REPOSITORY:=https://hg.mozilla.org/mozilla-central}
-: GECKO_HEAD_REV                ${GECKO_HEAD_REV:=default}
-: WORKSPACE                     ${WORKSPACE:=/home/worker/workspace}
-
-
-# TODO: when bug 1093833 is solved and tasks can run as non-root, reduce this
-# to a simple fail-if-root check
-if [ $(id -u) = 0 ]; then
-    chown -R worker:worker /home/worker
-    # drop privileges by re-running this script
-    exec sudo -E -u worker bash /home/worker/bin/test.sh "${@}"
-fi
-
-[ -d $WORKSPACE ] || mkdir -p $WORKSPACE
-cd $WORKSPACE
-exec /home/worker/bin/test-linux.sh "${@}"
deleted file mode 100644
--- a/testing/docker/desktop1604-test/bin/test.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#! /bin/bash -vex
-
-set -x -e
-
-: GECKO_HEAD_REPOSITORY         ${GECKO_HEAD_REPOSITORY:=https://hg.mozilla.org/mozilla-central}
-: GECKO_HEAD_REV                ${GECKO_HEAD_REV:=default}
-: WORKSPACE                     ${WORKSPACE:=/home/worker/workspace}
-
-
-# TODO: when bug 1093833 is solved and tasks can run as non-root, reduce this
-# to a simple fail-if-root check
-if [ $(id -u) = 0 ]; then
-    chown -R worker:worker /home/worker
-    # drop privileges by re-running this script
-    exec sudo -E -u worker bash /home/worker/bin/test.sh "${@}"
-fi
-
-[ -d $WORKSPACE ] || mkdir -p $WORKSPACE
-cd $WORKSPACE
-exec /home/worker/bin/test-linux.sh "${@}"
--- a/testing/docker/recipes/run-task
+++ b/testing/docker/recipes/run-task
@@ -151,16 +151,23 @@ def main(args):
         task_args = args[i + 1:]
     except ValueError:
         our_args = args
         task_args = []
 
     parser = argparse.ArgumentParser()
     parser.add_argument('--user', default='worker', help='user to run as')
     parser.add_argument('--group', default='worker', help='group to run as')
+    # We allow paths to be chowned by the --user:--group before permissions are
+    # dropped. This is often necessary for caches/volumes, since they default
+    # to root:root ownership.
+    parser.add_argument('--chown', action='append',
+                        help='Directory to chown to --user:--group')
+    parser.add_argument('--chown-recursive', action='append',
+                        help='Directory to recursively chown to --user:--group')
     parser.add_argument('--vcs-checkout',
                         help='Directory where Gecko checkout should be created')
 
     args = parser.parse_args(our_args)
 
     try:
         user = pwd.getpwnam(args.user)
     except KeyError:
@@ -175,16 +182,33 @@ def main(args):
         return 1
 
     uid = user.pw_uid
     gid = group.gr_gid
 
     # Find all groups to which this user is a member.
     gids = [g.gr_gid for g in grp.getgrall() if args.group in g.gr_mem]
 
+    # Change ownership of requested paths.
+    # FUTURE: parse argument values for user/group if we don't want to
+    # use --user/--group.
+    for path in args.chown or []:
+        print_line(b'chown', b'changing ownership of %s to %s:%s\n' % (
+                   path, user.pw_name, group.gr_name))
+        os.chown(path, uid, gid)
+
+    for path in args.chown_recursive or []:
+        print_line(b'chown', b'recursively changing ownership of %s to %s:%s\n' %
+                   (path, user.pw_name, group.gr_name))
+        for root, dirs, files in os.walk(path):
+            for d in dirs:
+                os.chown(os.path.join(root, d), uid, gid)
+            for f in files:
+                os.chown(os.path.join(root, f), uid, gid)
+
     checkout = args.vcs_checkout
     if checkout:
         # Ensure the directory for the source checkout exists.
         try:
             os.makedirs(os.path.dirname(checkout))
         except OSError as e:
             if e.errno != errno.EEXIST:
                 raise
deleted file mode 100644
--- a/testing/web-platform/meta/XMLHttpRequest/event-readystatechange-loaded.htm.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[event-readystatechange-loaded.htm]
-  type: testharness
-  [XMLHttpRequest: the LOADING state change should only happen once]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/XMLHttpRequest/open-url-bogus.htm.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[open-url-bogus.htm]
-  type: testharness
-  [XMLHttpRequest: open() - bogus URLs (http://u:p@/)]
-    expected: FAIL
-
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-002a.html.ini
@@ -0,0 +1,7 @@
+[dir-isolation-002a.html]
+  type: reftest
+  expected:
+    if debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if not debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-002b.html.ini
@@ -0,0 +1,8 @@
+[dir-isolation-002b.html]
+  type: reftest
+  expected:
+    if debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if not debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-002c.html.ini
@@ -0,0 +1,8 @@
+[dir-isolation-002c.html]
+  type: reftest
+  expected:
+    if debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if not debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-006a.html.ini
@@ -0,0 +1,7 @@
+[dir-isolation-006a.html]
+  type: reftest
+  expected:
+    if debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if not debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-006b.html.ini
@@ -0,0 +1,8 @@
+[dir-isolation-006b.html]
+  type: reftest
+  expected:
+    if debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if not debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-006c.html.ini
@@ -0,0 +1,8 @@
+[dir-isolation-006c.html]
+  type: reftest
+  expected:
+    if debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if not debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009a.html.ini
@@ -0,0 +1,8 @@
+[dir-isolation-009a.html]
+  type: reftest
+  expected:
+    if debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if not debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009b.html.ini
@@ -0,0 +1,8 @@
+[dir-isolation-009b.html]
+  type: reftest
+  expected:
+    if debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if not debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/dom/elements/requirements-relating-to-bidirectional-algorithm-formatting-characters/dir-isolation-009c.html.ini
@@ -0,0 +1,7 @@
+[dir-isolation-009c.html]
+  type: reftest
+  expected:
+    if debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if not debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if not debug and not e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
+    if debug and e10s and (os == "win") and (version == "6.1.7601") and (processor == "x86") and (bits == 32): FAIL
--- a/testing/web-platform/meta/url/url-constructor.html.ini
+++ b/testing/web-platform/meta/url/url-constructor.html.ini
@@ -76,19 +76,16 @@
     expected: FAIL
 
   [Parsing: <http://example.com/foo/%2e> against <about:blank>]
     expected: FAIL
 
   [Parsing: <data:test# »> against <about:blank>]
     expected: FAIL
 
-  [Parsing: <http://user:pass@/> against <about:blank>]
-    expected: FAIL
-
   [Parsing: <httpa://foo:80/> against <about:blank>]
     expected: FAIL
 
   [Parsing: <gopher://foo:70/> against <about:blank>]
     expected: FAIL
 
   [Parsing: <gopher://foo:443/> against <about:blank>]
     expected: FAIL
@@ -109,40 +106,16 @@
     expected: FAIL
 
   [Parsing: <http:/:b@www.example.com> against <about:blank>]
     expected: FAIL
 
   [Parsing: <http://:b@www.example.com> against <about:blank>]
     expected: FAIL
 
-  [Parsing: <http://user@/www.example.com> against <about:blank>]
-    expected: FAIL
-
-  [Parsing: <http:@/www.example.com> against <about:blank>]
-    expected: FAIL
-
-  [Parsing: <http:/@/www.example.com> against <about:blank>]
-    expected: FAIL
-
-  [Parsing: <http://@/www.example.com> against <about:blank>]
-    expected: FAIL
-
-  [Parsing: <https:@/www.example.com> against <about:blank>]
-    expected: FAIL
-
-  [Parsing: <http:a:b@/www.example.com> against <about:blank>]
-    expected: FAIL
-
-  [Parsing: <http:/a:b@/www.example.com> against <about:blank>]
-    expected: FAIL
-
-  [Parsing: <http://a:b@/www.example.com> against <about:blank>]
-    expected: FAIL
-
   [Parsing: <http://www.@pple.com> against <about:blank>]
     expected: FAIL
 
   [Parsing: <http://:@www.example.com> against <about:blank>]
     expected: FAIL
 
   [Parsing: <http://﷐zyx.com> against <http://other.com/>]
     expected: FAIL
--- a/toolkit/components/extensions/test/xpcshell/head.js
+++ b/toolkit/components/extensions/test/xpcshell/head.js
@@ -1,16 +1,17 @@
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
-/* exported createHttpServer, promiseConsoleOutput */
+/* exported createHttpServer, promiseConsoleOutput, cleanupDir */
 
 Components.utils.import("resource://gre/modules/Task.jsm");
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/Timer.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
                                   "resource://gre/modules/AppConstants.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Extension",
                                   "resource://gre/modules/Extension.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ExtensionData",
                                   "resource://gre/modules/Extension.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
@@ -77,8 +78,33 @@ var promiseConsoleOutput = Task.async(fu
     Services.console.logStringMessage(DONE);
     yield awaitListener;
 
     return {messages, result};
   } finally {
     Services.console.unregisterListener(listener);
   }
 });
+
+// Attempt to remove a directory.  If the Windows OS is still using the
+// file sometimes remove() will fail.  So try repeatedly until we can
+// remove it or we give up.
+function cleanupDir(dir) {
+  let count = 0;
+  return new Promise((resolve, reject) => {
+    function tryToRemoveDir() {
+      count += 1;
+      try {
+        dir.remove(true);
+      } catch (e) {
+        // ignore
+      }
+      if (!dir.exists()) {
+        return resolve();
+      }
+      if (count >= 25) {
+        return reject(`Failed to cleanup directory: ${dir}`);
+      }
+      setTimeout(tryToRemoveDir, 100);
+    }
+    tryToRemoveDir();
+  });
+}
--- a/toolkit/components/extensions/test/xpcshell/test_ext_downloads_search.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_downloads_search.js
@@ -90,18 +90,17 @@ add_task(function* test_search() {
   }
 
   Services.prefs.setIntPref("browser.download.folderList", 2);
   Services.prefs.setComplexValue("browser.download.dir", nsIFile, downloadDir);
 
   do_register_cleanup(() => {
     Services.prefs.clearUserPref("browser.download.folderList");
     Services.prefs.clearUserPref("browser.download.dir");
-    downloadDir.remove(true);
-    return clearDownloads();
+    return cleanupDir(downloadDir).then(clearDownloads);
   });
 
   yield clearDownloads().then(downloads => {
     do_print(`removed ${downloads.length} pre-existing downloads from history`);
   });
 
   let extension = ExtensionTestUtils.loadExtension({
     background: backgroundScript,
--- a/toolkit/components/passwordmgr/test/browser/browser.ini
+++ b/toolkit/components/passwordmgr/test/browser/browser.ini
@@ -10,17 +10,16 @@ support-files =
   form_cross_origin_secure_action.html
   head.js
   insecure_test.html
   insecure_test_subframe.html
   multiple_forms.html
   streamConverter_content.sjs
 
 [browser_capture_doorhanger.js]
-skip-if = e10s # Bug 1277105
 support-files =
   subtst_notifications_1.html
   subtst_notifications_2.html
   subtst_notifications_2pw_0un.html
   subtst_notifications_2pw_1un_1text.html
   subtst_notifications_3.html
   subtst_notifications_4.html
   subtst_notifications_5.html
--- a/toolkit/components/passwordmgr/test/browser/browser_capture_doorhanger.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_capture_doorhanger.js
@@ -45,16 +45,18 @@ add_task(function* test_remember_opens()
 add_task(function* test_clickNever() {
   yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
     is(fieldValues.username, "notifyu1", "Checking submitted username");
     is(fieldValues.password, "notifyp1", "Checking submitted password");
     let notif = getCaptureDoorhanger("password-save");
     ok(notif, "got notification popup");
     is(true, Services.logins.getLoginSavingEnabled("http://example.com"),
        "Checking for login saving enabled");
+
+    yield* checkDoorhangerUsernamePassword("notifyu1", "notifyp1");
     clickDoorhangerButton(notif, NEVER_BUTTON);
   });
 
   is(Services.logins.getAllLogins().length, 0, "Should not have any logins yet");
 
   info("Make sure Never took effect");
   yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
     is(fieldValues.username, "notifyu1", "Checking submitted username");
@@ -72,16 +74,18 @@ add_task(function* test_clickNever() {
 add_task(function* test_clickRemember() {
   yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
     is(fieldValues.username, "notifyu1", "Checking submitted username");
     is(fieldValues.password, "notifyp1", "Checking submitted password");
     let notif = getCaptureDoorhanger("password-save");
     ok(notif, "got notification popup");
 
     is(Services.logins.getAllLogins().length, 0, "Should not have any logins yet");
+
+    yield* checkDoorhangerUsernamePassword("notifyu1", "notifyp1");
     clickDoorhangerButton(notif, REMEMBER_BUTTON);
   });
 
   let logins = Services.logins.getAllLogins();
   is(logins.length, 1, "Should only have 1 login");
   let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
   is(login.username, "notifyu1", "Check the username used on the new entry");
   is(login.password, "notifyp1", "Check the password used on the new entry");
@@ -266,16 +270,18 @@ add_task(function* test_changeUPLoginOnU
   info("Check for change-password popup, u+p login on u+p form. (not changed)");
   Services.logins.addLogin(login1);
 
   yield testSubmittingLoginForm("subtst_notifications_8.html", function*(fieldValues) {
     is(fieldValues.username, "notifyu1", "Checking submitted username");
     is(fieldValues.password, "pass2", "Checking submitted password");
     let notif = getCaptureDoorhanger("password-change");
     ok(notif, "got notification popup");
+
+    yield* checkDoorhangerUsernamePassword("notifyu1", "pass2");
     clickDoorhangerButton(notif, DONT_CHANGE_BUTTON);
   });
 
   let logins = Services.logins.getAllLogins();
   is(logins.length, 1, "Should only have 1 login");
   let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
   is(login.username, "notifyu1", "Check the username unchanged");
   is(login.password, "notifyp1", "Check the password unchanged");
@@ -288,17 +294,20 @@ add_task(function* test_changeUPLoginOnU
   info("Check for change-password popup, u+p login on u+p form.");
   Services.logins.addLogin(login1);
 
   yield testSubmittingLoginForm("subtst_notifications_8.html", function*(fieldValues) {
     is(fieldValues.username, "notifyu1", "Checking submitted username");
     is(fieldValues.password, "pass2", "Checking submitted password");
     let notif = getCaptureDoorhanger("password-change");
     ok(notif, "got notification popup");
+
+    yield* checkDoorhangerUsernamePassword("notifyu1", "pass2");
     clickDoorhangerButton(notif, CHANGE_BUTTON);
+
     ok(!getCaptureDoorhanger("password-change"), "popup should be gone");
   });
 
   let logins = Services.logins.getAllLogins();
   is(logins.length, 1, "Should only have 1 login");
   let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
   is(login.username, "notifyu1", "Check the username unchanged");
   is(login.password, "pass2", "Check the password changed");
@@ -316,17 +325,20 @@ add_task(function* test_changePLoginOnUP
   info("Check for change-password popup, p-only login on u+p form.");
   Services.logins.addLogin(login2);
 
   yield testSubmittingLoginForm("subtst_notifications_9.html", function*(fieldValues) {
     is(fieldValues.username, "", "Checking submitted username");
     is(fieldValues.password, "pass2", "Checking submitted password");
     let notif = getCaptureDoorhanger("password-change");
     ok(notif, "got notification popup");
+
+    yield* checkDoorhangerUsernamePassword("", "pass2");
     clickDoorhangerButton(notif, CHANGE_BUTTON);
+
     ok(!getCaptureDoorhanger("password-change"), "popup should be gone");
   });
 
   let logins = Services.logins.getAllLogins();
   is(logins.length, 1, "Should only have 1 login");
   let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
   is(login.username, "", "Check the username unchanged");
   is(login.password, "pass2", "Check the password changed");
@@ -338,17 +350,20 @@ add_task(function* test_changePLoginOnUP
 add_task(function* test_changePLoginOnPForm() {
   info("Check for change-password popup, p-only login on p-only form.");
 
   yield testSubmittingLoginForm("subtst_notifications_10.html", function*(fieldValues) {
     is(fieldValues.username, "null", "Checking submitted username");
     is(fieldValues.password, "notifyp1", "Checking submitted password");
     let notif = getCaptureDoorhanger("password-change");
     ok(notif, "got notification popup");
+
+    yield* checkDoorhangerUsernamePassword("", "notifyp1");
     clickDoorhangerButton(notif, CHANGE_BUTTON);
+
     ok(!getCaptureDoorhanger("password-change"), "popup should be gone");
   });
 
   let logins = Services.logins.getAllLogins();
   is(logins.length, 1, "Should only have 1 login");
   let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
   is(login.username, "", "Check the username unchanged");
   is(login.password, "notifyp1", "Check the password changed");
@@ -485,17 +500,20 @@ add_task(function* test_changeUPLoginOnP
   info("Check for change-password popup, u+p login on password update form.");
   Services.logins.addLogin(login1);
 
   yield testSubmittingLoginForm("subtst_notifications_change_p.html", function*(fieldValues) {
     is(fieldValues.username, "null", "Checking submitted username");
     is(fieldValues.password, "pass2", "Checking submitted password");
     let notif = getCaptureDoorhanger("password-change");
     ok(notif, "got notification popup");
+
+    yield* checkDoorhangerUsernamePassword("notifyu1", "pass2");
     clickDoorhangerButton(notif, CHANGE_BUTTON);
+
     ok(!getCaptureDoorhanger("password-change"), "popup should be gone");
   });
 
   let logins = Services.logins.getAllLogins();
   is(logins.length, 1, "Should only have 1 login");
   let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
   is(login.username, "notifyu1", "Check the username unchanged");
   is(login.password, "pass2", "Check the password changed");
@@ -517,17 +535,19 @@ add_task(function* test_recipeCaptureFie
     is(fieldValues.password, "notifyp1", "Checking submitted password");
     let notif = getCaptureDoorhanger("password-save");
     ok(notif, "got notification popup");
 
     // Sanity check, no logins should exist yet.
     let logins = Services.logins.getAllLogins();
     is(logins.length, 0, "Should not have any logins yet");
 
+    yield* checkDoorhangerUsernamePassword("notifyu1", "notifyp1");
     clickDoorhangerButton(notif, REMEMBER_BUTTON);
+
   }, "http://example.org"); // The recipe is for example.org
 
   let logins = Services.logins.getAllLogins();
   is(logins.length, 1, "Should only have 1 login");
   let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
   is(login.username, "notifyu1", "Check the username unchanged");
   is(login.password, "notifyp1", "Check the password unchanged");
   is(login.timesUsed, 1, "Check times used");
@@ -610,17 +630,20 @@ add_task(function* test_httpsUpgradeCapt
   let logins = Services.logins.getAllLogins();
   is(logins.length, 1, "Should have the HTTP login");
 
   yield testSubmittingLoginForm("subtst_notifications_8.html", function*(fieldValues) {
     is(fieldValues.username, "notifyu1", "Checking submitted username");
     is(fieldValues.password, "pass2", "Checking submitted password");
     let notif = getCaptureDoorhanger("password-change");
     ok(notif, "checking for a change popup");
+
+    yield* checkDoorhangerUsernamePassword("notifyu1", "pass2");
     clickDoorhangerButton(notif, CHANGE_BUTTON);
+
     ok(!getCaptureDoorhanger("password-change"), "popup should be gone");
   }, "https://example.com"); // This is HTTPS whereas the saved login is HTTP
 
   checkOnlyLoginWasUsedTwice({ justChanged: true });
   logins = Services.logins.getAllLogins();
   is(logins.length, 1, "Should only have 1 login still");
   let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
   is(login.hostname, "https://example.com", "Check the hostname is upgraded");
@@ -638,16 +661,18 @@ add_task(function* test_httpsUpgradeCapt
 
   yield testSubmittingLoginForm("subtst_notifications_1.html", function*(fieldValues) {
     is(fieldValues.username, "notifyu1", "Checking submitted username");
     is(fieldValues.password, "notifyp1", "Checking submitted password");
     let notif = getCaptureDoorhanger("password-save");
     ok(notif, "got notification popup");
 
     is(Services.logins.getAllLogins().length, 1, "Should only have the HTTPS login");
+
+    yield* checkDoorhangerUsernamePassword("notifyu1", "notifyp1");
     clickDoorhangerButton(notif, REMEMBER_BUTTON);
   });
 
   let logins = Services.logins.getAllLogins();
   is(logins.length, 2, "Should have both HTTP and HTTPS logins");
   for (let login of logins) {
     login = login.QueryInterface(Ci.nsILoginMetaInfo);
     is(login.username, "notifyu1", "Check the username used on the new entry");
--- a/toolkit/components/passwordmgr/test/browser/browser_capture_doorhanger_window_open.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_capture_doorhanger_window_open.js
@@ -28,27 +28,16 @@ function withTestTabUntilStorageChange(a
     ok(true, "loaded " + aPageFile);
     info("running test case task");
     yield* aTaskFn();
     info("waiting for storage change");
     yield storageChangedPromised;
   });
 }
 
-function* checkDoorhangerUsernamePassword(username, password) {
-  yield BrowserTestUtils.waitForCondition(() => {
-    return document.getElementById("password-notification-username").value == username;
-  }, "Wait for nsLoginManagerPrompter writeDataToUI()");
-  is(document.getElementById("password-notification-username").value, username,
-     "Check doorhanger username");
-  is(document.getElementById("password-notification-password").value, password,
-     "Check doorhanger password");
-}
-
-
 add_task(function* setup() {
   yield SimpleTest.promiseFocus(window);
 });
 
 add_task(function* test_saveChromeHiddenAutoClose() {
   let notifShownPromise = BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
   // query arguments are: username, password, features, auto-close (delimited by '|')
   let url = "subtst_notifications_11.html?notifyu1|notifyp1|" +
--- a/toolkit/components/passwordmgr/test/browser/head.js
+++ b/toolkit/components/passwordmgr/test/browser/head.js
@@ -113,9 +113,25 @@ function clickDoorhangerButton(aPopup, a
     ok(true, "Triggering main action");
     notification.button.doCommand();
   } else if (aButtonIndex <= aPopup.secondaryActions.length) {
     ok(true, "Triggering secondary action " + aButtonIndex);
     notification.childNodes[aButtonIndex].doCommand();
   }
 }
 
+/**
+ * Checks the doorhanger's username and password.
+ *
+ * @param {String} username The username.
+ * @param {String} password The password.
+ */
+function* checkDoorhangerUsernamePassword(username, password) {
+  yield BrowserTestUtils.waitForCondition(() => {
+    return document.getElementById("password-notification-username").value == username;
+  }, "Wait for nsLoginManagerPrompter writeDataToUI()");
+  is(document.getElementById("password-notification-username").value, username,
+     "Check doorhanger username");
+  is(document.getElementById("password-notification-password").value, password,
+     "Check doorhanger password");
+}
+
 // End popup notification (doorhanger) functions //
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -1716,16 +1716,40 @@
     "alert_emails": ["rbarnes@mozilla.com"],
     "bug_numbers": [1266571],
     "expires_in_version": "52",
     "kind": "enumerated",
     "n_values": 8,
     "releaseChannelCollection": "opt-out",
     "description": "Recorded once for each HTTP 401 response. The value records the type of authentication and the TLS-enabled status. (0=basic/clear, 1=basic/tls, 2=digest/clear, 3=digest/tls, 4=ntlm/clear, 5=ntlm/tls, 6=negotiate/clear, 7=negotiate/tls)"
   },
+  "TLS_EARLY_DATA_NEGOTIATED": {
+    "expires_in_version": "never",
+    "kind": "enumerated",
+    "n_values": 3,
+    "description": "TLS early data was available: 0 - not available, 1 - avaialable but not used, 2 - available and used.",
+    "alert_emails": ["necko@mozilla.com"],
+    "bug_numbers": [1296288]
+  },
+  "TLS_EARLY_DATA_ACCEPTED": {
+    "expires_in_version": "never",
+    "kind": "boolean",
+    "description": "TLS early data was used and it was accepted or rejected by the remote host.",
+    "alert_emails": ["necko@mozilla.com"],
+    "bug_numbers": [1296288]
+  },
+  "TLS_EARLY_DATA_BYTES_WRITTEN": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 60000,
+    "n_buckets": 100,
+    "description": "Amount of bytes sent using TLS early data.",
+    "alert_emails": ["necko@mozilla.com"],
+    "bug_numbers": [1296288]
+  },
   "SSL_HANDSHAKE_VERSION": {
     "alert_emails": ["seceng-telemetry@mozilla.com"],
     "bug_numbers": [1250568],
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 16,
     "description": "SSL Version (1=tls1, 2=tls1.1, 3=tls1.2, 4=tls1.3)"
   },
--- a/toolkit/components/thumbnails/test/browser_thumbnails_bg_no_cookies_stored.js
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_bg_no_cookies_stored.js
@@ -10,19 +10,20 @@ function* runTests() {
     xhr: bgTestPageURL({ setRedCookie: true})
   });
   ok(!thumbnailExists(url), "Thumbnail file should not exist before capture.");
   yield bgCapture(url);
   ok(thumbnailExists(url), "Thumbnail file should exist after capture.");
   removeThumbnail(url);
   // now load it up in a browser - it should *not* be red, otherwise the
   // cookie above was saved.
-  let tab = gBrowser.loadOneTab(url, { inBackground: false });
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
   let browser = tab.linkedBrowser;
-  yield whenLoaded(browser);
 
   // The root element of the page shouldn't be red.
-  let redStr = "rgb(255, 0, 0)";
-  isnot(browser.contentDocument.documentElement.style.backgroundColor,
-        redStr,
-        "The page shouldn't be red.");
+  yield ContentTask.spawn(browser, null, function() {
+    Assert.notEqual(content.document.documentElement.style.backgroundColor,
+                    "rgb(255, 0, 0)",
+                    "The page shouldn't be red.");
+  });
+
   gBrowser.removeTab(tab);
 }
--- a/widget/windows/IMMHandler.cpp
+++ b/widget/windows/IMMHandler.cpp
@@ -1465,23 +1465,24 @@ IMMHandler::HandleComposition(nsWindow* 
       MOZ_LOG(gIMMLog, LogLevel::Info,
         ("HandleComposition, GCS_COMPCLAUSE, clauseArrayLength=%ld but "
          "clauseArrayLength2=%ld",
          clauseArrayLength, clauseArrayLength2));
       if (clauseArrayLength > clauseArrayLength2)
         clauseArrayLength = clauseArrayLength2;
     }
 
-    if (useA_API) {
+    if (useA_API && clauseArrayLength > 0) {
       // Convert each values of sIMECompClauseArray. The values mean offset of
       // the clauses in ANSI string. But we need the values in Unicode string.
       nsAutoCString compANSIStr;
       if (ConvertToANSIString(mCompositionString, GetKeyboardCodePage(),
                               compANSIStr)) {
         uint32_t maxlen = compANSIStr.Length();
+        mClauseArray.SetLength(clauseArrayLength);
         mClauseArray[0] = 0; // first value must be 0
         for (int32_t i = 1; i < clauseArrayLength; i++) {
           uint32_t len = std::min(mClauseArray[i], maxlen);
           mClauseArray[i] = ::MultiByteToWideChar(GetKeyboardCodePage(), 
                                                   MB_PRECOMPOSED,
                                                   (LPCSTR)compANSIStr.get(),
                                                   len, nullptr, 0);
         }
@@ -1989,16 +1990,22 @@ IMMHandler::DispatchCompositionChangeEve
         MOZ_LOG(gIMMLog, LogLevel::Info,
           ("DispatchCompositionChangeEvent, mClauseArray[%ld]=%lu. "
            "This is larger than mCompositionString.Length()=%lu",
            i + 1, current, mCompositionString.Length()));
         current = int32_t(mCompositionString.Length());
       }
 
       uint32_t length = current - lastOffset;
+      if (NS_WARN_IF(lastOffset >= mAttributeArray.Length())) {
+        MOZ_LOG(gIMMLog, LogLevel::Error,
+          ("DispatchCompositionChangeEvent, FAILED due to invalid data of "
+            "mClauseArray or mAttributeArray"));
+        return;
+      }
       TextRangeType textRangeType =
         PlatformToNSAttr(mAttributeArray[lastOffset]);
       rv = dispatcher->AppendClauseToPendingComposition(length, textRangeType);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         MOZ_LOG(gIMMLog, LogLevel::Error,
           ("DispatchCompositionChangeEvent, FAILED due to"
            "TextEventDispatcher::AppendClauseToPendingComposition() failure"));
         return;
--- a/xpcom/ds/nsExpirationTracker.h
+++ b/xpcom/ds/nsExpirationTracker.h
@@ -88,17 +88,17 @@ public:
     , mInAgeOneGeneration(false)
     , mName(aName)
   {
     static_assert(K >= 2 && K <= nsExpirationState::NOT_TRACKED,
                   "Unsupported number of generations (must be 2 <= K <= 15)");
     mObserver = new ExpirationTrackerObserver();
     mObserver->Init(this);
   }
-  ~nsExpirationTracker()
+  virtual ~nsExpirationTracker()
   {
     if (mTimer) {
       mTimer->Cancel();
     }
     mObserver->Destroy();
   }
 
   /**
--- a/xpcom/tests/gtest/TestStateWatching.cpp
+++ b/xpcom/tests/gtest/TestStateWatching.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "gtest/gtest.h"
+#include "mozilla/SharedThreadPool.h"
 #include "mozilla/StateWatching.h"
 #include "mozilla/TaskQueue.h"
 #include "nsISupportsImpl.h"
 #include "VideoUtils.h"
 
 namespace TestStateWatching {
 
 using namespace mozilla;
--- a/xpcom/threads/AbstractThread.cpp
+++ b/xpcom/threads/AbstractThread.cpp
@@ -65,17 +65,16 @@ public:
 
   virtual bool IsCurrentThreadIn() override
   {
     // Compare NSPR threads so that this works after shutdown when
     // NS_GetCurrentThread starts returning null.
     PRThread* thread = nullptr;
     mTarget->GetPRThread(&thread);
     bool in = PR_GetCurrentThread() == thread;
-    MOZ_ASSERT(in == (GetCurrent() == this));
     return in;
   }
 
   void FireTailDispatcher()
   {
     MOZ_DIAGNOSTIC_ASSERT(mTailDispatcher.isSome());
     mTailDispatcher.ref().DrainDirectTasks();
     mTailDispatcher.reset();
--- a/xpcom/threads/SharedThreadPool.h
+++ b/xpcom/threads/SharedThreadPool.h
@@ -53,17 +53,20 @@ public:
   NS_IMETHOD_(MozExternalRefCountType) Release(void) override;
 
   // Forward behaviour to wrapped thread pool implementation.
   NS_FORWARD_SAFE_NSITHREADPOOL(mPool);
 
   // Call this when dispatching from an event on the same
   // threadpool that is about to complete. We should not create a new thread
   // in that case since a thread is about to become idle.
-  nsresult TailDispatch(nsIRunnable *event) { return Dispatch(event, NS_DISPATCH_TAIL); }
+  nsresult DispatchFromEndOfTaskInThisPool(nsIRunnable *event)
+  {
+    return Dispatch(event, NS_DISPATCH_AT_END);
+  }
 
   NS_IMETHOD DispatchFromScript(nsIRunnable *event, uint32_t flags) override {
       return Dispatch(event, flags);
   }
 
   NS_IMETHOD Dispatch(already_AddRefed<nsIRunnable> event, uint32_t flags) override
     { return !mEventTarget ? NS_ERROR_NULL_POINTER : mEventTarget->Dispatch(Move(event), flags); }
 
--- a/xpcom/threads/TaskQueue.cpp
+++ b/xpcom/threads/TaskQueue.cpp
@@ -1,25 +1,76 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "mozilla/TaskQueue.h"
 
+#include "nsIEventTarget.h"
 #include "nsThreadUtils.h"
-#include "mozilla/SharedThreadPool.h"
 
 namespace mozilla {
 
-TaskQueue::TaskQueue(already_AddRefed<SharedThreadPool> aPool,
-                               bool aRequireTailDispatch)
+class TaskQueue::EventTargetWrapper final : public nsIEventTarget
+{
+  RefPtr<TaskQueue> mTaskQueue;
+
+  ~EventTargetWrapper()
+  {
+  }
+
+public:
+  explicit EventTargetWrapper(TaskQueue* aTaskQueue)
+    : mTaskQueue(aTaskQueue)
+  {
+    MOZ_ASSERT(mTaskQueue);
+  }
+
+  NS_IMETHOD
+  DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags) override
+  {
+    nsCOMPtr<nsIRunnable> ref = aEvent;
+    return Dispatch(ref.forget(), aFlags);
+  }
+
+  NS_IMETHOD
+  Dispatch(already_AddRefed<nsIRunnable> aEvent, uint32_t aFlags) override
+  {
+    nsCOMPtr<nsIRunnable> runnable = aEvent;
+    MonitorAutoLock mon(mTaskQueue->mQueueMonitor);
+    return mTaskQueue->DispatchLocked(/* passed by ref */runnable,
+                                      AbortIfFlushing,
+                                      DontAssertDispatchSuccess,
+                                      NormalDispatch);
+  }
+
+  NS_IMETHOD
+  DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t aFlags) override
+  {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  NS_IMETHOD
+  IsOnCurrentThread(bool* aResult) override
+  {
+    *aResult = mTaskQueue->IsCurrentThreadIn();
+    return NS_OK;
+  }
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+};
+
+NS_IMPL_ISUPPORTS(TaskQueue::EventTargetWrapper, nsIEventTarget)
+
+TaskQueue::TaskQueue(already_AddRefed<nsIEventTarget> aTarget,
+                     bool aRequireTailDispatch)
   : AbstractThread(aRequireTailDispatch)
-  , mPool(aPool)
+  , mTarget(aTarget)
   , mQueueMonitor("TaskQueue::Queue")
   , mTailDispatcher(nullptr)
   , mIsRunning(false)
   , mIsShutdown(false)
   , mIsFlushing(false)
 {
   MOZ_COUNT_CTOR(TaskQueue);
 }
@@ -59,17 +110,17 @@ TaskQueue::DispatchLocked(nsCOMPtr<nsIRu
   if (mIsShutdown) {
     return NS_ERROR_FAILURE;
   }
   mTasks.push(aRunnable.forget());
   if (mIsRunning) {
     return NS_OK;
   }
   RefPtr<nsIRunnable> runner(new Runner(this));
-  nsresult rv = mPool->Dispatch(runner.forget(), NS_DISPATCH_NORMAL);
+  nsresult rv = mTarget->Dispatch(runner.forget(), NS_DISPATCH_NORMAL);
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to dispatch runnable to run TaskQueue");
     return rv;
   }
   mIsRunning = true;
 
   return NS_OK;
 }
@@ -131,24 +182,37 @@ TaskQueue::BeginShutdown()
 
 bool
 TaskQueue::IsEmpty()
 {
   MonitorAutoLock mon(mQueueMonitor);
   return mTasks.empty();
 }
 
+uint32_t
+TaskQueue::ImpreciseLengthForHeuristics()
+{
+  MonitorAutoLock mon(mQueueMonitor);
+  return mTasks.size();
+}
+
 bool
 TaskQueue::IsCurrentThreadIn()
 {
   bool in = NS_GetCurrentThread() == mRunningThread;
-  MOZ_ASSERT(in == (GetCurrent() == this));
   return in;
 }
 
+already_AddRefed<nsIEventTarget>
+TaskQueue::WrapAsEventTarget()
+{
+  nsCOMPtr<nsIEventTarget> ref = new EventTargetWrapper(this);
+  return ref.forget();
+}
+
 nsresult
 TaskQueue::Runner::Run()
 {
   RefPtr<nsIRunnable> event;
   {
     MonitorAutoLock mon(mQueue->mQueueMonitor);
     MOZ_ASSERT(mQueue->mIsRunning);
     if (mQueue->mTasks.size() == 0) {
@@ -186,21 +250,21 @@ TaskQueue::Runner::Run()
       mQueue->mIsRunning = false;
       mQueue->MaybeResolveShutdown();
       mon.NotifyAll();
       return NS_OK;
     }
   }
 
   // There's at least one more event that we can run. Dispatch this Runner
-  // to the thread pool again to ensure it runs again. Note that we don't just
-  // run in a loop here so that we don't hog the thread pool. This means we may
+  // to the target again to ensure it runs again. Note that we don't just
+  // run in a loop here so that we don't hog the target. This means we may
   // run on another thread next time, but we rely on the memory fences from
   // mQueueMonitor for thread safety of non-threadsafe tasks.
-  nsresult rv = mQueue->mPool->TailDispatch(this);
+  nsresult rv = mQueue->mTarget->Dispatch(this, NS_DISPATCH_AT_END);
   if (NS_FAILED(rv)) {
     // Failed to dispatch, shutdown!
     MonitorAutoLock mon(mQueue->mQueueMonitor);
     mQueue->mIsRunning = false;
     mQueue->mIsShutdown = true;
     mQueue->MaybeResolveShutdown();
     mon.NotifyAll();
   }
--- a/xpcom/threads/TaskQueue.h
+++ b/xpcom/threads/TaskQueue.h
@@ -10,35 +10,53 @@
 #include "mozilla/Monitor.h"
 #include "mozilla/MozPromise.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/TaskDispatcher.h"
 #include "mozilla/Unused.h"
 
 #include <queue>
 
-#include "mozilla/SharedThreadPool.h"
 #include "nsThreadUtils.h"
 
+class nsIEventTarget;
 class nsIRunnable;
 
 namespace mozilla {
 
-class SharedThreadPool;
-
 typedef MozPromise<bool, bool, false> ShutdownPromise;
 
-// Abstracts executing runnables in order in a thread pool. The runnables
-// dispatched to the TaskQueue will be executed in the order in which
+// Abstracts executing runnables in order on an arbitrary event target. The
+// runnables dispatched to the TaskQueue will be executed in the order in which
 // they're received, and are guaranteed to not be executed concurrently.
 // They may be executed on different threads, and a memory barrier is used
 // to make this threadsafe for objects that aren't already threadsafe.
-class TaskQueue : public AbstractThread {
+//
+// Note, since a TaskQueue can also be converted to an nsIEventTarget using
+// WrapAsEventTarget() its possible to construct a hierarchy of TaskQueues.
+// Consider these three TaskQueues:
+//
+//  TQ1 dispatches to the main thread
+//  TQ2 dispatches to TQ1
+//  TQ3 dispatches to TQ1
+//
+// This ensures there is only ever a single runnable from the entire chain on
+// the main thread.  It also ensures that TQ2 and TQ3 only have a single runnable
+// in TQ1 at any time.
+//
+// This arrangement lets you prioritize work by dispatching runnables directly
+// to TQ1.  You can issue many runnables for important work.  Meanwhile the TQ2
+// and TQ3 work will always execute at most one runnable and then yield.
+class TaskQueue : public AbstractThread
+{
+  class EventTargetWrapper;
+
 public:
-  explicit TaskQueue(already_AddRefed<SharedThreadPool> aPool, bool aSupportsTailDispatch = false);
+  explicit TaskQueue(already_AddRefed<nsIEventTarget> aTarget,
+                     bool aSupportsTailDispatch = false);
 
   TaskDispatcher& TailDispatcher() override;
 
   TaskQueue* AsTaskQueue() override { return this; }
 
   void Dispatch(already_AddRefed<nsIRunnable> aRunnable,
                 DispatchFailureHandling aFailureHandling = AssertDispatchSuccess,
                 DispatchReason aReason = NormalDispatch) override
@@ -54,34 +72,39 @@ public:
     // dispatch failure, it will be deleted here outside the lock. We do so
     // since the destructor of the runnable might access TaskQueue and result
     // in deadlocks.
   }
 
   // Puts the queue in a shutdown state and returns immediately. The queue will
   // remain alive at least until all the events are drained, because the Runners
   // hold a strong reference to the task queue, and one of them is always held
-  // by the threadpool event queue when the task queue is non-empty.
+  // by the target event queue when the task queue is non-empty.
   //
   // The returned promise is resolved when the queue goes empty.
   RefPtr<ShutdownPromise> BeginShutdown();
 
   // Blocks until all task finish executing.
   void AwaitIdle();
 
   // Blocks until the queue is flagged for shutdown and all tasks have finished
   // executing.
   void AwaitShutdownAndIdle();
 
   bool IsEmpty();
+  uint32_t ImpreciseLengthForHeuristics();
 
   // Returns true if the current thread is currently running a Runnable in
   // the task queue.
   bool IsCurrentThreadIn() override;
 
+  // Create a new nsIEventTarget wrapper object that dispatches to this
+  // TaskQueue.
+  already_AddRefed<nsIEventTarget> WrapAsEventTarget();
+
 protected:
   virtual ~TaskQueue();
 
 
   // Blocks until all task finish executing. Called internally by methods
   // that need to wait until the task queue is idle.
   // mQueueMonitor must be held.
   void AwaitIdleLocked();
@@ -93,21 +116,21 @@ protected:
                           DispatchFailureHandling aFailureHandling,
                           DispatchReason aReason = NormalDispatch);
 
   void MaybeResolveShutdown()
   {
     mQueueMonitor.AssertCurrentThreadOwns();
     if (mIsShutdown && !mIsRunning) {
       mShutdownPromise.ResolveIfExists(true, __func__);
-      mPool = nullptr;
+      mTarget = nullptr;
     }
   }
 
-  RefPtr<SharedThreadPool> mPool;
+  nsCOMPtr<nsIEventTarget> mTarget;
 
   // Monitor that protects the queue and mIsRunning;
   Monitor mQueueMonitor;
 
   // Queue of tasks to run.
   std::queue<nsCOMPtr<nsIRunnable>> mTasks;
 
   // The thread currently running the task queue. We store a reference
@@ -121,47 +144,49 @@ protected:
   Atomic<nsIThread*> mRunningThread;
 
   // RAII class that gets instantiated for each dispatched task.
   class AutoTaskGuard : public AutoTaskDispatcher
   {
   public:
     explicit AutoTaskGuard(TaskQueue* aQueue)
       : AutoTaskDispatcher(/* aIsTailDispatcher = */ true), mQueue(aQueue)
+      , mLastCurrentThread(nullptr)
     {
       // NB: We don't hold the lock to aQueue here. Don't do anything that
       // might require it.
       MOZ_ASSERT(!mQueue->mTailDispatcher);
       mQueue->mTailDispatcher = this;
 
-      MOZ_ASSERT(sCurrentThreadTLS.get() == nullptr);
+      mLastCurrentThread = sCurrentThreadTLS.get();
       sCurrentThreadTLS.set(aQueue);
 
       MOZ_ASSERT(mQueue->mRunningThread == nullptr);
       mQueue->mRunningThread = NS_GetCurrentThread();
     }
 
     ~AutoTaskGuard()
     {
       DrainDirectTasks();
 
       MOZ_ASSERT(mQueue->mRunningThread == NS_GetCurrentThread());
       mQueue->mRunningThread = nullptr;
 
-      sCurrentThreadTLS.set(nullptr);
+      sCurrentThreadTLS.set(mLastCurrentThread);
       mQueue->mTailDispatcher = nullptr;
     }
 
   private:
   TaskQueue* mQueue;
+  AbstractThread* mLastCurrentThread;
   };
 
   TaskDispatcher* mTailDispatcher;
 
-  // True if we've dispatched an event to the pool to execute events from
+  // True if we've dispatched an event to the target to execute events from
   // the queue.
   bool mIsRunning;
 
   // True if we've started our shutdown process.
   bool mIsShutdown;
   MozPromiseHolder<ShutdownPromise> mShutdownPromise;
 
   // True if we're flushing; we reject new tasks if we're flushing.
--- a/xpcom/threads/nsIEventTarget.idl
+++ b/xpcom/threads/nsIEventTarget.idl
@@ -45,17 +45,17 @@ interface nsIEventTarget : nsISupports
    * that was dispatched to the same event target, and that event is about to
    * finish.
    *
    * A thread pool can use this as an optimization hint to not spin up
    * another thread, since the current thread is about to become idle.
    *
    * These events are always async.
    */
-  const unsigned long DISPATCH_TAIL = 2;
+  const unsigned long DISPATCH_AT_END = 2;
 
   /**
    * Check to see if this event target is associated with the current thread.
    *
    * @returns
    *   A boolean value that if "true" indicates that events dispatched to this
    *   event target will run on the current thread (i.e., the thread calling
    *   this method).
@@ -118,10 +118,10 @@ interface nsIEventTarget : nsISupports
    */
   [noscript] void delayedDispatch(in alreadyAddRefed_nsIRunnable event, in unsigned long delay);
 };
 
 %{C++
 // convenient aliases:
 #define NS_DISPATCH_NORMAL nsIEventTarget::DISPATCH_NORMAL
 #define NS_DISPATCH_SYNC   nsIEventTarget::DISPATCH_SYNC
-#define NS_DISPATCH_TAIL   nsIEventTarget::DISPATCH_TAIL
+#define NS_DISPATCH_AT_END nsIEventTarget::DISPATCH_AT_END
 %}
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -743,17 +743,18 @@ nsThread::DispatchInternal(already_AddRe
 
     // Allows waiting; ensure no locks are held that would deadlock us!
     while (wrapper->IsPending()) {
       NS_ProcessNextEvent(thread, true);
     }
     return NS_OK;
   }
 
-  NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL, "unexpected dispatch flags");
+  NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL ||
+               aFlags == NS_DISPATCH_AT_END, "unexpected dispatch flags");
   return PutEvent(event.take(), aTarget);
 }
 
 //-----------------------------------------------------------------------------
 // nsIEventTarget
 
 NS_IMETHODIMP
 nsThread::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
--- a/xpcom/threads/nsThreadPool.cpp
+++ b/xpcom/threads/nsThreadPool.cpp
@@ -81,17 +81,17 @@ nsThreadPool::PutEvent(already_AddRefed<
       return NS_ERROR_NOT_AVAILABLE;
     }
     LOG(("THRD-P(%p) put [%d %d %d]\n", this, mIdleCount, mThreads.Count(),
          mThreadLimit));
     MOZ_ASSERT(mIdleCount <= (uint32_t)mThreads.Count(), "oops");
 
     // Make sure we have a thread to service this event.
     if (mThreads.Count() < (int32_t)mThreadLimit &&
-        !(aFlags & NS_DISPATCH_TAIL) &&
+        !(aFlags & NS_DISPATCH_AT_END) &&
         // Spawn a new thread if we don't have enough idle threads to serve
         // pending events immediately.
         mEvents.Count(lock) >= mIdleCount) {
       spawnThread = true;
     }
 
     mEvents.PutEvent(Move(aEvent), lock);
     stackSize = mStackSize;
@@ -265,17 +265,17 @@ nsThreadPool::Dispatch(already_AddRefed<
       new nsThreadSyncDispatch(thread, Move(aEvent));
     PutEvent(wrapper);
 
     while (wrapper->IsPending()) {
       NS_ProcessNextEvent(thread);
     }
   } else {
     NS_ASSERTION(aFlags == NS_DISPATCH_NORMAL ||
-                 aFlags == NS_DISPATCH_TAIL, "unexpected dispatch flags");
+                 aFlags == NS_DISPATCH_AT_END, "unexpected dispatch flags");
     PutEvent(Move(aEvent), aFlags);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsThreadPool::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
 {