Merge mozilla-central to autoland
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 14 Sep 2016 12:17:20 +0200
changeset 355162 b9c4a0402a0a90ff4ef6223fcd2fa92422f8bf44
parent 355161 bd37ea2f83d30f87940378c47f7f3cdd61d675bc (current diff)
parent 355158 501e27643a529ce2844924cd47f01ef3150fa0ba (diff)
child 355163 820356f508df8c83436f8ae3de1a3cbbd28b19bf
push id6570
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:26:13 +0000
treeherdermozilla-beta@f455459b2ae5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone51.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge 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)
 {