Merge m-i to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Fri, 11 Dec 2015 21:20:03 -0800
changeset 310378 694a530e236564a3d9f4b9fd7d9e96ae782b3a7b
parent 310323 d8ce27c85590380ef025bb4ed66e564a4dff9bff (current diff)
parent 310377 412e32773fbf36ba8f6ccca4247890204bf7880a (diff)
child 310379 eeaa9b452b6572829b6ca276b9d1de1c8b045d2d
child 310385 6c24ecacc1a9c5e9c054c074269f808d587ee29c
child 310387 bb024874e231243244f641a0d0ab579a0d31a8b5
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone45.0a1
first release with
nightly linux32
694a530e2365 / 45.0a1 / 20151212030222 / files
nightly linux64
694a530e2365 / 45.0a1 / 20151212030222 / files
nightly mac
694a530e2365 / 45.0a1 / 20151212030222 / files
nightly win32
694a530e2365 / 45.0a1 / 20151212030222 / files
nightly win64
694a530e2365 / 45.0a1 / 20151212030222 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-i to m-c, a=merge
js/src/jit-test/lib/class.js
testing/web-platform/mozilla/meta/service-workers/service-worker/update-after-navigation-fetch-event.https.html.ini
--- a/accessible/windows/msaa/Compatibility.h
+++ b/accessible/windows/msaa/Compatibility.h
@@ -34,36 +34,16 @@ public:
    */
   static bool IsWE() { return !!(sConsumers & WE); }
 
   /**
    * Return true if Dolphin mode is enabled.
    */
   static bool IsDolphin() { return !!(sConsumers & DOLPHIN); }
 
-  /**
-   * Return true if we should disable e10s due to a detected
-   * accessibility client.
-   */
-  static bool IsBlacklistedForE10S()
-  {
-    // We currently blacklist everything except UNKNOWN and UIAUTOMATION
-    return !!(sConsumers &
-              (NVDA     |
-               JAWS     |
-               OLDJAWS  |
-               WE       |
-               DOLPHIN  |
-               SEROTEK  |
-               COBRA    |
-               ZOOMTEXT |
-               KAZAGURU |
-               YOUDAO));
-  }
-
 private:
   Compatibility();
   Compatibility(const Compatibility&);
   Compatibility& operator = (const Compatibility&);
 
   /**
    * Initialize compatibility mode. Called by platform (see Platform.h) during
    * accessibility initialization.
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -629,16 +629,19 @@ pref("network.protocol-handler.expose.ne
 pref("network.protocol-handler.expose.snews", false);
 pref("network.protocol-handler.expose.nntp", false);
 
 pref("accessibility.typeaheadfind", false);
 pref("accessibility.typeaheadfind.timeout", 5000);
 pref("accessibility.typeaheadfind.linksonly", false);
 pref("accessibility.typeaheadfind.flashBar", 1);
 
+// Tracks when accessibility is loaded into the previous session.
+pref("accessibility.loadedInLastSession", false);
+
 pref("plugins.update.url", "https://www.mozilla.org/%LOCALE%/plugincheck/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=plugincheck-update");
 pref("plugins.update.notifyUser", false);
 
 pref("plugins.click_to_play", true);
 pref("plugins.testmode", false);
 
 pref("plugin.default.state", 1);
 
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_pageAction_icon.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_pageAction_icon.js
@@ -320,17 +320,17 @@ add_task(function* testDefaultDetails() 
     let pageActionImage = document.getElementById(pageActionId);
     image = pageActionImage.src;
 
     ok(expectedURL.test(image), `page action image ${image} matches ${expectedURL}`);
 
     yield extension.unload();
 
     let node = document.getElementById(pageActionId);
-    is(node, undefined, "pageAction image removed from document");
+    is(node, null, "pageAction image removed from document");
   }
 });
 
 
 // Check that attempts to load a privileged URL as an icon image fail.
 add_task(function* testSecureURLsDenied() {
   // Test URLs passed to setIcon.
 
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_popup.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_popup.js
@@ -124,22 +124,22 @@ add_task(function* testPageActionPopup()
 
   extension.onMessage("next-test", Task.async(function* () {
     let panel = document.getElementById(panelId);
     if (panel) {
       yield promisePopupShown(panel);
       panel.hidePopup();
 
       panel = document.getElementById(panelId);
-      is(panel, undefined, "panel successfully removed from document after hiding");
+      is(panel, null, "panel successfully removed from document after hiding");
     }
 
     extension.sendMessage("next-test");
   }));
 
 
   yield Promise.all([extension.startup(), extension.awaitFinish("browseraction-tests-done")]);
 
   yield extension.unload();
 
   let panel = document.getElementById(panelId);
-  is(panel, undefined, "browserAction panel removed from document");
+  is(panel, null, "browserAction panel removed from document");
 });
--- a/browser/components/extensions/test/browser/browser_ext_pageAction_context.js
+++ b/browser/components/extensions/test/browser/browser_ext_pageAction_context.js
@@ -221,18 +221,18 @@ add_task(function* testTabSwitchContext(
 
   yield extension.startup();
 
   yield awaitFinish;
 
   yield extension.unload();
 
   let node = document.getElementById(pageActionId);
-  is(node, undefined, "pageAction image removed from document");
+  is(node, null, "pageAction image removed from document");
 
   currentWindow = null;
   for (let win of windows.splice(0)) {
     node = win.document.getElementById(pageActionId);
-    is(node, undefined, "pageAction image removed from second document");
+    is(node, null, "pageAction image removed from second document");
 
     yield BrowserTestUtils.closeWindow(win);
   }
 });
--- a/browser/components/extensions/test/browser/browser_ext_pageAction_popup.js
+++ b/browser/components/extensions/test/browser/browser_ext_pageAction_popup.js
@@ -132,33 +132,33 @@ add_task(function* testPageActionPopup()
 
   extension.onMessage("next-test", Task.async(function* () {
     let panel = document.getElementById(panelId);
     if (panel) {
       yield promisePopupShown(panel);
       panel.hidePopup();
 
       panel = document.getElementById(panelId);
-      is(panel, undefined, "panel successfully removed from document after hiding");
+      is(panel, null, "panel successfully removed from document after hiding");
     }
 
     extension.sendMessage("next-test");
   }));
 
 
   yield extension.startup();
   yield extension.awaitFinish("pageaction-tests-done");
 
   yield extension.unload();
 
   let node = document.getElementById(pageActionId);
-  is(node, undefined, "pageAction image removed from document");
+  is(node, null, "pageAction image removed from document");
 
   let panel = document.getElementById(panelId);
-  is(panel, undefined, "pageAction panel removed from document");
+  is(panel, null, "pageAction panel removed from document");
 });
 
 
 add_task(function* testPageActionSecurity() {
   const URL = "chrome://browser/content/browser.xul";
 
   let messages = [/Access to restricted URI denied/,
                   /Access to restricted URI denied/];
@@ -192,13 +192,13 @@ add_task(function* testPageActionSecurit
 
   yield clickBrowserAction(extension);
   yield clickPageAction(extension);
 
   yield extension.unload();
 
   let pageActionId = makeWidgetId(extension.id) + "-page-action";
   let node = document.getElementById(pageActionId);
-  is(node, undefined, "pageAction image removed from document");
+  is(node, null, "pageAction image removed from document");
 
   SimpleTest.endMonitorConsole();
   yield waitForConsole;
 });
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -1328,21 +1328,24 @@ BrowserGlue.prototype = {
 
       if (willPrompt) {
         Services.tm.mainThread.dispatch(function() {
           DefaultBrowserCheck.prompt(RecentWindow.getMostRecentBrowserWindow());
         }.bind(this), Ci.nsIThread.DISPATCH_NORMAL);
       }
     }
 
-#ifdef E10S_TESTING_ONLY
-    E10SUINotification.checkStatus();
-#else
-    E10SAccessibilityCheck.init();
-#endif
+    if (AppConstants.E10S_TESTING_ONLY) {
+      E10SUINotification.checkStatus();
+    }
+    if (AppConstants.XP_WIN) {
+      // Handles prompting to inform about incompatibilites when accessibility
+      // and e10s are active together.
+      E10SAccessibilityCheck.init();
+    }
   },
 
 #ifdef MOZ_DEV_EDITION
   _createExtraDefaultProfile: function () {
     // If Developer Edition is the only installed Firefox version and no other
     // profiles are present, create a second one for use by other versions.
     // This helps Firefox versions earlier than 35 avoid accidentally using the
     // unsuitable Developer Edition profile.
@@ -3080,46 +3083,32 @@ var DefaultBrowserCheck = {
 var E10SUINotification = {
   // Increase this number each time we want to roll out an
   // e10s testing period to Nightly users.
   CURRENT_NOTICE_COUNT: 4,
   CURRENT_PROMPT_PREF: "browser.displayedE10SPrompt.1",
   PREVIOUS_PROMPT_PREF: "browser.displayedE10SPrompt",
 
   checkStatus: function() {
-    let skipE10sChecks = false;
-    try {
-      let updateChannel = UpdateUtils.UpdateChannel;
-      let channelAuthorized = updateChannel == "nightly" || updateChannel == "aurora";
-
-      skipE10sChecks = !channelAuthorized ||
-                       Services.prefs.getBoolPref("browser.tabs.remote.disabled-for-a11y");
-    } catch(e) {}
-
-    if (skipE10sChecks) {
+    let updateChannel = UpdateUtils.UpdateChannel;
+    let channelAuthorized = updateChannel == "nightly" || updateChannel == "aurora";
+    if (!channelAuthorized) {
       return;
     }
 
     if (Services.appinfo.browserTabsRemoteAutostart) {
       let notice = 0;
       try {
         notice = Services.prefs.getIntPref("browser.displayedE10SNotice");
       } catch(e) {}
       let activationNoticeShown = notice >= this.CURRENT_NOTICE_COUNT;
 
       if (!activationNoticeShown) {
         this._showE10sActivatedNotice();
       }
-
-      // e10s doesn't work with accessibility, so we prompt to disable
-      // e10s if a11y is enabled, now or in the future.
-      Services.obs.addObserver(this, "a11y-init-or-shutdown", true);
-      if (Services.appinfo.accessibilityIsBlacklistedForE10S) {
-        this._showE10sAccessibilityWarning();
-      }
     } else {
       let displayFeedbackRequest = false;
       try {
         displayFeedbackRequest = Services.prefs.getBoolPref("browser.requestE10sFeedback");
       } catch (e) {}
 
       if (displayFeedbackRequest) {
         let win = RecentWindow.getMostRecentBrowserWindow();
@@ -3169,24 +3158,16 @@ var E10SUINotification = {
           }
         }, Ci.nsIThread.DISPATCH_NORMAL);
       }
     }
   },
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
 
-  observe: function(subject, topic, data) {
-    if (topic == "a11y-init-or-shutdown"
-        && data == "1" &&
-        Services.appinfo.accessibilityIsBlacklistedForE10S) {
-      this._showE10sAccessibilityWarning();
-    }
-  },
-
   _showE10sActivatedNotice: function() {
     let win = RecentWindow.getMostRecentBrowserWindow();
     if (!win)
       return;
 
     Services.prefs.setIntPref("browser.displayedE10SNotice", this.CURRENT_NOTICE_COUNT);
 
     let nb = win.document.getElementById("high-priority-global-notificationbox");
@@ -3257,164 +3238,128 @@ var E10SUINotification = {
 
     let doorhangerExtraContent = win.document.getElementById("enable-e10s-notification")
                                              .querySelector("popupnotificationcontent");
     for (let highlight of highlights) {
       let highlightLabel = win.document.createElement("label");
       highlightLabel.setAttribute("value", highlight);
       doorhangerExtraContent.appendChild(highlightLabel);
     }
+  }
+};
+#endif // E10S_TESTING_ONLY
+
+var E10SAccessibilityCheck = {
+  init: function() {
+    Services.obs.addObserver(this, "a11y-init-or-shutdown", true);
+    Services.obs.addObserver(this, "quit-application-granted", true);
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
+
+  get forcedOn() {
+    try {
+      return Services.prefs.getBoolPref("browser.tabs.remote.force-enable");
+    } catch (e) {}
+    return false;
+  },
+
+  observe: function(subject, topic, data) {
+    switch (topic) {
+      case "quit-application-granted":
+        // Tag the profile with a11y load state. We use this in nsAppRunner
+        // checks on the next start.
+        Services.prefs.setBoolPref("accessibility.loadedInLastSession",
+                                   Services.appinfo.accessibilityEnabled);
+        break;
+      case "a11y-init-or-shutdown":
+        if (data == "1") {
+          // Update this so users can check this while still running
+          Services.prefs.setBoolPref("accessibility.loadedInLastSession", true);
+          this._showE10sAccessibilityWarning();
+        }
+        break;
+    }
   },
 
   _warnedAboutAccessibility: false,
 
   _showE10sAccessibilityWarning: function() {
-    try {
-      if (!Services.prefs.getBoolPref("browser.tabs.remote.disabled-for-a11y")) {
-        // Only return if the pref exists and was set to false, but not
-        // if the pref didn't exist (which will throw).
-        return;
-      }
-    } catch (e) { }
-
-    Services.prefs.setBoolPref("browser.tabs.remote.disabled-for-a11y", true);
-
+    // We don't prompt about a11y incompat if e10s is off.
+    if (!Services.appinfo.browserTabsRemoteAutostart) {
+      return;
+    }
+
+    // If the user set the forced pref and it's true, ignore a11y init.
+    // If the pref doesn't exist or if it's false, prompt.
+    if (this.forcedOn) {
+      return;
+    }
+
+    // Only prompt once per session
     if (this._warnedAboutAccessibility) {
       return;
     }
     this._warnedAboutAccessibility = true;
 
     let win = RecentWindow.getMostRecentBrowserWindow();
+    let browser = win.gBrowser.selectedBrowser;
     if (!win) {
-      // Just restart immediately.
-      Services.startup.quit(Services.startup.eAttemptQuit | Services.startup.eRestart);
+      Services.console.logStringMessage("Accessibility support is partially disabled due to compatibility issues with new features.");
       return;
     }
 
-    let browser = win.gBrowser.selectedBrowser;
-
+    // We disable a11y for content and prompt on the chrome side letting
+    // a11y users know they need to disable e10s and restart.
     let promptMessage = win.gNavigatorBundle.getFormattedString(
-                          "e10s.accessibilityNotice.mainMessage",
+                          "e10s.accessibilityNotice.mainMessage2",
                           [gBrandBundle.GetStringFromName("brandShortName")]
                         );
-    let mainAction = {
-      label: win.gNavigatorBundle.getString("e10s.accessibilityNotice.disableAndRestart.label"),
-      accessKey: win.gNavigatorBundle.getString("e10s.accessibilityNotice.disableAndRestart.accesskey"),
-      callback: function () {
-        // Restart the app
-        let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
-        Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
-        if (cancelQuit.data)
-          return; // somebody canceled our quit request
-        Services.startup.quit(Services.startup.eAttemptQuit | Services.startup.eRestart);
+    let notification;
+    let restartCallback  = function() {
+      let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
+      Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
+      if (cancelQuit.data) {
+        return; // somebody canceled our quit request
       }
+      // Restart the browser
+      Services.startup.quit(Services.startup.eAttemptQuit | Services.startup.eRestart);
     };
-    let secondaryActions = [
-      {
-        label: win.gNavigatorBundle.getString("e10s.accessibilityNotice.dontDisable.label"),
-        accessKey: win.gNavigatorBundle.getString("e10s.accessibilityNotice.dontDisable.accesskey"),
-        callback: function () {
-          Services.prefs.setBoolPref("browser.tabs.remote.disabled-for-a11y", false);
-        }
-      }
-    ];
+    // main option: an Ok button, keeps running with content accessibility disabled
+    let mainAction = {
+      label: win.gNavigatorBundle.getString("e10s.accessibilityNotice.acceptButton.label"),
+      accessKey: win.gNavigatorBundle.getString("e10s.accessibilityNotice.acceptButton.accesskey"),
+      callback: function () {
+        // If the user invoked the button option remove the notification,
+        // otherwise keep the alert icon around in the address bar.
+        notification.remove();
+      },
+      dismiss: true
+    };
+    // secondary option: a restart now button. When we restart e10s will be disabled due to
+    // accessibility having been loaded in the previous session.
+    let secondaryActions = [{
+      label: win.gNavigatorBundle.getString("e10s.accessibilityNotice.enableAndRestart.label"),
+      accessKey: win.gNavigatorBundle.getString("e10s.accessibilityNotice.enableAndRestart.accesskey"),
+      callback: restartCallback,
+    }];
     let options = {
       popupIconURL: "chrome://browser/skin/e10s-64@2x.png",
-      learnMoreURL: "https://wiki.mozilla.org/Electrolysis",
-      persistWhileVisible: true
+      learnMoreURL: "https://support.mozilla.org/kb/accessibility-and-ppt",
+      persistWhileVisible: true,
+      hideNotNow: true,
     };
 
-    win.PopupNotifications.show(browser, "a11y_enabled_with_e10s", promptMessage, null, mainAction, secondaryActions, options);
+    notification =
+      win.PopupNotifications.show(browser, "a11y_enabled_with_e10s",
+                                  promptMessage, null, mainAction,
+                                  secondaryActions, options);
   },
 };
 
-#else // E10S_TESTING_ONLY
-
-var E10SAccessibilityCheck = {
-  init: function() {
-    Services.obs.addObserver(this, "a11y-init-or-shutdown", true);
-    if (Services.appinfo.accessibilityEnabled) {
-      this._showE10sAccessibilityWarning();
-    }
-  },
-
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
-
-  observe: function(subject, topic, data) {
-    if (topic == "a11y-init-or-shutdown"
-        && data == "1") {
-      this._showE10sAccessibilityWarning();
-    }
-  },
-
-  _warnedAboutAccessibility: false,
-
-  _showE10sAccessibilityWarning: function() {
-    try {
-      if (!Services.prefs.getBoolPref("browser.tabs.remote.disabled-for-a11y")) {
-        // Only return if the pref exists and was set to false, but not
-        // if the pref didn't exist (which will throw).
-        return;
-      }
-    } catch (e) { }
-
-    Services.prefs.setBoolPref("browser.tabs.remote.disabled-for-a11y", true);
-
-    if (this._warnedAboutAccessibility ||
-        !Services.appinfo.browserTabsRemoteAutostart) {
-      return;
-    }
-    this._warnedAboutAccessibility = true;
-
-    let win = RecentWindow.getMostRecentBrowserWindow();
-    if (!win) {
-      // Just restart immediately.
-      Services.startup.quit(Services.startup.eAttemptQuit | Services.startup.eRestart);
-      return;
-    }
-
-    let browser = win.gBrowser.selectedBrowser;
-
-    let promptMessage = win.gNavigatorBundle.getFormattedString(
-                          "e10s.accessibilityNotice.mainMessage",
-                          [gBrandBundle.GetStringFromName("brandShortName")]
-                        );
-    let mainAction = {
-      label: win.gNavigatorBundle.getString("e10s.accessibilityNotice.disableAndRestart.label"),
-      accessKey: win.gNavigatorBundle.getString("e10s.accessibilityNotice.disableAndRestart.accesskey"),
-      callback: function () {
-        // Restart the app
-        let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
-        Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
-        if (cancelQuit.data)
-          return; // somebody canceled our quit request
-        Services.startup.quit(Services.startup.eAttemptQuit | Services.startup.eRestart);
-      }
-    };
-    let secondaryActions = [
-      {
-        label: win.gNavigatorBundle.getString("e10s.accessibilityNotice.dontDisable.label"),
-        accessKey: win.gNavigatorBundle.getString("e10s.accessibilityNotice.dontDisable.accesskey"),
-        callback: function () {
-          Services.prefs.setBoolPref("browser.tabs.remote.disabled-for-a11y", false);
-        }
-      }
-    ];
-    let options = {
-      popupIconURL: "chrome://browser/skin/e10s-64@2x.png",
-      learnMoreURL: "https://wiki.mozilla.org/Electrolysis",
-      persistWhileVisible: true
-    };
-
-    win.PopupNotifications.show(browser, "a11y_enabled_with_e10s", promptMessage, null, mainAction, secondaryActions, options);
-  },
-};
-
-#endif // E10S_TESTING_ONLY
-
 var components = [BrowserGlue, ContentPermissionPrompt, AboutNewTabService];
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
 
 
 // Listen for UITour messages.
 // Do it here instead of the UITour module itself so that the UITour module is lazy loaded
 // when the first message is received.
 var globalMM = Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -1,16 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 Components.utils.import("resource://gre/modules/Downloads.jsm");
 Components.utils.import("resource://gre/modules/FileUtils.jsm");
 Components.utils.import("resource://gre/modules/Task.jsm");
 Components.utils.import("resource:///modules/TransientPrefs.jsm");
+#ifdef E10S_TESTING_ONLY
+XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
+                                  "resource://gre/modules/UpdateUtils.jsm");
+#endif
 
 var gMainPane = {
   /**
    * Initialization of this.
    */
   init: function ()
   {
     function setEventListener(aId, aEventType, aCallback)
@@ -92,17 +96,23 @@ var gMainPane = {
           // we want to allow the user to un-toggle the pref.
           // We're relying on the nsAppRunner code only specifying "Safe mode"
           // as the reason if the pref is otherwise enabled, and there are no
           // other reasons to block e10s.
           // Update the checkbox to reflect the pref state.
           e10sCheckbox.checked = true;
         } else {
           e10sCheckbox.disabled = true;
-          e10sCheckbox.label += " (disabled: " + e10sBlockedReason.data + ")";
+          let updateChannel = UpdateUtils.UpdateChannel;
+          // only add this label on developer channels
+          if (updateChannel == "default" ||
+              updateChannel == "nightly" ||
+              updateChannel == "aurora") {
+            e10sCheckbox.label += " (disabled: " + e10sBlockedReason.data + ")";
+          }
         }
       }
     }
 
     // If E10S is blocked because of safe mode, we want the checkbox to be
     // enabled
 #endif
 
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -745,39 +745,37 @@ appMenuRemoteTabs.mobilePromo.ios = Fire
 #                    e10s.offerPopup.enableAndRestart.label
 #                    e10s.offerPopup.enableAndRestart.accesskey
 #                    e10s.offerPopup.noThanks.label
 #                    e10s.offerPopup.noThanks.accesskey
 #                    e10s.postActivationInfobar.message
 #                    e10s.postActivationInfobar.learnMore.label
 #                    e10s.postActivationInfobar.learnMore.accesskey
 #                    e10s.accessibilityNotice.mainMessage
-#                    e10s.accessibilityNotice.disableAndRestart.label
-#                    e10s.accessibilityNotice.disableAndRestart.accesskey
-#                    e10s.accessibilityNotice.dontDisable.label
-#                    e10s.accessibilityNotice.dontDisable.accesskey):
+#                    e10s.accessibilityNotice.enableAndRestart.label
+#                    e10s.accessibilityNotice.enableAndRestart.accesskey
 # These strings are related to the messages we display to offer e10s (Multi-process) to users
 # on the pre-release channels. They won't be used in release but they will likely be used in
 # beta starting from version 41, so it's still useful to have these strings properly localized.
 # %S is brandShortName
 e10s.offerPopup.mainMessage = Multi-process is coming soon to %S. You can start using it now to get early access to some of the benefits:
 e10s.offerPopup.highlight1 = Improved responsiveness
 e10s.offerPopup.highlight2 = Fewer crashes
 e10s.offerPopup.enableAndRestart.label = Enable and Restart
 e10s.offerPopup.enableAndRestart.accesskey = E
 e10s.offerPopup.noThanks.label = No, thanks
 e10s.offerPopup.noThanks.accesskey = N
 e10s.postActivationInfobar.message = You're now helping to test multi-process in %S! Please report problems you find.
 e10s.postActivationInfobar.learnMore.label = Learn More
 e10s.postActivationInfobar.learnMore.accesskey = L
-e10s.accessibilityNotice.mainMessage = Multi-process does not yet support accessibility features. Multi-process will be disabled if you restart %S. Would you like to restart?
-e10s.accessibilityNotice.disableAndRestart.label = Disable and Restart
-e10s.accessibilityNotice.disableAndRestart.accesskey = R
-e10s.accessibilityNotice.dontDisable.label = Don't Disable
-e10s.accessibilityNotice.dontDisable.accesskey = D
+e10s.accessibilityNotice.mainMessage2 = Accessibility support is partially disabled due to compatibility issues with new %S features.
+e10s.accessibilityNotice.acceptButton.label = Ok
+e10s.accessibilityNotice.acceptButton.accesskey = O
+e10s.accessibilityNotice.enableAndRestart.label = Enable (Requires Restart)
+e10s.accessibilityNotice.enableAndRestart.accesskey = E
 
 # LOCALIZATION NOTE (usercontext.personal.label,
 #                    usercontext.work.label,
 #                    usercontext.shopping.label,
 #                    usercontext.banking.label):
 # These strings specify the four default contexts included in support of the
 # Contextual Identity / Containers project. Each context is meant to represent
 # the context that the user is in when interacting with the site. Different
--- a/docshell/base/nsDocShellLoadInfo.cpp
+++ b/docshell/base/nsDocShellLoadInfo.cpp
@@ -7,17 +7,18 @@
 #include "nsDocShellLoadInfo.h"
 #include "nsISHEntry.h"
 #include "nsIInputStream.h"
 #include "nsIURI.h"
 #include "nsIDocShell.h"
 #include "mozilla/net/ReferrerPolicy.h"
 
 nsDocShellLoadInfo::nsDocShellLoadInfo()
-  : mInheritOwner(false)
+  : mLoadReplace(false)
+  , mInheritOwner(false)
   , mOwnerIsExplicit(false)
   , mSendReferrer(true)
   , mReferrerPolicy(mozilla::net::RP_Default)
   , mLoadType(nsIDocShellLoadInfo::loadNormal)
   , mIsSrcdocLoad(false)
 {
 }
 
--- a/docshell/base/nsDocShellLoadInfo.h
+++ b/docshell/base/nsDocShellLoadInfo.h
@@ -28,18 +28,18 @@ public:
   NS_DECL_NSIDOCSHELLLOADINFO
 
 protected:
   virtual ~nsDocShellLoadInfo();
 
 protected:
   nsCOMPtr<nsIURI> mReferrer;
   nsCOMPtr<nsIURI> mOriginalURI;
+  nsCOMPtr<nsISupports> mOwner;
   bool mLoadReplace;
-  nsCOMPtr<nsISupports> mOwner;
   bool mInheritOwner;
   bool mOwnerIsExplicit;
   bool mSendReferrer;
   nsDocShellInfoReferrerPolicy mReferrerPolicy;
   nsDocShellInfoLoadType mLoadType;
   nsCOMPtr<nsISHEntry> mSHEntry;
   nsString mTarget;
   nsCOMPtr<nsIInputStream> mPostDataStream;
--- a/docshell/shistory/nsSHEntry.cpp
+++ b/docshell/shistory/nsSHEntry.cpp
@@ -19,16 +19,17 @@
 #include <algorithm>
 
 namespace dom = mozilla::dom;
 
 static uint32_t gEntryID = 0;
 
 nsSHEntry::nsSHEntry()
   : mShared(new nsSHEntryShared())
+  , mLoadReplace(false)
   , mReferrerPolicy(mozilla::net::RP_Default)
   , mLoadType(0)
   , mID(gEntryID++)
   , mScrollPositionX(0)
   , mScrollPositionY(0)
   , mParent(nullptr)
   , mURIWasModified(false)
   , mIsSrcdocEntry(false)
--- a/dom/audiochannel/AudioChannelAgent.cpp
+++ b/dom/audiochannel/AudioChannelAgent.cpp
@@ -1,17 +1,21 @@
 /* -*- 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 "AudioChannelAgent.h"
 #include "AudioChannelService.h"
+#include "mozilla/Preferences.h"
+#include "nsIAppsService.h"
+#include "nsIDocument.h"
 #include "nsIDOMWindow.h"
+#include "nsIPrincipal.h"
 #include "nsPIDOMWindow.h"
 #include "nsXULAppAPI.h"
 
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(AudioChannelAgent)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioChannelAgent)
@@ -73,16 +77,91 @@ AudioChannelAgent::InitWithWeakCallback(
                                         int32_t aChannelType,
                                         nsIAudioChannelAgentCallback *aCallback)
 {
   return InitInternal(aWindow, aChannelType, aCallback,
                       /* useWeakRef = */ true);
 }
 
 nsresult
+AudioChannelAgent::FindCorrectWindow(nsIDOMWindow* aWindow)
+{
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
+  MOZ_ASSERT(window->IsInnerWindow());
+
+  mWindow = window->GetScriptableTop();
+  if (NS_WARN_IF(!mWindow)) {
+    return NS_OK;
+  }
+
+  mWindow = mWindow->GetOuterWindow();
+  if (NS_WARN_IF(!mWindow)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // From here we do an hack for nested iframes.
+  // The system app doesn't have access to the nested iframe objects so it
+  // cannot control the volume of the agents running in nested apps. What we do
+  // here is to assign those Agents to the top scriptable window of the parent
+  // iframe (what is controlled by the system app).
+  // For doing this we go recursively back into the chain of windows until we
+  // find apps that are not the system one.
+  window = mWindow->GetParent();
+  if (!window || window == mWindow) {
+    return NS_OK;
+  }
+
+  window = window->GetCurrentInnerWindow();
+  if (!window) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
+  if (!doc) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
+
+  uint32_t appId;
+  nsresult rv = principal->GetAppId(&appId);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (appId == nsIScriptSecurityManager::NO_APP_ID ||
+      appId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
+  if (NS_WARN_IF(!appsService)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsAdoptingString systemAppManifest =
+    mozilla::Preferences::GetString("b2g.system_manifest_url");
+  if (!systemAppManifest) {
+    return NS_OK;
+  }
+
+  uint32_t systemAppId;
+  rv = appsService->GetAppLocalIdByManifestURL(systemAppManifest, &systemAppId);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (systemAppId == appId) {
+    return NS_OK;
+  }
+
+  return FindCorrectWindow(window);
+}
+
+nsresult
 AudioChannelAgent::InitInternal(nsIDOMWindow* aWindow, int32_t aChannelType,
                                 nsIAudioChannelAgentCallback *aCallback,
                                 bool aUseWeakRef)
 {
   // We syncd the enum of channel type between nsIAudioChannelAgent.idl and
   // AudioChannelBinding.h the same.
   MOZ_ASSERT(int(AUDIO_AGENT_CHANNEL_NORMAL) == int(AudioChannel::Normal) &&
              int(AUDIO_AGENT_CHANNEL_CONTENT) == int(AudioChannel::Content) &&
@@ -103,28 +182,19 @@ AudioChannelAgent::InitInternal(nsIDOMWi
   if (NS_WARN_IF(!aWindow)) {
     return NS_OK;
   }
 
   nsCOMPtr<nsPIDOMWindow> pInnerWindow = do_QueryInterface(aWindow);
   MOZ_ASSERT(pInnerWindow->IsInnerWindow());
   mInnerWindowID = pInnerWindow->WindowID();
 
-  nsCOMPtr<nsPIDOMWindow> topWindow = pInnerWindow->GetScriptableTop();
-  if (NS_WARN_IF(!topWindow)) {
-    return NS_OK;
-  }
-
-  mWindow = do_QueryInterface(topWindow);
-  if (mWindow) {
-    mWindow = mWindow->GetOuterWindow();
-  }
-
-  if (NS_WARN_IF(!mWindow)) {
-    return NS_ERROR_FAILURE;
+  nsresult rv = FindCorrectWindow(aWindow);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
   }
 
   mAudioChannelType = aChannelType;
 
   if (aUseWeakRef) {
     mWeakCallback = do_GetWeakReference(aCallback);
   } else {
     mCallback = aCallback;
--- a/dom/audiochannel/AudioChannelAgent.h
+++ b/dom/audiochannel/AudioChannelAgent.h
@@ -51,16 +51,18 @@ private:
   already_AddRefed<nsIAudioChannelAgentCallback> GetCallback();
 
   nsresult InitInternal(nsIDOMWindow* aWindow, int32_t aAudioAgentType,
                         nsIAudioChannelAgentCallback* aCallback,
                         bool aUseWeakRef);
 
   void Shutdown();
 
+  nsresult FindCorrectWindow(nsIDOMWindow* aWindow);
+
   nsCOMPtr<nsPIDOMWindow> mWindow;
   nsCOMPtr<nsIAudioChannelAgentCallback> mCallback;
 
   nsWeakPtr mWeakCallback;
 
   int32_t mAudioChannelType;
   uint64_t mInnerWindowID;
   bool mIsRegToService;
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -9,16 +9,17 @@
 #include "base/basictypes.h"
 
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/unused.h"
 
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/TabParent.h"
 
 #include "nsContentUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsISupportsPrimitives.h"
 #include "nsThreadUtils.h"
 #include "nsHashPropertyBag.h"
 #include "nsComponentManagerUtils.h"
 #include "nsPIDOMWindow.h"
@@ -214,16 +215,17 @@ AudioChannelService::Shutdown()
         // To monitor the volume settings based on audio channel.
         obs->RemoveObserver(gAudioChannelService, "mozsettings-changed");
 #endif
       }
     }
 
     gAudioChannelService->mWindows.Clear();
     gAudioChannelService->mPlayingChildren.Clear();
+    gAudioChannelService->mTabParents.Clear();
 #ifdef MOZ_WIDGET_GONK
     gAudioChannelService->mSpeakerManager.Clear();
 #endif
 
     gAudioChannelService = nullptr;
   }
 }
 
@@ -337,16 +339,31 @@ AudioChannelService::UnregisterAudioChan
       new MediaPlaybackRunnable(aAgent->Window(), false /* active */);
     NS_DispatchToCurrentThread(runnable);
   }
 
   MaybeSendStatusUpdate();
 }
 
 void
+AudioChannelService::RegisterTabParent(TabParent* aTabParent)
+{
+  MOZ_ASSERT(aTabParent);
+  MOZ_ASSERT(!mTabParents.Contains(aTabParent));
+  mTabParents.AppendElement(aTabParent);
+}
+
+void
+AudioChannelService::UnregisterTabParent(TabParent* aTabParent)
+{
+  MOZ_ASSERT(aTabParent);
+  mTabParents.RemoveElement(aTabParent);
+}
+
+void
 AudioChannelService::GetState(nsPIDOMWindow* aWindow, uint32_t aAudioChannel,
                               float* aVolume, bool* aMuted)
 {
   MOZ_ASSERT(!aWindow || aWindow->IsOuterWindow());
   MOZ_ASSERT(aVolume && aMuted);
   MOZ_ASSERT(aAudioChannel < NUMBER_OF_AUDIO_CHANNELS);
 
 
@@ -556,16 +573,42 @@ AudioChannelService::Observe(nsISupports
 
     RemoveChildStatus(childID);
   }
 
   return NS_OK;
 }
 
 void
+AudioChannelService::RefreshAgentsVolumeAndPropagate(AudioChannel aAudioChannel,
+                                                     nsPIDOMWindow* aWindow)
+{
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aWindow->IsOuterWindow());
+
+  nsCOMPtr<nsPIDOMWindow> topWindow = aWindow->GetScriptableTop();
+  if (!topWindow) {
+    return;
+  }
+
+  AudioChannelWindow* winData = GetWindowData(topWindow->WindowID());
+  if (!winData) {
+    return;
+  }
+
+  for (uint32_t i = 0; i < mTabParents.Length(); ++i) {
+    mTabParents[i]->AudioChannelChangeNotification(aWindow, aAudioChannel,
+                                                   winData->mChannels[(uint32_t)aAudioChannel].mVolume,
+                                                   winData->mChannels[(uint32_t)aAudioChannel].mMuted);
+  }
+
+  RefreshAgentsVolume(aWindow);
+}
+
+void
 AudioChannelService::RefreshAgentsVolume(nsPIDOMWindow* aWindow)
 {
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(aWindow->IsOuterWindow());
 
   nsCOMPtr<nsPIDOMWindow> topWindow = aWindow->GetScriptableTop();
   if (!topWindow) {
     return;
@@ -746,17 +789,17 @@ AudioChannelService::SetAudioChannelVolu
   MOZ_ASSERT(aWindow->IsOuterWindow());
 
   MOZ_LOG(GetAudioChannelLog(), LogLevel::Debug,
          ("AudioChannelService, SetAudioChannelVolume, window = %p, type = %d, "
           "volume = %d\n", aWindow, aAudioChannel, aVolume));
 
   AudioChannelWindow* winData = GetOrCreateWindowData(aWindow);
   winData->mChannels[(uint32_t)aAudioChannel].mVolume = aVolume;
-  RefreshAgentsVolume(aWindow);
+  RefreshAgentsVolumeAndPropagate(aAudioChannel, aWindow);
 }
 
 NS_IMETHODIMP
 AudioChannelService::SetAudioChannelVolume(nsIDOMWindow* aWindow,
                                            unsigned short aAudioChannel,
                                            float aVolume)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -809,17 +852,17 @@ AudioChannelService::SetAudioChannelMute
 
   if (aAudioChannel == AudioChannel::System) {
     // Workaround for bug1183033, system channel type can always playback.
     return;
   }
 
   AudioChannelWindow* winData = GetOrCreateWindowData(aWindow);
   winData->mChannels[(uint32_t)aAudioChannel].mMuted = aMuted;
-  RefreshAgentsVolume(aWindow);
+  RefreshAgentsVolumeAndPropagate(aAudioChannel, aWindow);
 }
 
 NS_IMETHODIMP
 AudioChannelService::SetAudioChannelMuted(nsIDOMWindow* aWindow,
                                           unsigned short aAudioChannel,
                                           bool aMuted)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -18,20 +18,23 @@
 #include "mozilla/dom/AudioChannelBinding.h"
 
 class nsIRunnable;
 class nsPIDOMWindow;
 struct PRLogModuleInfo;
 
 namespace mozilla {
 namespace dom {
+
 #ifdef MOZ_WIDGET_GONK
 class SpeakerManagerService;
 #endif
 
+class TabParent;
+
 #define NUMBER_OF_AUDIO_CHANNELS (uint32_t)AudioChannel::EndGuard_
 
 class AudioChannelService final : public nsIAudioChannelService
                                 , public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
@@ -59,16 +62,22 @@ public:
   /**
    * Any audio channel agent that stops playing should unregister itself to
    * this service.
    */
   void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent,
                                    uint32_t aNotifyPlayback);
 
   /**
+   * For nested iframes.
+   */
+  void RegisterTabParent(TabParent* aTabParent);
+  void UnregisterTabParent(TabParent* aTabParent);
+
+  /**
    * Return the state to indicate this audioChannel for his window should keep
    * playing/muted.
    */
   void GetState(nsPIDOMWindow* aWindow, uint32_t aChannel,
                 float* aVolume, bool* aMuted);
 
   /* Methods for the BrowserElementAudioChannel */
   float GetAudioChannelVolume(nsPIDOMWindow* aWindow, AudioChannel aChannel);
@@ -103,16 +112,19 @@ public:
    */
   virtual void SetDefaultVolumeControlChannel(int32_t aChannel,
                                               bool aVisible);
 
   bool AnyAudioChannelIsActive();
 
   void RefreshAgentsVolume(nsPIDOMWindow* aWindow);
 
+  void RefreshAgentsVolumeAndPropagate(AudioChannel aAudioChannel,
+                                       nsPIDOMWindow* aWindow);
+
   // This method needs to know the inner window that wants to capture audio. We
   // group agents per top outer window, but we can have multiple innerWindow per
   // top outerWindow (subiframes, etc.) and we have to identify all the agents
   // just for a particular innerWindow.
   void RefreshAgentsCapture(nsPIDOMWindow* aWindow,
                             uint64_t aInnerWindowID);
 
 
@@ -218,16 +230,19 @@ private:
   nsTObserverArray<nsAutoPtr<AudioChannelWindow>> mWindows;
 
   nsTObserverArray<nsAutoPtr<AudioChannelChildStatus>> mPlayingChildren;
 
 #ifdef MOZ_WIDGET_GONK
   nsTArray<SpeakerManagerService*>  mSpeakerManager;
 #endif
 
+  // Raw pointers because TabParents must unregister themselves.
+  nsTArray<TabParent*> mTabParents;
+
   nsCOMPtr<nsIRunnable> mRunnable;
 
   uint64_t mDefChannelChildID;
 
   // These boolean are used to know if we have to send an status update to the
   // service running in the main process.
   bool mTelephonyChannel;
   bool mContentOrNormalChannel;
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -157,16 +157,17 @@
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIStreamConverterService.h"
 #include "nsIStringBundle.h"
 #include "nsIURI.h"
 #include "nsIURIWithPrincipal.h"
 #include "nsIURL.h"
 #include "nsIWebNavigation.h"
+#include "nsIWindowMediator.h"
 #include "nsIWordBreaker.h"
 #include "nsIXPConnect.h"
 #include "nsJSUtils.h"
 #include "nsLWBrkCIID.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 #include "nsNodeInfoManager.h"
 #include "nsNullPrincipal.h"
@@ -5163,16 +5164,33 @@ nsContentUtils::RemoveScriptBlocker()
 nsIWindowProvider*
 nsContentUtils::GetWindowProviderForContentProcess()
 {
   MOZ_ASSERT(XRE_IsContentProcess());
   return ContentChild::GetSingleton();
 }
 
 /* static */
+already_AddRefed<nsPIDOMWindow>
+nsContentUtils::GetMostRecentNonPBWindow()
+{
+  nsCOMPtr<nsIWindowMediator> windowMediator =
+    do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
+  nsCOMPtr<nsIWindowMediator_44> wm = do_QueryInterface(windowMediator);
+
+  nsCOMPtr<nsIDOMWindow> window;
+  wm->GetMostRecentNonPBWindow(MOZ_UTF16("navigator:browser"),
+                               getter_AddRefs(window));
+  nsCOMPtr<nsPIDOMWindow> pwindow;
+  pwindow = do_QueryInterface(window);
+
+  return pwindow.forget();
+}
+
+/* static */
 void
 nsContentUtils::WarnScriptWasIgnored(nsIDocument* aDocument)
 {
   nsAutoString msg;
   if (aDocument) {
     nsCOMPtr<nsIURI> uri = aDocument->GetDocumentURI();
     if (uri) {
       nsCString spec;
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -1661,16 +1661,21 @@ public:
     return sScriptBlockerCount == 0;
   }
 
   // XXXcatalinb: workaround for weird include error when trying to reference
   // ipdl types in WindowWatcher.
   static nsIWindowProvider*
   GetWindowProviderForContentProcess();
 
+  // Returns the browser window with the most recent time stamp that is
+  // not in private browsing mode.
+  static already_AddRefed<nsPIDOMWindow>
+  GetMostRecentNonPBWindow();
+
   /**
    * Call this function if !IsSafeToRunScript() and we fail to run the script
    * (rather than using AddScriptRunner as we usually do). |aDocument| is
    * optional as it is only used for showing the URL in the console.
    */
   static void WarnScriptWasIgnored(nsIDocument* aDocument);
 
   /**
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -5045,16 +5045,24 @@ nsDocument::DispatchContentLoadedEvents(
   // event.
   Element* root = GetRootElement();
   if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::manifest)) {
     nsContentUtils::DispatchChromeEvent(this, static_cast<nsIDocument*>(this),
                                         NS_LITERAL_STRING("MozApplicationManifest"),
                                         true, true);
   }
 
+  if (mMaybeServiceWorkerControlled) {
+    using mozilla::dom::workers::ServiceWorkerManager;
+    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+    if (swm) {
+      swm->MaybeCheckNavigationUpdate(this);
+    }
+  }
+
   UnblockOnload(true);
 }
 
 void
 nsDocument::EndLoad()
 {
   // Drop the ref to our parser, if any, but keep hold of the sink so that we
   // can flush it from FlushPendingNotifications as needed.  We might have to
--- a/dom/base/nsIFrameLoader.idl
+++ b/dom/base/nsIFrameLoader.idl
@@ -1,15 +1,16 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "nsISupports.idl"
 
+interface mozIApplication;
 interface nsFrameLoader;
 interface nsIDocShell;
 interface nsIURI;
 interface nsIFrame;
 interface nsSubDocumentFrame;
 interface nsIMessageSender;
 interface nsIVariant;
 interface nsIDOMElement;
@@ -209,26 +210,32 @@ interface nsIFrameLoader : nsISupports
 };
 
 %{C++
 class nsFrameLoader;
 %}
 
 native alreadyAddRefed_nsFrameLoader(already_AddRefed<nsFrameLoader>);
 
-[scriptable, uuid(c4abebcf-55f3-47d4-af15-151311971255)]
+[scriptable, uuid(adc1b3ba-8deb-4943-8045-e6de0044f2ce)]
 interface nsIFrameLoaderOwner : nsISupports
 {
   /**
    * The frame loader owned by this nsIFrameLoaderOwner
    */
   readonly attribute nsIFrameLoader frameLoader;
   [noscript, notxpcom] alreadyAddRefed_nsFrameLoader GetFrameLoader();
 
   /**
+   * The principal of parent mozIApplication in case of nested mozbrowser
+   * iframes.
+   */
+  readonly attribute mozIApplication parentApplication;
+
+  /**
    * Puts the FrameLoaderOwner in prerendering mode.
    */
   void setIsPrerendered();
 
   /**
    * Swap frame loaders with the given nsIFrameLoaderOwner.  This may
    * only be posible in a very limited range of circumstances, or
    * never, depending on the object implementing this interface.
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -1211,16 +1211,27 @@ nsObjectLoadingContent::GetFrameLoader(n
 NS_IMETHODIMP_(already_AddRefed<nsFrameLoader>)
 nsObjectLoadingContent::GetFrameLoader()
 {
   RefPtr<nsFrameLoader> loader = mFrameLoader;
   return loader.forget();
 }
 
 NS_IMETHODIMP
+nsObjectLoadingContent::GetParentApplication(mozIApplication** aApplication)
+{
+  if (!aApplication) {
+    return NS_ERROR_FAILURE;
+  }
+
+  *aApplication = nullptr;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsObjectLoadingContent::SetIsPrerendered()
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherLoader)
 {
--- a/dom/bindings/test/test_exception_options_from_jsimplemented.html
+++ b/dom/bindings/test/test_exception_options_from_jsimplemented.html
@@ -13,17 +13,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   /** Test for Bug 1107592 **/
 
   SimpleTest.waitForExplicitFinish();
 
   function doTest() {
     var file = location.href;
     var asyncFrame;
     /* Async parent frames from pushPrefEnv don't show up in e10s.  */
-    var isE10S = !SpecialPowers.Services.wm.getMostRecentWindow("navigator:browser");
+    var isE10S = !SpecialPowers.isMainProcess();
     if (!isE10S && SpecialPowers.getBoolPref("javascript.options.asyncstack")) {
       asyncFrame = `Async*@${file}:153:1
 `;
     } else {
       asyncFrame = "";
     }
 
     var t = new TestInterfaceJS();
--- a/dom/bindings/test/test_promise_rejections_from_jsimplemented.html
+++ b/dom/bindings/test/test_promise_rejections_from_jsimplemented.html
@@ -33,17 +33,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   function ensurePromiseFail(testNumber, value) {
     ok(false, "Test " + testNumber + " should not have a fulfilled promise");
   }
 
   function doTest() {
     var t = new TestInterfaceJS();
     /* Async parent frames from pushPrefEnv don't show up in e10s.  */
-    var isE10S = !SpecialPowers.Services.wm.getMostRecentWindow("navigator:browser");
+    var isE10S = !SpecialPowers.isMainProcess();
     var asyncStack = SpecialPowers.getBoolPref("javascript.options.asyncstack");
     var ourFile = location.href;
     var parentFrame = (asyncStack && !isE10S) ? `Async*@${ourFile}:121:1
 ` : "";
 
     Promise.all([
       t.testPromiseWithThrowingChromePromiseInit().then(
           ensurePromiseFail.bind(null, 1),
--- a/dom/browser-element/BrowserElementAudioChannel.cpp
+++ b/dom/browser-element/BrowserElementAudioChannel.cpp
@@ -2,42 +2,34 @@
  * 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 "BrowserElementAudioChannel.h"
 
 #include "mozilla/Services.h"
 #include "mozilla/dom/BrowserElementAudioChannelBinding.h"
 #include "mozilla/dom/DOMRequest.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/TabParent.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "AudioChannelService.h"
 #include "nsIBrowserElementAPI.h"
 #include "nsIDocShell.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMDOMRequest.h"
 #include "nsIObserverService.h"
 #include "nsISupportsPrimitives.h"
 #include "nsISystemMessagesInternal.h"
 #include "nsITabParent.h"
 #include "nsNetUtil.h"
 #include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
 
-namespace {
-
-void
-AssertIsInMainProcess()
-{
-  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
-}
-
-} // anonymous namespace
-
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ADDREF_INHERITED(BrowserElementAudioChannel, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(BrowserElementAudioChannel, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BrowserElementAudioChannel)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
@@ -84,17 +76,16 @@ BrowserElementAudioChannel::BrowserEleme
   : DOMEventTargetHelper(aWindow)
   , mFrameLoader(aFrameLoader)
   , mBrowserElementAPI(aAPI)
   , mAudioChannel(aAudioChannel)
   , mManifestURL(aManifestURL)
   , mState(eStateUnknown)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  AssertIsInMainProcess();
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     nsAutoString name;
     AudioChannelService::GetAudioChannelString(aAudioChannel, name);
 
     nsAutoCString topic;
     topic.Assign("audiochannel-activity-");
@@ -102,17 +93,16 @@ BrowserElementAudioChannel::BrowserEleme
 
     obs->AddObserver(this, topic.get(), true);
   }
 }
 
 BrowserElementAudioChannel::~BrowserElementAudioChannel()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  AssertIsInMainProcess();
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     nsAutoString name;
     AudioChannelService::GetAudioChannelString(mAudioChannel, name);
 
     nsAutoCString topic;
     topic.Assign("audiochannel-activity-");
@@ -168,18 +158,16 @@ BrowserElementAudioChannel::WrapObject(J
 {
   return BrowserElementAudioChannelBinding::Wrap(aCx, this, aGivenProto);
 }
 
 AudioChannel
 BrowserElementAudioChannel::Name() const
 {
   MOZ_ASSERT(NS_IsMainThread());
-  AssertIsInMainProcess();
-
   return mAudioChannel;
 }
 
 namespace {
 
 class BaseRunnable : public nsRunnable
 {
 protected:
@@ -356,17 +344,16 @@ RespondSuccessHandler::RejectedCallback(
 }
 
 } // anonymous namespace
 
 already_AddRefed<dom::DOMRequest>
 BrowserElementAudioChannel::GetVolume(ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  AssertIsInMainProcess();
 
   if (!mFrameWindow) {
     nsCOMPtr<nsIDOMDOMRequest> request;
     aRv = mBrowserElementAPI->GetAudioChannelVolume((uint32_t)mAudioChannel,
                                                     getter_AddRefs(request));
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
@@ -382,17 +369,16 @@ BrowserElementAudioChannel::GetVolume(Er
 
   return domRequest.forget();
 }
 
 already_AddRefed<dom::DOMRequest>
 BrowserElementAudioChannel::SetVolume(float aVolume, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  AssertIsInMainProcess();
 
   if (!mFrameWindow) {
     nsCOMPtr<nsIDOMDOMRequest> request;
     aRv = mBrowserElementAPI->SetAudioChannelVolume((uint32_t)mAudioChannel,
                                                     aVolume,
                                                     getter_AddRefs(request));
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
@@ -415,17 +401,16 @@ BrowserElementAudioChannel::SetVolume(fl
 
   return domRequest.forget();
 }
 
 already_AddRefed<dom::DOMRequest>
 BrowserElementAudioChannel::GetMuted(ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  AssertIsInMainProcess();
 
   if (!mFrameWindow) {
     nsCOMPtr<nsIDOMDOMRequest> request;
     aRv = mBrowserElementAPI->GetAudioChannelMuted((uint32_t)mAudioChannel,
                                                    getter_AddRefs(request));
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
@@ -441,17 +426,16 @@ BrowserElementAudioChannel::GetMuted(Err
 
   return domRequest.forget();
 }
 
 already_AddRefed<dom::DOMRequest>
 BrowserElementAudioChannel::SetMuted(bool aMuted, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  AssertIsInMainProcess();
 
   if (!mFrameWindow) {
     nsCOMPtr<nsIDOMDOMRequest> request;
     aRv = mBrowserElementAPI->SetAudioChannelMuted((uint32_t)mAudioChannel,
                                                    aMuted,
                                                    getter_AddRefs(request));
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
@@ -474,17 +458,16 @@ BrowserElementAudioChannel::SetMuted(boo
 
   return domRequest.forget();
 }
 
 already_AddRefed<dom::DOMRequest>
 BrowserElementAudioChannel::IsActive(ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  AssertIsInMainProcess();
 
   if (mState != eStateUnknown) {
     RefPtr<DOMRequest> domRequest = new DOMRequest(GetOwner());
 
     nsCOMPtr<nsIRunnable> runnable =
       new IsActiveRunnable(GetOwner(), mFrameWindow, domRequest, mAudioChannel,
                            mState == eStateActive);
     NS_DispatchToMainThread(runnable);
@@ -588,18 +571,39 @@ BrowserElementAudioChannel::Observe(nsIS
     if (mTabParent == aSubject) {
       ProcessStateChanged(aData);
     }
 
     return NS_OK;
   }
 
   nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
-  if (NS_WARN_IF(!wrapper)) {
-    return NS_ERROR_FAILURE;
+  // This can be a nested iframe.
+  if (!wrapper) {
+    nsCOMPtr<nsITabParent> iTabParent = do_QueryInterface(aSubject);
+    if (!iTabParent) {
+      return NS_ERROR_FAILURE;
+    }
+
+    RefPtr<TabParent> tabParent = TabParent::GetFrom(iTabParent);
+    if (!tabParent) {
+      return NS_ERROR_FAILURE;
+    }
+
+    Element* element = tabParent->GetOwnerElement();
+    if (!element) {
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsPIDOMWindow> window = element->OwnerDoc()->GetWindow();
+    if (window == mFrameWindow) {
+      ProcessStateChanged(aData);
+    }
+
+    return NS_OK;
   }
 
   uint64_t windowID;
   nsresult rv = wrapper->GetData(&windowID);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
--- a/dom/browser-element/mochitest/browserElement_AudioChannel.js
+++ b/dom/browser-element/mochitest/browserElement_AudioChannel.js
@@ -187,12 +187,15 @@ function runTests() {
     return;
   }
 
   var test = tests.shift();
   test();
 }
 
 
-addEventListener('load', function() {
-  SimpleTest.executeSoon(runTests);
+addEventListener('testready', function() {
+  SpecialPowers.pushPrefEnv({'set': [["b2g.system_manifest_url", "http://mochi.test:8888/manifest.webapp"]]},
+                            function() {
+    SimpleTest.executeSoon(runTests);
+  });
 });
 
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/browserElement_AudioChannel_nested.js
@@ -0,0 +1,79 @@
+/* Any copyright is dedicated to the public domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Bug 1113086 - tests for AudioChannel API into BrowserElement
+
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+browserElementTestHelpers.setEnabledPref(true);
+browserElementTestHelpers.addPermission();
+
+function runTests() {
+  var iframe = document.createElement('iframe');
+  iframe.setAttribute('mozbrowser', 'true');
+  iframe.setAttribute('mozapp', 'http://example.org/manifest.webapp');
+
+  var listener = function(e) {
+    var message = e.detail.message;
+    if (/^OK/.exec(message)) {
+      ok(true, "Message from app: " + message);
+    } else if (/^KO/.exec(message)) {
+      ok(false, "Message from app: " + message);
+    } else if (/DONE/.exec(message)) {
+      ok(true, "Messaging from app complete");
+      iframe.removeEventListener('mozbrowsershowmodalprompt', listener);
+    }
+  }
+
+  function audio_loadend() {
+    ok("mute" in iframe, "iframe.mute exists");
+    ok("unmute" in iframe, "iframe.unmute exists");
+    ok("getMuted" in iframe, "iframe.getMuted exists");
+    ok("getVolume" in iframe, "iframe.getVolume exists");
+    ok("setVolume" in iframe, "iframe.setVolume exists");
+
+    ok("allowedAudioChannels" in iframe, "allowedAudioChannels exist");
+    var channels = iframe.allowedAudioChannels;
+    is(channels.length, 1, "1 audio channel by default");
+
+    var ac = channels[0];
+
+    ok(ac instanceof BrowserElementAudioChannel, "Correct class");
+    ok("getVolume" in ac, "ac.getVolume exists");
+    ok("setVolume" in ac, "ac.setVolume exists");
+    ok("getMuted" in ac, "ac.getMuted exists");
+    ok("setMuted" in ac, "ac.setMuted exists");
+    ok("isActive" in ac, "ac.isActive exists");
+
+    info("Setting the volume...");
+    ac.setVolume(0.5);
+
+    ac.onactivestatechanged = function() {
+      ok(true, "activestatechanged event received.");
+      ac.onactivestatechanged = null;
+      SimpleTest.finish();
+    }
+  }
+
+  iframe.addEventListener('mozbrowserloadend', audio_loadend);
+  iframe.addEventListener('mozbrowsershowmodalprompt', listener, false);
+  document.body.appendChild(iframe);
+
+  var context = { 'url': 'http://example.org',
+                  'appId': SpecialPowers.Ci.nsIScriptSecurityManager.NO_APP_ID,
+                  'isInBrowserElement': true };
+  SpecialPowers.pushPermissions([
+    {'type': 'browser', 'allow': 1, 'context': context},
+    {'type': 'embed-apps', 'allow': 1, 'context': context}
+  ], function() {
+    iframe.src = 'http://example.org/tests/dom/browser-element/mochitest/file_browserElement_AudioChannel_nested.html';
+  });
+}
+
+addEventListener('testready', function() {
+  SpecialPowers.pushPrefEnv({'set': [["b2g.system_manifest_url", "http://mochi.test:8888/manifest.webapp"]]},
+                            function() {
+    SimpleTest.executeSoon(runTests);
+  });
+});
--- a/dom/browser-element/mochitest/browserElement_AudioPlayback.js
+++ b/dom/browser-element/mochitest/browserElement_AudioPlayback.js
@@ -63,9 +63,14 @@ function runTest() {
     ok(false,
        'mozbrowseraudioplaybackchange should dispatch to the correct browser');
   });
 
   // Load a simple page to get the process started.
   iframe.src = browserElementTestHelpers.emptyPage1;
 }
 
-addEventListener('testready', runTest);
+addEventListener('testready', function() {
+  SpecialPowers.pushPrefEnv({'set': [["b2g.system_manifest_url", "http://mochi.test:8888/manifest.webapp"]]},
+                            function() {
+    SimpleTest.executeSoon(runTest);
+  });
+});
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/file_browserElement_AudioChannel_nested.html
@@ -0,0 +1,63 @@
+<html>
+<head>
+<script type="text/javascript">
+
+  function ok(a, msg) {
+    alert((!!a ? "OK" : "KO") + " " + msg);
+  }
+
+  function is(a, b, msg) {
+    ok(a === b, msg);
+  }
+
+  function finish(a, b, msg) {
+    alert("DONE");
+  }
+
+  addEventListener('load', function(e) {
+    var iframe = document.createElement('iframe');
+    iframe.setAttribute('mozbrowser', 'true');
+    // set 'remote' to true here will make the the iframe remote in _inproc_
+    // test and in-process in _oop_  test.
+    iframe.setAttribute('remote', 'true');
+    iframe.setAttribute('mozapp', 'http://example.org/manifest.webapp');
+
+    iframe.addEventListener('mozbrowserloadend', function(e) {
+      ok("mute" in iframe, "iframe.mute exists");
+      ok("unmute" in iframe, "iframe.unmute exists");
+      ok("getMuted" in iframe, "iframe.getMuted exists");
+      ok("getVolume" in iframe, "iframe.getVolume exists");
+      ok("setVolume" in iframe, "iframe.setVolume exists");
+
+      ok("allowedAudioChannels" in iframe, "allowedAudioChannels exist");
+      var channels = iframe.allowedAudioChannels;
+      is(channels.length, 1, "1 audio channel by default");
+
+      var ac = channels[0];
+
+      ok(ac instanceof BrowserElementAudioChannel, "Correct class");
+      ok("getVolume" in ac, "ac.getVolume exists");
+      ok("setVolume" in ac, "ac.setVolume exists");
+      ok("getMuted" in ac, "ac.getMuted exists");
+      ok("setMuted" in ac, "ac.setMuted exists");
+      ok("isActive" in ac, "ac.isActive exists");
+
+      ac.onactivestatechanged = function() {
+        ok("activestatechanged event received.");
+
+        ac.getVolume().onsuccess = function(e) {
+          ok(e.target.result, 1, "Default volume is 1");
+        };
+
+        finish();
+      }
+    });
+
+    document.body.appendChild(iframe);
+    iframe.src = 'http://example.org/tests/dom/browser-element/mochitest/file_audio.html';
+  });
+</script>
+</head>
+<body>
+</body>
+</html>
--- a/dom/browser-element/mochitest/mochitest-oop.ini
+++ b/dom/browser-element/mochitest/mochitest-oop.ini
@@ -116,12 +116,13 @@ disabled = bug 930449
 [test_browserElement_oop_CloseFromOpener.html]
 disabled = bug 924771
 [test_browserElement_oop_CloseApp.html]
 disabled = bug 924771
 [test_browserElement_oop_ExposableURI.html]
 disabled = bug 924771
 [test_browserElement_oop_GetContentDimensions.html]
 [test_browserElement_oop_AudioChannel.html]
+[test_browserElement_oop_AudioChannel_nested.html]
 [test_browserElement_oop_SetNFCFocus.html]
 [test_browserElement_oop_getWebManifest.html]
 [test_browserElement_oop_OpenWindowEmpty.html]
 skip-if = (toolkit == 'gonk') # Test doesn't work on B2G emulator
--- a/dom/browser-element/mochitest/mochitest.ini
+++ b/dom/browser-element/mochitest/mochitest.ini
@@ -78,21 +78,23 @@ support-files =
   browserElement_VisibilityChange.js
   browserElement_XFrameOptions.js
   browserElement_XFrameOptionsAllowFrom.js
   browserElement_XFrameOptionsDeny.js
   browserElement_XFrameOptionsSameOrigin.js
   browserElement_XFrameOptionsSameOrigin.js
   browserElement_GetContentDimensions.js
   browserElement_AudioChannel.js
+  browserElement_AudioChannel_nested.js
   file_browserElement_AlertInFrame.html
   file_browserElement_AlertInFrame_Inner.html
   file_browserElement_AllowEmbedAppsInNestedOOIframe.html
   file_browserElement_AppFramePermission.html
   file_browserElement_AppWindowNamespace.html
+  file_browserElement_AudioChannel_nested.html
   file_browserElement_Viewmode.html
   file_browserElement_ThemeColor.html
   file_browserElement_BrowserWindowNamespace.html
   file_browserElement_CloseApp.html
   file_browserElement_CloseFromOpener.html
   file_browserElement_CookiesNotThirdParty.html
   file_browserElement_DisallowEmbedAppsInOOP.html
   file_browserElement_ExecuteScript.html
@@ -245,12 +247,13 @@ skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_inproc_XFrameOptionsSameOrigin.html]
 [test_browserElement_oop_NextPaint.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 936226
 # Disabled due to https://bugzilla.mozilla.org/show_bug.cgi?id=774100
 [test_browserElement_inproc_Reload.html]
 disabled = bug 774100
 [test_browserElement_inproc_GetContentDimensions.html]
 [test_browserElement_inproc_AudioChannel.html]
+[test_browserElement_inproc_AudioChannel_nested.html]
 [test_browserElement_inproc_SetNFCFocus.html]
 [test_browserElement_inproc_getStructuredData.html]
 [test_browserElement_inproc_OpenWindowEmpty.html]
 skip-if = (toolkit == 'gonk') # Test doesn't work on B2G emulator
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_inproc_AudioChannel_nested.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test of browser element audioChannel in nested mozbrowser iframes.</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript;version=1.7" src="browserElement_AudioChannel_nested.js">
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_oop_AudioChannel_nested.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test of browser element audioChannel.</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript;version=1.7" src="browserElement_AudioChannel_nested.js">
+</script>
+</body>
+</html>
--- a/dom/html/nsBrowserElement.cpp
+++ b/dom/html/nsBrowserElement.cpp
@@ -558,36 +558,43 @@ nsBrowserElement::GetAllowedAudioChannel
     }
 
     nsAutoString manifestURL;
     aRv = mozBrowserFrame->GetAppManifestURL(manifestURL);
     if (NS_WARN_IF(aRv.Failed())) {
       return;
     }
 
+    nsCOMPtr<mozIApplication> parentApp;
+    aRv = GetParentApplication(getter_AddRefs(parentApp));
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
+
     MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
             ("nsBrowserElement, GetAllowedAudioChannels, this = %p\n", this));
 
     GenerateAllowedAudioChannels(window, frameLoader, mBrowserElementAPI,
-                                 manifestURL, mBrowserElementAudioChannels,
-                                 aRv);
+                                 manifestURL, parentApp,
+                                 mBrowserElementAudioChannels, aRv);
     if (NS_WARN_IF(aRv.Failed())) {
       return;
     }
   }
 
   aAudioChannels.AppendElements(mBrowserElementAudioChannels);
 }
 
 /* static */ void
 nsBrowserElement::GenerateAllowedAudioChannels(
                  nsPIDOMWindow* aWindow,
                  nsIFrameLoader* aFrameLoader,
                  nsIBrowserElementAPI* aAPI,
                  const nsAString& aManifestURL,
+                 mozIApplication* aParentApp,
                  nsTArray<RefPtr<BrowserElementAudioChannel>>& aAudioChannels,
                  ErrorResult& aRv)
 {
   MOZ_ASSERT(aAudioChannels.IsEmpty());
 
   nsCOMPtr<nsIAppsService> appsService =
     do_GetService("@mozilla.org/AppsService;1");
   if (NS_WARN_IF(!appsService)) {
@@ -620,16 +627,29 @@ nsBrowserElement::GenerateAllowedAudioCh
 
     bool allowed;
     nsAutoCString permissionName;
 
     for (uint32_t i = 0; audioChannelTable && audioChannelTable[i].tag; ++i) {
       permissionName.AssignASCII("audio-channel-");
       permissionName.AppendASCII(audioChannelTable[i].tag);
 
+      // In case of nested iframes we want to check if the parent has the
+      // permission to use this AudioChannel.
+      if (aParentApp) {
+        aRv = aParentApp->HasPermission(permissionName.get(), &allowed);
+        if (NS_WARN_IF(aRv.Failed())) {
+          return;
+        }
+
+        if (!allowed) {
+          continue;
+        }
+      }
+
       aRv = app->HasPermission(permissionName.get(), &allowed);
       if (NS_WARN_IF(aRv.Failed())) {
         return;
       }
 
       if (allowed) {
         RefPtr<BrowserElementAudioChannel> ac =
           BrowserElementAudioChannel::Create(aWindow, aFrameLoader, aAPI,
--- a/dom/html/nsBrowserElement.h
+++ b/dom/html/nsBrowserElement.h
@@ -120,21 +120,24 @@ public:
                    ErrorResult& aRv);
 
   // Helper
   static void GenerateAllowedAudioChannels(
                  nsPIDOMWindow* aWindow,
                  nsIFrameLoader* aFrameLoader,
                  nsIBrowserElementAPI* aAPI,
                  const nsAString& aManifestURL,
+                 mozIApplication* aParentApp,
                  nsTArray<RefPtr<dom::BrowserElementAudioChannel>>& aAudioChannels,
                  ErrorResult& aRv);
 
 protected:
   NS_IMETHOD_(already_AddRefed<nsFrameLoader>) GetFrameLoader() = 0;
+  NS_IMETHOD GetParentApplication(mozIApplication** aApplication) = 0;
+
   void InitBrowserElementAPI();
   nsCOMPtr<nsIBrowserElementAPI> mBrowserElementAPI;
   nsTArray<RefPtr<dom::BrowserElementAudioChannel>> mBrowserElementAudioChannels;
 
 private:
   bool IsBrowserElementOrThrow(ErrorResult& aRv);
   bool IsNotWidgetOrThrow(ErrorResult& aRv);
   bool mOwnerIsWidget;
--- a/dom/html/nsGenericHTMLFrameElement.cpp
+++ b/dom/html/nsGenericHTMLFrameElement.cpp
@@ -187,16 +187,45 @@ nsGenericHTMLFrameElement::GetFrameLoade
 NS_IMETHODIMP_(already_AddRefed<nsFrameLoader>)
 nsGenericHTMLFrameElement::GetFrameLoader()
 {
   RefPtr<nsFrameLoader> loader = mFrameLoader;
   return loader.forget();
 }
 
 NS_IMETHODIMP
+nsGenericHTMLFrameElement::GetParentApplication(mozIApplication** aApplication)
+{
+  if (!aApplication) {
+    return NS_ERROR_FAILURE;
+  }
+
+  *aApplication = nullptr;
+
+  uint32_t appId;
+  nsIPrincipal *principal = NodePrincipal();
+  nsresult rv = principal->GetAppId(&appId);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
+  if (NS_WARN_IF(!appsService)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  rv = appsService->GetAppByLocalId(appId, aApplication);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsGenericHTMLFrameElement::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherOwner)
 {
   // We don't support this yet
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsGenericHTMLFrameElement::SetIsPrerendered()
--- a/dom/interfaces/base/nsITabParent.idl
+++ b/dom/interfaces/base/nsITabParent.idl
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #include "domstubs.idl"
 
-[scriptable, uuid(7615408c-1fb3-4128-8dd5-a3e2f3fa8842)]
+[builtinclass, scriptable, uuid(8e49f7b0-1f98-4939-bf91-e9c39cd56434)]
 interface nsITabParent : nsISupports
 {
   void injectTouchEvent(in AString aType,
                         [array, size_is(count)] in uint32_t aIdentifiers,
                         [array, size_is(count)] in int32_t aXs,
                         [array, size_is(count)] in int32_t aYs,
                         [array, size_is(count)] in uint32_t aRxs,
                         [array, size_is(count)] in uint32_t aRys,
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -143,17 +143,16 @@
 #include "nsIScriptError.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsISiteSecurityService.h"
 #include "nsISpellChecker.h"
 #include "nsISupportsPrimitives.h"
 #include "nsISystemMessagesInternal.h"
 #include "nsITimer.h"
 #include "nsIURIFixup.h"
-#include "nsIWindowMediator.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIXULWindow.h"
 #include "nsIDOMChromeWindow.h"
 #include "nsIWindowWatcher.h"
 #include "nsPIWindowWatcher.h"
 #include "nsWindowWatcher.h"
 #include "nsIXULRuntime.h"
 #include "gfxDrawable.h"
@@ -266,16 +265,21 @@ using namespace mozilla::system;
 #endif
 
 #ifdef MOZ_GAMEPAD
 #include "mozilla/dom/GamepadMonitoring.h"
 #endif
 
 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
 
+#if defined(XP_WIN)
+// e10s forced enable pref, defined in nsAppRunner.cpp
+extern const char* kForceEnableE10sPref;
+#endif
+
 using base::ChildPrivileges;
 using base::KillProcess;
 #ifdef MOZ_ENABLE_PROFILER_SPS
 using mozilla::ProfileGatherer;
 #endif
 
 #ifdef MOZ_CRASHREPORTER
 using namespace CrashReporter;
@@ -1550,17 +1554,25 @@ ContentParent::Init()
         cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
         obs->NotifyObservers(static_cast<nsIObserver*>(this), "ipc:content-created", cpId.get());
     }
 
 #ifdef ACCESSIBILITY
     // If accessibility is running in chrome process then start it in content
     // process.
     if (nsIPresShell::IsAccessibilityActive()) {
+#if !defined(XP_WIN)
         Unused << SendActivateA11y();
+#else
+        // On Windows we currently only enable a11y in the content process
+        // for testing purposes.
+        if (Preferences::GetBool(kForceEnableE10sPref, false)) {
+            Unused << SendActivateA11y();
+        }
+#endif
     }
 #endif
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
     nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1"));
     bool profilerActive = false;
     DebugOnly<nsresult> rv = profiler->IsActive(&profilerActive);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
@@ -3329,17 +3341,25 @@ ContentParent::Observe(nsISupports* aSub
         Unused << SendVolumeRemoved(volName);
     }
 #endif
 #ifdef ACCESSIBILITY
     // Make sure accessibility is running in content process when accessibility
     // gets initiated in chrome process.
     else if (aData && (*aData == '1') &&
              !strcmp(aTopic, "a11y-init-or-shutdown")) {
+#if !defined(XP_WIN)
         Unused << SendActivateA11y();
+#else
+        // On Windows we currently only enable a11y in the content process
+        // for testing purposes.
+        if (Preferences::GetBool(kForceEnableE10sPref, false)) {
+            Unused << SendActivateA11y();
+        }
+#endif
     }
 #endif
     else if (!strcmp(aTopic, "app-theme-changed")) {
         Unused << SendOnAppThemeChanged();
     }
 #ifdef MOZ_ENABLE_PROFILER_SPS
     else if (!strcmp(aTopic, "profiler-started")) {
         nsCOMPtr<nsIProfilerStartParams> params(do_QueryInterface(aSubject));
@@ -5384,44 +5404,16 @@ ContentParent::AllocPWebBrowserPersistDo
 
 bool
 ContentParent::DeallocPWebBrowserPersistDocumentParent(PWebBrowserPersistDocumentParent* aActor)
 {
   delete aActor;
   return true;
 }
 
-static already_AddRefed<nsPIDOMWindow>
-FindMostRecentOpenWindow()
-{
-    nsCOMPtr<nsIWindowMediator> windowMediator =
-        do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
-    nsCOMPtr<nsISimpleEnumerator> windowEnumerator;
-    windowMediator->GetEnumerator(MOZ_UTF16("navigator:browser"),
-                                  getter_AddRefs(windowEnumerator));
-
-    nsCOMPtr<nsPIDOMWindow> latest;
-
-    bool hasMore = false;
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(windowEnumerator->HasMoreElements(&hasMore)));
-    while (hasMore) {
-        nsCOMPtr<nsISupports> item;
-        MOZ_ALWAYS_TRUE(NS_SUCCEEDED(windowEnumerator->GetNext(getter_AddRefs(item))));
-        nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(item);
-
-        if (window && !window->Closed()) {
-            latest = window;
-        }
-
-        MOZ_ALWAYS_TRUE(NS_SUCCEEDED(windowEnumerator->HasMoreElements(&hasMore)));
-    }
-
-    return latest.forget();
-}
-
 bool
 ContentParent::RecvCreateWindow(PBrowserParent* aThisTab,
                                 PBrowserParent* aNewTab,
                                 const uint32_t& aChromeFlags,
                                 const bool& aCalledFromJS,
                                 const bool& aPositionSpecified,
                                 const bool& aSizeSpecified,
                                 const nsCString& aURI,
@@ -5485,17 +5477,17 @@ ContentParent::RecvCreateWindow(PBrowser
     nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
     if (thisTabParent) {
         browserDOMWin = thisTabParent->GetBrowserDOMWindow();
     }
 
     // If we haven't found a chrome window to open in, just use the most recently
     // opened one.
     if (!parent) {
-        parent = FindMostRecentOpenWindow();
+        parent = nsContentUtils::GetMostRecentNonPBWindow();
         if (NS_WARN_IF(!parent)) {
             *aResult = NS_ERROR_FAILURE;
             return true;
         }
 
         nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(parent);
         if (rootChromeWin) {
             rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -755,16 +755,23 @@ child:
      * using the list of provided charCodes.
      *
      * @param charCode array of potential character codes
      * @param isTrusted true if triggered by a trusted key event
      * @param modifierMask indicates which accesskey modifiers are pressed
      */
     HandleAccessKey(uint32_t[] charCodes, bool isTrusted, int32_t modifierMask);
 
+    /**
+     * Propagate a refresh to the child process
+     */
+    AudioChannelChangeNotification(uint32_t aAudioChannel,
+                                   float aVolume,
+                                   bool aMuted);
+
 /*
  * FIXME: write protocol!
 
 state LIVE:
     send LoadURL goto LIVE;
 //etc.
     send Destroy goto DYING;
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2250,16 +2250,37 @@ TabChild::RecvHandleAccessKey(nsTArray<u
       pc->EventStateManager()->HandleAccessKey(pc, aCharCodes, aIsTrusted, aModifierMask);
     }
   }
 
   return true;
 }
 
 bool
+TabChild::RecvAudioChannelChangeNotification(const uint32_t& aAudioChannel,
+                                             const float& aVolume,
+                                             const bool& aMuted)
+{
+  nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(WebNavigation());
+  if (window) {
+    RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
+    MOZ_ASSERT(service);
+
+    service->SetAudioChannelVolume(window,
+                                   static_cast<AudioChannel>(aAudioChannel),
+                                   aVolume);
+    service->SetAudioChannelMuted(window,
+                                  static_cast<AudioChannel>(aAudioChannel),
+                                  aMuted);
+  }
+
+  return true;
+}
+
+bool
 TabChild::RecvDestroy()
 {
   MOZ_ASSERT(mDestroyed == false);
   mDestroyed = true;
 
   nsTArray<PContentPermissionRequestChild*> childArray =
       nsContentPermissionUtils::GetContentPermissionRequestChildById(GetTabId());
 
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -499,16 +499,20 @@ public:
     virtual bool RecvUIResolutionChanged(const float& aDpi, const double& aScale) override;
 
     virtual bool RecvThemeChanged(nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache) override;
 
     virtual bool RecvHandleAccessKey(nsTArray<uint32_t>&& aCharCodes,
                                      const bool& aIsTrusted,
                                      const int32_t& aModifierMask) override;
 
+    virtual bool RecvAudioChannelChangeNotification(const uint32_t& aAudioChannel,
+                                                    const float& aVolume,
+                                                    const bool& aMuted) override;
+
     /**
      * Native widget remoting protocol for use with windowed plugins with e10s.
      */
     PPluginWidgetChild* AllocPPluginWidgetChild() override;
     bool DeallocPPluginWidgetChild(PPluginWidgetChild* aActor) override;
     nsresult CreatePluginWidget(nsIWidget* aParent, nsIWidget** aOut);
 
     LayoutDeviceIntPoint GetChromeDisplacement() { return mChromeDisp; };
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -179,17 +179,17 @@ private:
             FileDescriptor::PlatformHandleType handle =
                 FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(mFD));
             fd = FileDescriptor(handle);
         }
 
         // Our TabParent may have been destroyed already.  If so, don't send any
         // fds over, just go back to the IO thread and close them.
         if (!tabParent->IsDestroyed()) {
-            mozilla::Unused << tabParent->SendCacheFileDescriptor(mPath, fd);
+            Unused << tabParent->SendCacheFileDescriptor(mPath, fd);
         }
 
         if (!mFD) {
             return;
         }
 
         nsCOMPtr<nsIEventTarget> eventTarget;
         mEventTarget.swap(eventTarget);
@@ -227,17 +227,17 @@ private:
         OpenBlobImpl();
 
         if (NS_FAILED(NS_DispatchToMainThread(this))) {
             NS_WARNING("Failed to dispatch to main thread!");
 
             // Intentionally leak the runnable (but not the fd) rather
             // than crash when trying to release a main thread object
             // off the main thread.
-            mozilla::Unused << mTabParent.forget();
+            Unused << mTabParent.forget();
             CloseFile();
         }
     }
 
     void CloseFile()
     {
         // It's possible for this to happen on the main thread if the dispatch
         // to the stream service fails after we've already opened the file so
@@ -382,16 +382,21 @@ TabParent::AddWindowListeners()
         eventTarget->AddEventListener(NS_LITERAL_STRING("MozUpdateWindowPos"),
                                       this, false, false);
       }
     }
     if (nsIPresShell* shell = mFrameElement->OwnerDoc()->GetShell()) {
       mPresShellWithRefreshListener = shell;
       shell->AddPostRefreshObserver(this);
     }
+
+    RefPtr<AudioChannelService> acs = AudioChannelService::GetOrCreate();
+    if (acs) {
+      acs->RegisterTabParent(this);
+    }
   }
 }
 
 void
 TabParent::RemoveWindowListeners()
 {
   if (mFrameElement && mFrameElement->OwnerDoc()->GetWindow()) {
     nsCOMPtr<nsPIDOMWindow> window = mFrameElement->OwnerDoc()->GetWindow();
@@ -400,16 +405,21 @@ TabParent::RemoveWindowListeners()
       eventTarget->RemoveEventListener(NS_LITERAL_STRING("MozUpdateWindowPos"),
                                        this, false);
     }
   }
   if (mPresShellWithRefreshListener) {
     mPresShellWithRefreshListener->RemovePostRefreshObserver(this);
     mPresShellWithRefreshListener = nullptr;
   }
+
+  RefPtr<AudioChannelService> acs = AudioChannelService::GetOrCreate();
+  if (acs) {
+    acs->UnregisterTabParent(this);
+  }
 }
 
 void
 TabParent::DidRefresh()
 {
   if (mChromeOffset != -GetChildProcessOffset()) {
     UpdatePosition();
   }
@@ -2631,17 +2641,16 @@ TabParent::RecvAudioChannelActivityNotif
                                                 const bool& aActive)
 {
   if (aAudioChannel >= NUMBER_OF_AUDIO_CHANNELS) {
     return false;
   }
 
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (os) {
-    RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
     nsAutoCString topic;
     topic.Assign("audiochannel-activity-");
     topic.Append(AudioChannelService::GetAudioChannelTable()[aAudioChannel].tag);
 
     os->NotifyObservers(NS_ISUPPORTS_CAST(nsITabParent*, this),
                         topic.get(),
                         aActive ? MOZ_UTF16("active") : MOZ_UTF16("inactive"));
   }
@@ -3392,16 +3401,43 @@ TabParent::GetShowInfo()
     return ShowInfo(name, allowFullscreen, isPrivate, false,
                     mDPI, mDefaultScale.scale);
   }
 
   return ShowInfo(EmptyString(), false, false, false,
                   mDPI, mDefaultScale.scale);
 }
 
+void
+TabParent::AudioChannelChangeNotification(nsPIDOMWindow* aWindow,
+                                          AudioChannel aAudioChannel,
+                                          float aVolume,
+                                          bool aMuted)
+{
+  if (!mFrameElement || !mFrameElement->OwnerDoc()) {
+    return;
+  }
+
+  nsCOMPtr<nsPIDOMWindow> window = mFrameElement->OwnerDoc()->GetWindow();
+  while (window) {
+    if (window == aWindow) {
+      Unused << SendAudioChannelChangeNotification(static_cast<uint32_t>(aAudioChannel),
+                                                   aVolume, aMuted);
+      break;
+    }
+
+    nsCOMPtr<nsPIDOMWindow> win = window->GetScriptableParent();
+    if (window == win) {
+      break;
+    }
+
+    window = win;
+  }
+}
+
 NS_IMETHODIMP
 FakeChannel::OnAuthAvailable(nsISupports *aContext, nsIAuthInformation *aAuthInfo)
 {
   nsAuthInformationHolder* holder =
     static_cast<nsAuthInformationHolder*>(aAuthInfo);
 
   if (!net::gNeckoChild->SendOnAuthAvailable(mCallbackId,
                                              holder->User(),
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -4,16 +4,17 @@
  * 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_tabs_TabParent_h
 #define mozilla_tabs_TabParent_h
 
 #include "js/TypeDecls.h"
 #include "mozilla/ContentCache.h"
+#include "mozilla/dom/AudioChannelBinding.h"
 #include "mozilla/dom/ipc/IdType.h"
 #include "mozilla/dom/PBrowserParent.h"
 #include "mozilla/dom/PContent.h"
 #include "mozilla/dom/PFilePickerParent.h"
 #include "mozilla/dom/TabContext.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/RefPtr.h"
@@ -450,16 +451,21 @@ public:
                                int32_t& aDragAreaX, int32_t& aDragAreaY);
     layout::RenderFrameParent* GetRenderFrame();
 
     // Called by HttpChannelParent. The function may use a new process to
     // reload the URI associated with the given channel.
     void OnStartSignedPackageRequest(nsIChannel* aChannel,
                                      const nsACString& aPackageId);
 
+    void AudioChannelChangeNotification(nsPIDOMWindow* aWindow,
+                                        AudioChannel aAudioChannel,
+                                        float aVolume,
+                                        bool aMuted);
+
 protected:
     bool ReceiveMessage(const nsString& aMessage,
                         bool aSync,
                         ipc::StructuredCloneData* aData,
                         mozilla::jsipc::CpowHolder* aCpows,
                         nsIPrincipal* aPrincipal,
                         nsTArray<ipc::StructuredCloneData>* aJSONRetVal = nullptr);
 
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1564,17 +1564,16 @@ MediaManager::Get() {
     if (!sSingleton->mMediaThread->StartWithOptions(options)) {
       MOZ_CRASH();
     }
 
     LOG(("New Media thread for gum"));
 
     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     if (obs) {
-      obs->AddObserver(sSingleton, "xpcom-will-shutdown", false);
       obs->AddObserver(sSingleton, "last-pb-context-exited", false);
       obs->AddObserver(sSingleton, "getUserMedia:privileged:allow", false);
       obs->AddObserver(sSingleton, "getUserMedia:response:allow", false);
       obs->AddObserver(sSingleton, "getUserMedia:response:deny", false);
       obs->AddObserver(sSingleton, "getUserMedia:revoke", false);
       obs->AddObserver(sSingleton, "phone-state-changed", false);
     }
     // else MediaManager won't work properly and will leak (see bug 837874)
@@ -2601,17 +2600,16 @@ MediaManager::Shutdown()
   MOZ_ASSERT(NS_IsMainThread());
   if (sInShutdown) {
     return;
   }
   sInShutdown = true;
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
 
-  obs->RemoveObserver(this, "xpcom-will-shutdown");
   obs->RemoveObserver(this, "last-pb-context-exited");
   obs->RemoveObserver(this, "getUserMedia:privileged:allow");
   obs->RemoveObserver(this, "getUserMedia:response:allow");
   obs->RemoveObserver(this, "getUserMedia:response:deny");
   obs->RemoveObserver(this, "getUserMedia:revoke");
 
   nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
   if (prefs) {
@@ -2699,19 +2697,16 @@ MediaManager::Observe(nsISupports* aSubj
 
   if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
     nsCOMPtr<nsIPrefBranch> branch( do_QueryInterface(aSubject) );
     if (branch) {
       GetPrefs(branch,NS_ConvertUTF16toUTF8(aData).get());
       LOG(("%s: %dx%d @%dfps (min %d)", __FUNCTION__,
            mPrefs.mWidth, mPrefs.mHeight, mPrefs.mFPS, mPrefs.mMinFPS));
     }
-  } else if (!strcmp(aTopic, "xpcom-will-shutdown")) {
-    Shutdown();
-    return NS_OK;
   } else if (!strcmp(aTopic, "last-pb-context-exited")) {
     // Clear memory of private-browsing-specific deviceIds. Fire and forget.
     media::SanitizeOriginKeys(0, true);
     return NS_OK;
   } else if (!strcmp(aTopic, "getUserMedia:privileged:allow") ||
              !strcmp(aTopic, "getUserMedia:response:allow")) {
     nsString key(aData);
     nsAutoPtr<GetUserMediaTask> task;
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -3021,16 +3021,32 @@ NS_IMETHODIMP nsPluginInstanceOwner::Cre
   }
 #endif
 
   mWidgetCreationComplete = true;
 
   return NS_OK;
 }
 
+#if defined(XP_WIN)
+// See QUIRK_FLASH_FIXUP_MOUSE_CURSOR
+void
+nsPluginInstanceOwner::ResetWidgetCursorCaching()
+{
+  if (!mPluginFrame || !XRE_IsContentProcess()) {
+    return;
+  }
+  nsIWidget* aWidget = mPluginFrame->GetNearestWidget();
+  if (!aWidget) {
+    return;
+  }
+  aWidget->ClearCachedCursor();
+}
+#endif
+
 // Mac specific code to fix up the port location and clipping region
 #ifdef XP_MACOSX
 
 void nsPluginInstanceOwner::FixUpPluginWindow(int32_t inPaintState)
 {
   if (!mPluginWindow || !mInstance || !mPluginFrame) {
     return;
   }
--- a/dom/plugins/base/nsPluginInstanceOwner.h
+++ b/dom/plugins/base/nsPluginInstanceOwner.h
@@ -251,16 +251,21 @@ public:
   // Called from AndroidJNI when we removed the fullscreen view.
   static void ExitFullScreen(jobject view);
 #endif
 
   void NotifyHostAsyncInitFailed();
   void NotifyHostCreateWidget();
   void NotifyDestroyPending();
 
+#if defined(XP_WIN)
+  // See QUIRK_FLASH_FIXUP_MOUSE_CURSOR
+  void ResetWidgetCursorCaching();
+#endif
+
 private:
   virtual ~nsPluginInstanceOwner();
 
   // return FALSE if LayerSurface dirty (newly created and don't have valid plugin content yet)
   bool IsUpToDate()
   {
     nsIntSize size;
     return NS_SUCCEEDED(mInstance->GetImageSize(&size)) &&
--- a/dom/plugins/ipc/PPluginInstance.ipdl
+++ b/dom/plugins/ipc/PPluginInstance.ipdl
@@ -284,16 +284,19 @@ parent:
   /* NPN_NewStream */
   intr PPluginStream(nsCString mimeType,
                     nsCString target)
     returns (NPError result);
 
 parent:
   intr PluginFocusChange(bool gotFocus);
 
+  // Notify the instance parent that this flash instance just set the cursor.
+  async PluginDidSetCursor();
+
 child:
   intr SetPluginFocus();
   intr UpdateWindow();
 
   async PPluginBackgroundDestroyer();
 };
 
 } // namespace plugins
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -189,19 +189,18 @@ PluginInstanceChild::PluginInstanceChild
     mWsInfo.display = nullptr;
     mXtClient.top_widget = nullptr;
 #else
     mWsInfo.display = DefaultXDisplay();
 #endif
 #endif // MOZ_X11 && XP_UNIX && !XP_MACOSX
 #if defined(OS_WIN)
     InitPopupMenuHook();
-    if (GetQuirks() & QUIRK_UNITY_FIXUP_MOUSE_CAPTURE) {
-        SetUnityHooks();
-    }
+    InitSetCursorHook();
+    SetUnityHooks();
 #endif // OS_WIN
 }
 
 PluginInstanceChild::~PluginInstanceChild()
 {
 #if defined(OS_WIN)
     NS_ASSERTION(!mPluginWindowHWND, "Destroying PluginInstanceChild without NPP_Destroy?");
     if (GetQuirks() & QUIRK_UNITY_FIXUP_MOUSE_CAPTURE) {
@@ -1752,16 +1751,18 @@ PluginInstanceChild::HookSetWindowLongPt
         sUser32Intercept.AddHook("SetWindowLongA", reinterpret_cast<intptr_t>(SetWindowLongAHook),
                                  (void**) &sUser32SetWindowLongAHookStub);
     if (!sUser32SetWindowLongWHookStub)
         sUser32Intercept.AddHook("SetWindowLongW", reinterpret_cast<intptr_t>(SetWindowLongWHook),
                                  (void**) &sUser32SetWindowLongWHookStub);
 #endif
 }
 
+/* QUIRK_UNITY_FIXUP_MOUSE_CAPTURE */
+
 class SetCaptureHookData
 {
 public:
     explicit SetCaptureHookData(HWND aHwnd)
         : mHwnd(aHwnd)
         , mHaveRect(false)
     {
         MOZ_ASSERT(aHwnd);
@@ -1885,17 +1886,17 @@ PluginInstanceChild::UnitySendMessageHoo
             sSetCaptureHookData->IsUnityLosingCapture(*info)) {
             sSetCaptureHookData = nullptr;
         }
     }
 
     return CallNextHookEx(0, aCode, aWparam, aLParam);
 }
 
-/* windowless track popup menu helpers */
+/* QUIRK_WINLESS_TRACKPOPUP_HOOK */
 
 BOOL
 WINAPI
 PluginInstanceChild::TrackPopupHookProc(HMENU hMenu,
                                         UINT uFlags,
                                         int x,
                                         int y,
                                         int nReserved,
@@ -1986,16 +1987,48 @@ PluginInstanceChild::CreateWinlessPopupS
 void
 PluginInstanceChild::DestroyWinlessPopupSurrogate()
 {
     if (mWinlessPopupSurrogateHWND)
         DestroyWindow(mWinlessPopupSurrogateHWND);
     mWinlessPopupSurrogateHWND = nullptr;
 }
 
+/* QUIRK_FLASH_FIXUP_MOUSE_CURSOR */
+
+typedef HCURSOR
+(WINAPI *User32SetCursor)(HCURSOR hCursor);
+static User32SetCursor sUser32SetCursorHookStub = nullptr;
+static bool sFlashChangedCursor;
+
+HCURSOR
+WINAPI
+PluginInstanceChild::SetCursorHookProc(HCURSOR hCursor)
+{
+    if (!sUser32SetCursorHookStub) {
+        NS_ERROR("SetCursor stub isn't set! Badness!");
+        return nullptr;
+    }
+    sFlashChangedCursor = true;
+    return sUser32SetCursorHookStub(hCursor);
+}
+
+
+void
+PluginInstanceChild::InitSetCursorHook()
+{
+  if (!(GetQuirks() & QUIRK_FLASH_FIXUP_MOUSE_CURSOR) ||
+      sUser32SetCursorHookStub) {
+      return;
+  }
+
+  sUser32Intercept.AddHook("SetCursor", reinterpret_cast<intptr_t>(SetCursorHookProc),
+                            (void**) &sUser32SetCursorHookStub);
+}
+
 int16_t
 PluginInstanceChild::WinlessHandleEvent(NPEvent& event)
 {
     if (!mPluginIface->event)
         return false;
 
     // Events that might generate nested event dispatch loops need
     // special handling during delivery.
@@ -2170,19 +2203,32 @@ PluginInstanceChild::FlashThrottleAsyncM
 {
     RemoveFromAsyncList();
 
     // GetProc() checks mInstance, and pulls the procedure from
     // PluginInstanceChild. We don't transport sub-class procedure
     // ptrs around in FlashThrottleAsyncMsg msgs.
     if (!GetProc())
         return;
-  
-    // deliver the event to flash 
+
+    // Update the static cursor changed flag
+    sFlashChangedCursor = false;
+
+    // deliver the event to flash
     CallWindowProc(GetProc(), GetWnd(), GetMsg(), GetWParam(), GetLParam());
+
+    // Notify our parent that the cursor changed. It's possible for
+    // CallWindowProc to get wrapped up in message processing which
+    // might result in this code getting serviced by the wrong instance.
+    // In practice though this doesn't seem to happen even with multiple
+    // instances running in multiple windows.
+    if (!mWindowed && sFlashChangedCursor && mInstance) {
+        sFlashChangedCursor = false;
+        mInstance->SendPluginDidSetCursor();
+    }
 }
 
 void
 PluginInstanceChild::FlashThrottleMessage(HWND aWnd,
                                           UINT aMsg,
                                           WPARAM aWParam,
                                           LPARAM aLParam,
                                           bool isWindowed)
--- a/dom/plugins/ipc/PluginInstanceChild.h
+++ b/dom/plugins/ipc/PluginInstanceChild.h
@@ -295,16 +295,17 @@ private:
     static bool RegisterWindowClass();
     bool CreatePluginWindow();
     void DestroyPluginWindow();
     void SizePluginWindow(int width, int height);
     int16_t WinlessHandleEvent(NPEvent& event);
     void CreateWinlessPopupSurrogate();
     void DestroyWinlessPopupSurrogate();
     void InitPopupMenuHook();
+    void InitSetCursorHook();
     void SetupFlashMsgThrottle();
     void UnhookWinlessFlashThrottle();
     void HookSetWindowLongPtr();
     void SetUnityHooks();
     void ClearUnityHooks();
     static inline bool SetWindowLongHookCheck(HWND hWnd,
                                                 int nIndex,
                                                 LONG_PTR newLong);
@@ -328,16 +329,17 @@ private:
     static LRESULT CALLBACK UnityGetMessageHookProc(int aCode, WPARAM aWparam, LPARAM aLParam);
     static LRESULT CALLBACK UnitySendMessageHookProc(int aCode, WPARAM aWparam, LPARAM aLParam);
     static BOOL CALLBACK EnumThreadWindowsCallback(HWND hWnd,
                                                    LPARAM aParam);
     static LRESULT CALLBACK WinlessHiddenFlashWndProc(HWND hWnd,
                                                       UINT message,
                                                       WPARAM wParam,
                                                       LPARAM lParam);
+    static HCURSOR WINAPI SetCursorHookProc(HCURSOR hCursor);
 #ifdef _WIN64
     static LONG_PTR WINAPI SetWindowLongPtrAHook(HWND hWnd,
                                                  int nIndex,
                                                  LONG_PTR newLong);
     static LONG_PTR WINAPI SetWindowLongPtrWHook(HWND hWnd,
                                                  int nIndex,
                                                  LONG_PTR newLong);
                       
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -2363,16 +2363,33 @@ PluginInstanceParent::Cast(NPP aInstance
 
     if (aSurrogate) {
         *aSurrogate = resolver->GetAsyncSurrogate();
     }
 
     return instancePtr;
 }
 
+bool
+PluginInstanceParent::RecvPluginDidSetCursor()
+{
+#if defined(OS_WIN)
+    nsPluginInstanceOwner* owner = GetOwner();
+    if (!owner) {
+        return true;
+    }
+    owner->ResetWidgetCursorCaching();
+    return true;
+#else
+    NS_NOTREACHED("PluginInstanceParent::RecvPluginDidSetCursor not implemented!");
+    return false;
+#endif
+}
+
+
 void
 PluginInstanceParent::RecordDrawingModel()
 {
     int mode = -1;
     switch (mWindowType) {
     case NPWindowTypeWindow:
         // We use 0=windowed since there is no specific NPDrawingModel value.
         mode = 0;
--- a/dom/plugins/ipc/PluginInstanceParent.h
+++ b/dom/plugins/ipc/PluginInstanceParent.h
@@ -347,16 +347,19 @@ public:
 
     virtual PluginAsyncSurrogate* GetAsyncSurrogate() override;
 
     virtual PluginInstanceParent* GetInstance() override { return this; }
 
     static PluginInstanceParent* Cast(NPP instance,
                                       PluginAsyncSurrogate** aSurrogate = nullptr);
 
+    virtual bool
+    RecvPluginDidSetCursor() override;
+
 private:
     // Create an appropriate platform surface for a background of size
     // |aSize|.  Return true if successful.
     bool CreateBackground(const nsIntSize& aSize);
     void DestroyBackground();
     SurfaceDescriptor BackgroundDescriptor() /*const*/;
 
     typedef mozilla::layers::ImageContainer ImageContainer;
--- a/dom/plugins/ipc/PluginQuirks.cpp
+++ b/dom/plugins/ipc/PluginQuirks.cpp
@@ -29,16 +29,17 @@ int GetQuirksFromMimeTypeAndFilename(con
     if (specialType == nsPluginHost::eSpecialType_Flash) {
         quirks |= QUIRK_FLASH_RETURN_EMPTY_DOCUMENT_ORIGIN;
 #ifdef OS_WIN
         quirks |= QUIRK_WINLESS_TRACKPOPUP_HOOK;
         quirks |= QUIRK_FLASH_THROTTLE_WMUSER_EVENTS;
         quirks |= QUIRK_FLASH_HOOK_SETLONGPTR;
         quirks |= QUIRK_FLASH_HOOK_GETWINDOWINFO;
         quirks |= QUIRK_FLASH_FIXUP_MOUSE_CAPTURE;
+        quirks |= QUIRK_FLASH_FIXUP_MOUSE_CURSOR;
 #endif
     }
 
 #ifdef OS_WIN
     // QuickTime plugin usually loaded with audio/mpeg mimetype
     NS_NAMED_LITERAL_CSTRING(quicktime, "npqtplugin");
     if (FindInReadable(quicktime, aPluginFilename)) {
         quirks |= QUIRK_QUICKTIME_AVOID_SETWINDOW;
--- a/dom/plugins/ipc/PluginQuirks.h
+++ b/dom/plugins/ipc/PluginQuirks.h
@@ -53,16 +53,24 @@ enum PluginQuirks {
   // outside of that call.  See bug 804606.
   QUIRK_FLASH_AVOID_CGMODE_CRASHES                = 1 << 10,
   // Work around a Flash bug where it fails to check the error code of a
   // NPN_GetValue(NPNVdocumentOrigin) call before trying to dereference
   // its char* output.
   QUIRK_FLASH_RETURN_EMPTY_DOCUMENT_ORIGIN        = 1 << 11,
   // Win: Addresses a Unity bug with mouse capture.
   QUIRK_UNITY_FIXUP_MOUSE_CAPTURE                 = 1 << 12,
+  // Win, Flash, windowless, e10s: Works around a bug where flash uses a
+  // delayed timer to update the cursor after a mouse move. If the mouse
+  // leaves a plugin frame associated with the plugin, the cursor may not
+  // get updated. With this quirk set, instances communicates cursor changes
+  // to the plugin frame, which reset cursor state caching on the PuppetWidget
+  // associated with the plugin. This in turn triggers cursor updates if the
+  // mouse is hovering over non-plugin content.
+  QUIRK_FLASH_FIXUP_MOUSE_CURSOR                  = 1 << 13,
 };
 
 int GetQuirksFromMimeTypeAndFilename(const nsCString& aMimeType,
                                      const nsCString& aPluginFilename);
 
 } /* namespace plugins */
 } /* namespace mozilla */
 
--- a/dom/security/nsCSPParser.cpp
+++ b/dom/security/nsCSPParser.cpp
@@ -117,22 +117,25 @@ nsCSPTokenizer::tokenizeCSPPolicy(const 
 }
 
 /* ===== nsCSPParser ==================== */
 
 nsCSPParser::nsCSPParser(cspTokens& aTokens,
                          nsIURI* aSelfURI,
                          nsCSPContext* aCSPContext,
                          bool aDeliveredViaMetaTag)
- : mHasHashOrNonce(false)
+ : mCurChar(nullptr)
+ , mEndChar(nullptr)
+ , mHasHashOrNonce(false)
  , mUnsafeInlineKeywordSrc(nullptr)
  , mChildSrc(nullptr)
  , mFrameSrc(nullptr)
  , mTokens(aTokens)
  , mSelfURI(aSelfURI)
+ , mPolicy(nullptr)
  , mCSPContext(aCSPContext)
  , mDeliveredViaMetaTag(aDeliveredViaMetaTag)
 {
   CSPPARSERLOG(("nsCSPParser::nsCSPParser"));
 }
 
 nsCSPParser::~nsCSPParser()
 {
--- a/dom/system/gonk/AudioChannelManager.cpp
+++ b/dom/system/gonk/AudioChannelManager.cpp
@@ -208,16 +208,16 @@ AudioChannelManager::GetAllowedAudioChan
 
   nsAutoString manifestURL;
   aRv = appsService->GetManifestURLByLocalId(appId, manifestURL);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
   nsBrowserElement::GenerateAllowedAudioChannels(window, nullptr, nullptr,
-                                                 manifestURL, aAudioChannels,
-                                                 aRv);
+                                                 manifestURL, nullptr,
+                                                 aAudioChannels, aRv);
   NS_WARN_IF(aRv.Failed());
 }
 
 } // namespace system
 } // namespace dom
 } // namespace mozilla
--- a/dom/workers/ServiceWorkerClients.cpp
+++ b/dom/workers/ServiceWorkerClients.cpp
@@ -610,20 +610,19 @@ private:
                            false, false, true, nullptr, nullptr,
                            getter_AddRefs(newWindow));
       nsCOMPtr<nsPIDOMWindow> pwindow = do_QueryInterface(newWindow);
       pwindow.forget(aWindow);
       return NS_OK;
     }
 
     // Find the most recent browser window and open a new tab in it.
-    nsCOMPtr<nsIDOMWindow> browserWindow;
-    rv = wm->GetMostRecentWindow(MOZ_UTF16("navigator:browser"),
-                                 getter_AddRefs(browserWindow));
-    if (NS_WARN_IF(NS_FAILED(rv)) || !browserWindow) {
+    nsCOMPtr<nsPIDOMWindow> browserWindow =
+      nsContentUtils::GetMostRecentNonPBWindow();
+    if (!browserWindow) {
       // It is possible to be running without a browser window on Mac OS, so
       // we need to open a new chrome window.
       // TODO(catalinb): open new chrome window. Bug 1218080
       return NS_ERROR_FAILURE;
     }
 
     nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(browserWindow);
     if (NS_WARN_IF(!chromeWin)) {
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -16,16 +16,17 @@
 #include "nsIHttpChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIHttpHeaderVisitor.h"
 #include "nsIJARChannel.h"
 #include "nsINetworkInterceptController.h"
 #include "nsIMutableArray.h"
 #include "nsIScriptError.h"
 #include "nsISimpleEnumerator.h"
+#include "nsITimer.h"
 #include "nsIUploadChannel2.h"
 #include "nsPIDOMWindow.h"
 #include "nsScriptLoader.h"
 #include "nsServiceManagerUtils.h"
 #include "nsDebug.h"
 #include "nsISupportsPrimitives.h"
 
 #include "jsapi.h"
@@ -130,16 +131,19 @@ struct ServiceWorkerManager::Registratio
   nsTArray<nsCString> mOrderedScopes;
 
   // Scope to registration.
   // The scope should be a fully qualified valid URL.
   nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerRegistrationInfo> mInfos;
 
   // Maps scopes to job queues.
   nsClassHashtable<nsCStringHashKey, ServiceWorkerJobQueue> mJobQueues;
+
+  // Map scopes to scheduled update timers.
+  nsInterfaceHashtable<nsCStringHashKey, nsITimer> mUpdateTimers;
 };
 
 struct ServiceWorkerManager::PendingOperation final
 {
   nsCOMPtr<nsIRunnable> mRunnable;
 
   ServiceWorkerJobQueue* mQueue;
   RefPtr<ServiceWorkerJob> mJob;
@@ -313,17 +317,23 @@ PopulateRegistrationData(nsIPrincipal* a
   }
 
   nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &aData.principal());
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   aData.scope() = aRegistration->mScope;
-  aData.scriptSpec() = aRegistration->mScriptSpec;
+
+  RefPtr<ServiceWorkerInfo> newest = aRegistration->Newest();
+  if (NS_WARN_IF(!newest)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  aData.scriptSpec() = newest->ScriptSpec();
 
   if (aRegistration->mActiveWorker) {
     aData.currentWorkerURL() = aRegistration->mActiveWorker->ScriptSpec();
     aData.activeCacheName() = aRegistration->mActiveWorker->CacheName();
   }
 
   if (aRegistration->mWaitingWorker) {
     aData.waitingCacheName() = aRegistration->mWaitingWorker->CacheName();
@@ -417,19 +427,20 @@ ServiceWorkerRegistrationInfo::Clear()
                                                  WhichServiceWorker::INSTALLING_WORKER |
                                                  WhichServiceWorker::WAITING_WORKER |
                                                  WhichServiceWorker::ACTIVE_WORKER);
 }
 
 ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(const nsACString& aScope,
                                                              nsIPrincipal* aPrincipal)
   : mControlledDocumentsCounter(0)
+  , mUpdateState(NoUpdate)
+  , mLastUpdateCheckTime(0)
   , mScope(aScope)
   , mPrincipal(aPrincipal)
-  , mLastUpdateCheckTime(0)
   , mUpdating(false)
   , mPendingUninstall(false)
 {}
 
 ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo()
 {
   if (IsControllingDocuments()) {
     NS_WARNING("ServiceWorkerRegistrationInfo is still controlling documents. This can be a bug or a leak in ServiceWorker API or in any other API that takes the document alive.");
@@ -453,17 +464,20 @@ ServiceWorkerRegistrationInfo::GetScope(
   CopyUTF8toUTF16(mScope, aScope);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ServiceWorkerRegistrationInfo::GetScriptSpec(nsAString& aScriptSpec)
 {
   AssertIsOnMainThread();
-  CopyUTF8toUTF16(mScriptSpec, aScriptSpec);
+  RefPtr<ServiceWorkerInfo> newest = Newest();
+  if (newest) {
+    CopyUTF8toUTF16(newest->ScriptSpec(), aScriptSpec);
+  }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ServiceWorkerRegistrationInfo::GetInstallingWorker(nsIServiceWorkerInfo **aResult)
 {
   AssertIsOnMainThread();
   nsCOMPtr<nsIServiceWorkerInfo> info = do_QueryInterface(mInstallingWorker);
@@ -947,22 +961,52 @@ protected:
   RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
   bool mCanceled;
   RefPtr<ServiceWorkerRegistrationInfo> mRegistration;
   RefPtr<ServiceWorkerInfo> mUpdateAndInstallInfo;
 
   ~ServiceWorkerJobBase()
   { }
 
+  void
+  Succeed()
+  {
+    AssertIsOnMainThread();
+    // We don't have a callback for soft updates.
+    if (mCallback) {
+      mCallback->UpdateSucceeded(mRegistration);
+      mCallback = nullptr;
+    }
+  }
+};
+
+// Base type for jobs that work with a specific service worker script.
+class ServiceWorkerScriptJobBase : public ServiceWorkerJobBase
+{
+protected:
+  const nsCString mScriptSpec;
+
+  ServiceWorkerScriptJobBase(ServiceWorkerJobQueue* aQueue,
+                             ServiceWorkerJob::Type aJobType,
+                             ServiceWorkerUpdateFinishCallback* aCallback,
+                             ServiceWorkerRegistrationInfo* aRegistration,
+                             ServiceWorkerInfo* aServiceWorkerInfo,
+                             const nsACString& aScriptSpec)
+    : ServiceWorkerJobBase(aQueue, aJobType, aCallback, aRegistration,
+                           aServiceWorkerInfo)
+    , mScriptSpec(aScriptSpec)
+  {
+  }
+
   // This MUST only be called when the job is still performing actions related
   // to registration or update. After the spec resolves the update promise, use
   // Done() with the failure code instead.
   // Callers MUST hold a strong ref before calling this!
   void
-  Fail(ErrorResult& aRv)
+  FailWithErrorResult(ErrorResult& aRv)
   {
     AssertIsOnMainThread();
     MOZ_ASSERT(mRegistration);
 
     // With cancellation support, we may only be running with one reference
     // from another object like a stream loader or something.
     RefPtr<ServiceWorkerJob> kungFuDeathGrip = this;
 
@@ -972,17 +1016,17 @@ protected:
 
     // Ensure that we only surface SecurityErr or TypeErr to script.
     if (aRv.Failed() && !aRv.ErrorCodeIs(NS_ERROR_DOM_SECURITY_ERR) &&
                         !aRv.ErrorCodeIs(NS_ERROR_DOM_TYPE_ERR)) {
 
       // Remove the old error code so we can replace it with a TypeError.
       aRv.SuppressException();
 
-      NS_ConvertUTF8toUTF16 scriptSpec(mRegistration->mScriptSpec);
+      NS_ConvertUTF8toUTF16 scriptSpec(mScriptSpec);
       NS_ConvertUTF8toUTF16 scope(mRegistration->mScope);
 
       // Throw the type error with a generic error message.
       aRv.ThrowTypeError<MSG_SW_INSTALL_ERROR>(scriptSpec, scope);
     }
 
     if (mCallback) {
       mCallback->UpdateFailed(aRv);
@@ -1006,42 +1050,33 @@ protected:
     mRegistration = nullptr;
     Done(origStatus);
   }
 
   void
   Fail(nsresult aRv)
   {
     ErrorResult rv(aRv);
-    Fail(rv);
-  }
-
-  void
-  Succeed()
-  {
-    AssertIsOnMainThread();
-    // We don't have a callback for soft updates.
-    if (mCallback) {
-      mCallback->UpdateSucceeded(mRegistration);
-      mCallback = nullptr;
-    }
+    FailWithErrorResult(rv);
   }
 };
 
-class ServiceWorkerInstallJob final : public ServiceWorkerJobBase
+class ServiceWorkerInstallJob final : public ServiceWorkerScriptJobBase
 {
   friend class ContinueInstallTask;
 
 public:
   ServiceWorkerInstallJob(ServiceWorkerJobQueue* aQueue,
                           ServiceWorkerUpdateFinishCallback* aCallback,
                           ServiceWorkerRegistrationInfo* aRegistration,
-                          ServiceWorkerInfo* aServiceWorkerInfo)
-    : ServiceWorkerJobBase(aQueue, Type::InstallJob, aCallback,
-                           aRegistration, aServiceWorkerInfo)
+                          ServiceWorkerInfo* aServiceWorkerInfo,
+                          const nsACString& aScriptSpec)
+    : ServiceWorkerScriptJobBase(aQueue, Type::InstallJob, aCallback,
+                                 aRegistration, aServiceWorkerInfo,
+                                 aScriptSpec)
   {
     MOZ_ASSERT(aRegistration);
   }
 
   void
   Start()
   {
     AssertIsOnMainThread();
@@ -1156,56 +1191,56 @@ public:
     }
 
     Done(NS_OK);
     // Activate() is invoked out of band of atomic.
     mRegistration->TryToActivate();
   }
 };
 
-class ServiceWorkerRegisterJob final : public ServiceWorkerJobBase,
+class ServiceWorkerRegisterJob final : public ServiceWorkerScriptJobBase,
                                        public serviceWorkerScriptCache::CompareCallback
 {
   friend class ContinueUpdateRunnable;
 
   nsCString mScope;
-  nsCString mScriptSpec;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<nsILoadGroup> mLoadGroup;
 
   ~ServiceWorkerRegisterJob()
   { }
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   // [[Register]]
   ServiceWorkerRegisterJob(ServiceWorkerJobQueue* aQueue,
                            const nsCString& aScope,
                            const nsCString& aScriptSpec,
                            ServiceWorkerUpdateFinishCallback* aCallback,
                            nsIPrincipal* aPrincipal,
                            nsILoadGroup* aLoadGroup)
-    : ServiceWorkerJobBase(aQueue, Type::RegisterJob, aCallback)
+    : ServiceWorkerScriptJobBase(aQueue, Type::RegisterJob, aCallback, nullptr,
+                                 nullptr, aScriptSpec)
     , mScope(aScope)
-    , mScriptSpec(aScriptSpec)
     , mPrincipal(aPrincipal)
     , mLoadGroup(aLoadGroup)
   {
     AssertIsOnMainThread();
     MOZ_ASSERT(mLoadGroup);
     MOZ_ASSERT(aCallback);
   }
 
   // [[Update]]
   ServiceWorkerRegisterJob(ServiceWorkerJobQueue* aQueue,
                            ServiceWorkerRegistrationInfo* aRegistration,
-                           ServiceWorkerUpdateFinishCallback* aCallback)
-    : ServiceWorkerJobBase(aQueue, Type::UpdateJob, aCallback,
-                           aRegistration, nullptr)
+                           ServiceWorkerUpdateFinishCallback* aCallback,
+                           const nsACString& aScriptSpec)
+    : ServiceWorkerScriptJobBase(aQueue, Type::UpdateJob, aCallback,
+                                 aRegistration, nullptr, aScriptSpec)
   {
     AssertIsOnMainThread();
   }
 
   void
   Start() override
   {
     AssertIsOnMainThread();
@@ -1220,18 +1255,17 @@ public:
     }
 
     if (mJobType == RegisterJob) {
       mRegistration = swm->GetRegistration(mPrincipal, mScope);
 
       if (mRegistration) {
         mRegistration->mPendingUninstall = false;
         RefPtr<ServiceWorkerInfo> newest = mRegistration->Newest();
-        if (newest && mScriptSpec.Equals(newest->ScriptSpec()) &&
-            mScriptSpec.Equals(mRegistration->mScriptSpec)) {
+        if (newest && mScriptSpec.Equals(newest->ScriptSpec())) {
           swm->StoreRegistration(mPrincipal, mRegistration);
           Succeed();
 
           // Done() must always be called async from Start()
           nsCOMPtr<nsIRunnable> runnable =
             NS_NewRunnableMethodWithArg<nsresult>(
               this,
               &ServiceWorkerRegisterJob::Done,
@@ -1239,21 +1273,35 @@ public:
           MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(runnable)));
 
           return;
         }
       } else {
         mRegistration = swm->CreateNewRegistration(mScope, mPrincipal);
       }
 
-      mRegistration->mScriptSpec = mScriptSpec;
-      mRegistration->NotifyListenersOnChange();
       swm->StoreRegistration(mPrincipal, mRegistration);
     } else {
       MOZ_ASSERT(mJobType == UpdateJob);
+
+      // If a different script spec has been registered between when this update
+      // was scheduled and it running now, then simply abort.
+      RefPtr<ServiceWorkerInfo> newest = mRegistration->Newest();
+      if (newest && !mScriptSpec.Equals(newest->ScriptSpec())) {
+
+        // Done() must always be called async from Start()
+        nsCOMPtr<nsIRunnable> runnable =
+          NS_NewRunnableMethodWithArg<nsresult>(
+            this,
+            &ServiceWorkerRegisterJob::Fail,
+            NS_ERROR_DOM_ABORT_ERR);
+          MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(runnable)));
+
+        return;
+      }
     }
 
     Update();
   }
 
   void
   ComparisonResult(nsresult aStatus, bool aInCacheAndEqual,
                    const nsAString& aNewCacheName,
@@ -1277,17 +1325,17 @@ public:
     }
 
     AssertIsOnMainThread();
     Telemetry::Accumulate(Telemetry::SERVICE_WORKER_UPDATED, 1);
 
     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
 
     nsCOMPtr<nsIURI> scriptURI;
-    nsresult rv = NS_NewURI(getter_AddRefs(scriptURI), mRegistration->mScriptSpec);
+    nsresult rv = NS_NewURI(getter_AddRefs(scriptURI), mScriptSpec);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       Fail(NS_ERROR_DOM_SECURITY_ERR);
       return;
     }
     nsCOMPtr<nsIURI> maxScopeURI;
     if (!aMaxScope.IsEmpty()) {
       rv = NS_NewURI(getter_AddRefs(maxScopeURI), aMaxScope,
                      nullptr, scriptURI);
@@ -1327,18 +1375,17 @@ public:
 
     ServiceWorkerManager::RegistrationDataPerPrincipal* data;
     if (!swm->mRegistrationInfos.Get(scopeKey, &data)) {
       return Fail(NS_ERROR_FAILURE);
     }
 
     MOZ_ASSERT(!mUpdateAndInstallInfo);
     mUpdateAndInstallInfo =
-      new ServiceWorkerInfo(mRegistration, mRegistration->mScriptSpec,
-                            aNewCacheName);
+      new ServiceWorkerInfo(mRegistration, mScriptSpec, aNewCacheName);
 
     RefPtr<ServiceWorkerJob> upcasted = this;
     nsMainThreadPtrHandle<nsISupports> handle(
         new nsMainThreadPtrHolder<nsISupports>(upcasted));
     RefPtr<LifeCycleEventCallback> callback = new ContinueUpdateRunnable(handle);
 
     ServiceWorkerPrivate* workerPrivate =
       mUpdateAndInstallInfo->WorkerPrivate();
@@ -1362,25 +1409,25 @@ private:
     RefPtr<ServiceWorkerRegisterJob> kungFuDeathGrip = this;
     if (mCanceled) {
       return Fail(NS_ERROR_DOM_ABORT_ERR);
     }
 
     if (NS_WARN_IF(!aScriptEvaluationResult)) {
       ErrorResult error;
 
-      NS_ConvertUTF8toUTF16 scriptSpec(mRegistration->mScriptSpec);
+      NS_ConvertUTF8toUTF16 scriptSpec(mScriptSpec);
       NS_ConvertUTF8toUTF16 scope(mRegistration->mScope);
       error.ThrowTypeError<MSG_SW_SCRIPT_THREW>(scriptSpec, scope);
-      return Fail(error);
+      return FailWithErrorResult(error);
     }
 
     RefPtr<ServiceWorkerInstallJob> job =
-      new ServiceWorkerInstallJob(mQueue, mCallback,
-                                  mRegistration, mUpdateAndInstallInfo);
+      new ServiceWorkerInstallJob(mQueue, mCallback, mRegistration,
+                                  mUpdateAndInstallInfo, mScriptSpec);
     mQueue->Append(job);
     Done(NS_OK);
   }
 
   void
   Update()
   {
     AssertIsOnMainThread();
@@ -1413,24 +1460,24 @@ private:
     }
 
     RefPtr<ServiceWorkerInfo> workerInfo = mRegistration->Newest();
     nsAutoString cacheName;
 
     // 9.2.20 If newestWorker is not null, and newestWorker's script url is
     // equal to registration's registering script url and response is a
     // byte-for-byte match with the script resource of newestWorker...
-    if (workerInfo && workerInfo->ScriptSpec().Equals(mRegistration->mScriptSpec)) {
+    if (workerInfo && workerInfo->ScriptSpec().Equals(mScriptSpec)) {
       cacheName = workerInfo->CacheName();
     }
 
     nsresult rv =
       serviceWorkerScriptCache::Compare(mRegistration, mRegistration->mPrincipal, cacheName,
-                                        NS_ConvertUTF8toUTF16(mRegistration->mScriptSpec),
-                                        this, mLoadGroup);
+                                        NS_ConvertUTF8toUTF16(mScriptSpec), this,
+                                        mLoadGroup);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return Fail(rv);
     }
   }
 
   void
   Done(nsresult aStatus)
   {
@@ -2694,38 +2741,87 @@ ServiceWorkerRegistrationInfo::NotifyLis
 {
   nsTArray<nsCOMPtr<nsIServiceWorkerRegistrationInfoListener>> listeners(mListeners);
   for (size_t index = 0; index < listeners.Length(); ++index) {
     listeners[index]->OnChange();
   }
 }
 
 void
+ServiceWorkerRegistrationInfo::MaybeScheduleTimeCheckAndUpdate()
+{
+  AssertIsOnMainThread();
+
+  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+  if (!swm) {
+    // shutting down, do nothing
+    return;
+  }
+
+  if (mUpdateState == NoUpdate) {
+    mUpdateState = NeedTimeCheckAndUpdate;
+  }
+
+  swm->ScheduleUpdateTimer(mPrincipal, mScope);
+}
+
+void
+ServiceWorkerRegistrationInfo::MaybeScheduleUpdate()
+{
+  AssertIsOnMainThread();
+
+  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+  if (!swm) {
+    // shutting down, do nothing
+    return;
+  }
+
+  mUpdateState = NeedUpdate;
+
+  swm->ScheduleUpdateTimer(mPrincipal, mScope);
+}
+
+bool
+ServiceWorkerRegistrationInfo::CheckAndClearIfUpdateNeeded()
+{
+  AssertIsOnMainThread();
+
+  bool result = mUpdateState == NeedUpdate ||
+               (mUpdateState == NeedTimeCheckAndUpdate &&
+                IsLastUpdateCheckTimeOverOneDay());
+
+  mUpdateState = NoUpdate;
+
+  return result;
+}
+
+void
 ServiceWorkerManager::LoadRegistration(
                              const ServiceWorkerRegistrationData& aRegistration)
 {
   AssertIsOnMainThread();
 
   nsCOMPtr<nsIPrincipal> principal =
     PrincipalInfoToPrincipal(aRegistration.principal());
   if (!principal) {
     return;
   }
 
   RefPtr<ServiceWorkerRegistrationInfo> registration =
     GetRegistration(principal, aRegistration.scope());
   if (!registration) {
     registration = CreateNewRegistration(aRegistration.scope(), principal);
-  } else if (registration->mScriptSpec == aRegistration.scriptSpec() &&
-             !!registration->mActiveWorker == aRegistration.currentWorkerURL().IsEmpty()) {
-    // No needs for updates.
-    return;
-  }
-
-  registration->mScriptSpec = aRegistration.scriptSpec();
+  } else {
+    RefPtr<ServiceWorkerInfo> newest = registration->Newest();
+    if (newest && newest->ScriptSpec() == aRegistration.scriptSpec() &&
+        !!registration->mActiveWorker == aRegistration.currentWorkerURL().IsEmpty()) {
+      // No needs for updates.
+      return;
+    }
+  }
 
   const nsCString& currentWorkerURL = aRegistration.currentWorkerURL();
   if (!currentWorkerURL.IsEmpty()) {
     registration->mActiveWorker =
       new ServiceWorkerInfo(registration, currentWorkerURL,
                             aRegistration.activeCacheName());
     registration->mActiveWorker->SetActivateStateUncheckedWithoutEvent(ServiceWorkerState::Activated);
   }
@@ -3019,16 +3115,22 @@ ServiceWorkerManager::RemoveScopeAndRegi
     return;
   }
 
   RegistrationDataPerPrincipal* data;
   if (!swm->mRegistrationInfos.Get(scopeKey, &data)) {
     return;
   }
 
+  nsCOMPtr<nsITimer> timer = data->mUpdateTimers.Get(aRegistration->mScope);
+  if (timer) {
+    timer->Cancel();
+    data->mUpdateTimers.Remove(aRegistration->mScope);
+  }
+
   RefPtr<ServiceWorkerRegistrationInfo> info;
   data->mInfos.Get(aRegistration->mScope, getter_AddRefs(info));
 
   data->mInfos.Remove(aRegistration->mScope);
   data->mOrderedScopes.RemoveElement(aRegistration->mScope);
   swm->NotifyListenersOnUnregister(info);
 
   swm->MaybeRemoveRegistrationInfo(scopeKey);
@@ -3079,16 +3181,37 @@ ServiceWorkerManager::MaybeStopControlli
   if (registration) {
     StopControllingADocument(registration);
   }
 
   mAllDocuments.RemoveEntry(aDoc);
 }
 
 void
+ServiceWorkerManager::MaybeCheckNavigationUpdate(nsIDocument* aDoc)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aDoc);
+  // We perform these success path navigation update steps when the
+  // document tells us its more or less done loading.  This avoids
+  // slowing down page load and also lets pages consistently get
+  // updatefound events when they fire.
+  //
+  // 9.8.20 If respondWithEntered is false, then:
+  // 9.8.22 Else: (respondWith was entered and succeeded)
+  //    If request is a non-subresource request, then: Invoke Soft Update
+  //    algorithm.
+  RefPtr<ServiceWorkerRegistrationInfo> registration;
+  mControlledDocuments.Get(aDoc, getter_AddRefs(registration));
+  if (registration) {
+    registration->MaybeScheduleUpdate();
+  }
+}
+
+void
 ServiceWorkerManager::StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
                                                 nsIDocument* aDoc,
                                                 const nsAString& aDocumentId)
 {
   MOZ_ASSERT(aRegistration);
   MOZ_ASSERT(aDoc);
 
   aRegistration->StartControllingADocument();
@@ -3601,29 +3724,27 @@ ServiceWorkerManager::SoftUpdate(const O
   // "Let newestWorker be the result of running Get Newest Worker algorithm
   // passing registration as its argument.
   // If newestWorker is null, abort these steps."
   RefPtr<ServiceWorkerInfo> newest = registration->Newest();
   if (!newest) {
     return;
   }
 
-  // "Set registration's registering script url to newestWorker's script url."
-  registration->mScriptSpec = newest->ScriptSpec();
-
   // "If the registration queue for registration is empty, invoke Update algorithm,
   // or its equivalent, with client, registration as its argument."
   // TODO(catalinb): We don't implement the force bypass cache flag.
   // See: https://github.com/slightlyoff/ServiceWorker/issues/759
   if (!registration->mUpdating) {
     ServiceWorkerJobQueue* queue = GetOrCreateJobQueue(scopeKey, aScope);
     MOZ_ASSERT(queue);
 
     RefPtr<ServiceWorkerRegisterJob> job =
-      new ServiceWorkerRegisterJob(queue, registration, nullptr);
+      new ServiceWorkerRegisterJob(queue, registration, nullptr,
+                                   newest->ScriptSpec());
     queue->Append(job);
   }
 }
 
 void
 ServiceWorkerManager::Update(nsIPrincipal* aPrincipal,
                              const nsACString& aScope,
                              ServiceWorkerUpdateFinishCallback* aCallback)
@@ -3657,27 +3778,25 @@ ServiceWorkerManager::Update(nsIPrincipa
     aCallback->UpdateFailed(error);
 
     // In case the callback does not consume the exception
     error.SuppressException();
 
     return;
   }
 
-  // "Set registration's registering script url to newestWorker's script url."
-  registration->mScriptSpec = newest->ScriptSpec();
-
   ServiceWorkerJobQueue* queue =
     GetOrCreateJobQueue(scopeKey, aScope);
   MOZ_ASSERT(queue);
 
   // "Invoke Update algorithm, or its equivalent, with client, registration as
   // its argument."
   RefPtr<ServiceWorkerRegisterJob> job =
-    new ServiceWorkerRegisterJob(queue, registration, aCallback);
+    new ServiceWorkerRegisterJob(queue, registration, aCallback,
+                                 newest->ScriptSpec());
   queue->Append(job);
 }
 
 namespace {
 
 static void
 FireControllerChangeOnDocument(nsIDocument* aDocument)
 {
@@ -4118,16 +4237,23 @@ ServiceWorkerManager::ForceUnregister(Re
   MOZ_ASSERT(aRegistration);
 
   ServiceWorkerJobQueue* queue;
   aRegistrationData->mJobQueues.Get(aRegistration->mScope, &queue);
   if (queue) {
     queue->CancelJobs();
   }
 
+  nsCOMPtr<nsITimer> timer =
+    aRegistrationData->mUpdateTimers.Get(aRegistration->mScope);
+  if (timer) {
+    timer->Cancel();
+    aRegistrationData->mUpdateTimers.Remove(aRegistration->mScope);
+  }
+
   // Since Unregister is async, it is ok to call it in an enumeration.
   Unregister(aRegistration->mPrincipal, nullptr, NS_ConvertUTF8toUTF16(aRegistration->mScope));
 }
 
 NS_IMETHODIMP
 ServiceWorkerManager::RemoveAndPropagate(const nsACString& aHost)
 {
   Remove(aHost);
@@ -4447,16 +4573,24 @@ ServiceWorkerManager::Observe(nsISupport
 
     RemoveAllRegistrations(&attrs);
     return NS_OK;
   }
 
   if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
     mShuttingDown = true;
 
+    for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
+      for (auto it2 = it1.UserData()->mUpdateTimers.Iter(); !it2.Done(); it2.Next()) {
+        nsCOMPtr<nsITimer> timer = it2.UserData();
+        timer->Cancel();
+      }
+      it1.UserData()->mUpdateTimers.Clear();
+    }
+
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
       obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
 
       if (XRE_IsParentProcess()) {
         obs->RemoveObserver(this, PURGE_SESSION_HISTORY);
         obs->RemoveObserver(this, PURGE_DOMAIN_DATA);
         obs->RemoveObserver(this, CLEAR_ORIGIN_DATA);
@@ -4651,16 +4785,153 @@ ServiceWorkerManager::RemoveNavigationIn
     if (list->IsEmpty()) {
       list = nullptr;
       nsAutoPtr<InterceptionList> doomed;
       mNavigationInterceptions.RemoveAndForget(aScope, doomed);
     }
   }
 }
 
+class UpdateTimerCallback final : public nsITimerCallback
+{
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+  const nsCString mScope;
+
+  ~UpdateTimerCallback()
+  {
+  }
+
+public:
+  UpdateTimerCallback(nsIPrincipal* aPrincipal, const nsACString& aScope)
+    : mPrincipal(aPrincipal)
+    , mScope(aScope)
+  {
+    AssertIsOnMainThread();
+    MOZ_ASSERT(mPrincipal);
+    MOZ_ASSERT(!mScope.IsEmpty());
+  }
+
+  NS_IMETHOD
+  Notify(nsITimer* aTimer) override
+  {
+    AssertIsOnMainThread();
+
+    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+    if (!swm) {
+      // shutting down, do nothing
+      return NS_OK;
+    }
+
+    swm->UpdateTimerFired(mPrincipal, mScope);
+    return NS_OK;
+  }
+
+  NS_DECL_ISUPPORTS
+};
+
+NS_IMPL_ISUPPORTS(UpdateTimerCallback, nsITimerCallback)
+
+void
+ServiceWorkerManager::ScheduleUpdateTimer(nsIPrincipal* aPrincipal,
+                                          const nsACString& aScope)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aPrincipal);
+  MOZ_ASSERT(!aScope.IsEmpty());
+
+  if (mShuttingDown) {
+    return;
+  }
+
+  nsAutoCString scopeKey;
+  nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  RegistrationDataPerPrincipal* data;
+  if (!mRegistrationInfos.Get(scopeKey, &data)) {
+    return;
+  }
+
+  nsCOMPtr<nsITimer> timer = data->mUpdateTimers.Get(aScope);
+  if (timer) {
+    // There is already a timer scheduled.  In this case just use the original
+    // schedule time.  We don't want to push it out to a later time since that
+    // could allow updates to be starved forever if events are continuously
+    // fired.
+    return;
+  }
+
+  timer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  nsCOMPtr<nsITimerCallback> callback = new UpdateTimerCallback(aPrincipal,
+                                                                aScope);
+
+  const uint32_t UPDATE_DELAY_MS = 1000;
+
+  rv = timer->InitWithCallback(callback, UPDATE_DELAY_MS,
+                               nsITimer::TYPE_ONE_SHOT);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  data->mUpdateTimers.Put(aScope, timer);
+}
+
+void
+ServiceWorkerManager::UpdateTimerFired(nsIPrincipal* aPrincipal,
+                                       const nsACString& aScope)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aPrincipal);
+  MOZ_ASSERT(!aScope.IsEmpty());
+
+  if (mShuttingDown) {
+    return;
+  }
+
+  // First cleanup the timer.
+  nsAutoCString scopeKey;
+  nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  RegistrationDataPerPrincipal* data;
+  if (!mRegistrationInfos.Get(scopeKey, &data)) {
+    return;
+  }
+
+  nsCOMPtr<nsITimer> timer = data->mUpdateTimers.Get(aScope);
+  if (timer) {
+    timer->Cancel();
+    data->mUpdateTimers.Remove(aScope);
+  }
+
+  RefPtr<ServiceWorkerRegistrationInfo> registration;
+  data->mInfos.Get(aScope, getter_AddRefs(registration));
+  if (!registration) {
+    return;
+  }
+
+  if (!registration->CheckAndClearIfUpdateNeeded()) {
+    return;
+  }
+
+  PrincipalOriginAttributes attrs =
+    mozilla::BasePrincipal::Cast(aPrincipal)->OriginAttributesRef();
+
+  // Then trigger an update to fire asynchronously now.
+  PropagateSoftUpdate(attrs, NS_ConvertUTF8toUTF16(aScope));
+}
+
 NS_IMPL_ISUPPORTS(ServiceWorkerInfo, nsIServiceWorkerInfo)
 
 NS_IMETHODIMP
 ServiceWorkerInfo::GetScriptSpec(nsAString& aScriptSpec)
 {
   AssertIsOnMainThread();
   CopyUTF8toUTF16(mScriptSpec, aScriptSpec);
   return NS_OK;
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -52,53 +52,57 @@ class ServiceWorkerJobQueue;
 class ServiceWorkerManagerChild;
 class ServiceWorkerPrivate;
 
 class ServiceWorkerRegistrationInfo final
   : public nsIServiceWorkerRegistrationInfo
 {
   uint32_t mControlledDocumentsCounter;
 
+  enum
+  {
+    NoUpdate,
+    NeedTimeCheckAndUpdate,
+    NeedUpdate
+  } mUpdateState;
+
+  uint64_t mLastUpdateCheckTime;
+
   virtual ~ServiceWorkerRegistrationInfo();
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISERVICEWORKERREGISTRATIONINFO
 
   nsCString mScope;
-  // The scriptURL for the registration. This may be completely different from
-  // the URLs of the following three workers.
-  nsCString mScriptSpec;
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
 
   RefPtr<ServiceWorkerInfo> mActiveWorker;
   RefPtr<ServiceWorkerInfo> mWaitingWorker;
   RefPtr<ServiceWorkerInfo> mInstallingWorker;
 
   nsTArray<nsCOMPtr<nsIServiceWorkerRegistrationInfoListener>> mListeners;
 
-  uint64_t mLastUpdateCheckTime;
-
   // According to the spec, Soft Update shouldn't queue an update job
   // if the registration queue is not empty. Because our job queue
   // works slightly different, we use a flag to determine if the registration
   // is already updating.
   bool mUpdating;
 
   // When unregister() is called on a registration, it is not immediately
   // removed since documents may be controlled. It is marked as
   // pendingUninstall and when all controlling documents go away, removed.
   bool mPendingUninstall;
 
   ServiceWorkerRegistrationInfo(const nsACString& aScope,
                                 nsIPrincipal* aPrincipal);
 
   already_AddRefed<ServiceWorkerInfo>
-  Newest()
+  Newest() const
   {
     RefPtr<ServiceWorkerInfo> newest;
     if (mInstallingWorker) {
       newest = mInstallingWorker;
     } else if (mWaitingWorker) {
       newest = mWaitingWorker;
     } else {
       newest = mActiveWorker;
@@ -147,16 +151,25 @@ public:
   void
   RefreshLastUpdateCheckTime();
 
   bool
   IsLastUpdateCheckTimeOverOneDay() const;
 
   void
   NotifyListenersOnChange();
+
+  void
+  MaybeScheduleTimeCheckAndUpdate();
+
+  void
+  MaybeScheduleUpdate();
+
+  bool
+  CheckAndClearIfUpdateNeeded();
 };
 
 class ServiceWorkerUpdateFinishCallback
 {
 protected:
   virtual ~ServiceWorkerUpdateFinishCallback()
   {}
 
@@ -311,18 +324,20 @@ class ServiceWorkerManager final
 {
   friend class GetReadyPromiseRunnable;
   friend class GetRegistrationsRunnable;
   friend class GetRegistrationRunnable;
   friend class ServiceWorkerJobQueue;
   friend class ServiceWorkerInstallJob;
   friend class ServiceWorkerRegisterJob;
   friend class ServiceWorkerJobBase;
+  friend class ServiceWorkerScriptJobBase;
   friend class ServiceWorkerRegistrationInfo;
   friend class ServiceWorkerUnregisterJob;
+  friend class UpdateTimerCallback;
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISERVICEWORKERMANAGER
   NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
   NS_DECL_NSIOBSERVER
 
   struct RegistrationDataPerPrincipal;
@@ -475,16 +490,20 @@ public:
 
   NS_IMETHOD
   AddRegistrationEventListener(const nsAString& aScope,
                                ServiceWorkerRegistrationListener* aListener);
 
   NS_IMETHOD
   RemoveRegistrationEventListener(const nsAString& aScope,
                                   ServiceWorkerRegistrationListener* aListener);
+
+  void
+  MaybeCheckNavigationUpdate(nsIDocument* aDoc);
+
 private:
   ServiceWorkerManager();
   ~ServiceWorkerManager();
 
   void
   Init();
 
   ServiceWorkerJobQueue*
@@ -653,15 +672,21 @@ private:
 
   void
   AddNavigationInterception(const nsACString& aScope,
                             nsIInterceptedChannel* aChannel);
 
   void
   RemoveNavigationInterception(const nsACString& aScope,
                                nsIInterceptedChannel* aChannel);
+
+  void
+  ScheduleUpdateTimer(nsIPrincipal* aPrincipal, const nsACString& aScope);
+
+  void
+  UpdateTimerFired(nsIPrincipal* aPrincipal, const nsACString& aScope);
 };
 
 } // namespace workers
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_workers_serviceworkermanager_h
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -207,56 +207,36 @@ public:
     MOZ_ASSERT(workerPrivate);
     workerPrivate->AssertIsOnWorkerThread();
 #endif
   }
 };
 
 NS_IMPL_ISUPPORTS0(KeepAliveHandler)
 
-class SoftUpdateRequest : public nsRunnable
+class RegistrationUpdateRunnable : public nsRunnable
 {
-protected:
   nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
+  const bool mNeedTimeCheck;
+
 public:
-  explicit SoftUpdateRequest(nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration)
+  RegistrationUpdateRunnable(nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
+                             bool aNeedTimeCheck)
     : mRegistration(aRegistration)
+    , mNeedTimeCheck(aNeedTimeCheck)
   {
-    MOZ_ASSERT(aRegistration);
   }
 
-  NS_IMETHOD Run()
+  NS_IMETHOD
+  Run() override
   {
-    AssertIsOnMainThread();
-
-    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-    MOZ_ASSERT(swm);
-
-    PrincipalOriginAttributes attrs =
-      mozilla::BasePrincipal::Cast(mRegistration->mPrincipal)->OriginAttributesRef();
-
-    swm->PropagateSoftUpdate(attrs,
-                             NS_ConvertUTF8toUTF16(mRegistration->mScope));
-    return NS_OK;
-  }
-};
-
-class CheckLastUpdateTimeRequest final : public SoftUpdateRequest
-{
-public:
-  explicit CheckLastUpdateTimeRequest(
-    nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration)
-    : SoftUpdateRequest(aRegistration)
-  {}
-
-  NS_IMETHOD Run()
-  {
-    AssertIsOnMainThread();
-    if (mRegistration->IsLastUpdateCheckTimeOverOneDay()) {
-      SoftUpdateRequest::Run();
+    if (mNeedTimeCheck) {
+      mRegistration->MaybeScheduleTimeCheckAndUpdate();
+    } else {
+      mRegistration->MaybeScheduleUpdate();
     }
     return NS_OK;
   }
 };
 
 class ExtendableEventWorkerRunnable : public WorkerRunnable
 {
 protected:
@@ -330,19 +310,19 @@ public:
     , mRegistration(aRegistration)
   {
     MOZ_ASSERT(aRegistration);
   }
 
   void
   PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
   {
-    nsCOMPtr<nsIRunnable> runnable = new CheckLastUpdateTimeRequest(mRegistration);
-
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable.forget())));
+    nsCOMPtr<nsIRunnable> runnable =
+      new RegistrationUpdateRunnable(mRegistration, true /* time check */);
+    NS_DispatchToMainThread(runnable.forget());
   }
 };
 
 /*
  * Fires 'install' event on the ServiceWorkerGlobalScope. Modifies busy count
  * since it fires the event. This is ok since there can't be nested
  * ServiceWorkers, so the parent thread -> worker thread requirement for
  * runnables is satisfied.
@@ -1276,42 +1256,41 @@ private:
     event->SetTrusted(true);
 
     RefPtr<EventTarget> target = do_QueryObject(aWorkerPrivate->GlobalScope());
     nsresult rv2 = target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
     if (NS_WARN_IF(NS_FAILED(rv2)) || !event->WaitToRespond()) {
       nsCOMPtr<nsIRunnable> runnable;
       if (event->DefaultPrevented(aCx)) {
         event->ReportCanceled();
-        runnable = new CancelChannelRunnable(mInterceptedChannel,
-                                             NS_ERROR_INTERCEPTION_FAILED);
       } else if (event->GetInternalNSEvent()->mFlags.mExceptionHasBeenRisen) {
         // Exception logged via the WorkerPrivate ErrorReporter
+      } else {
+        runnable = new ResumeRequest(mInterceptedChannel);
+      }
+
+      if (!runnable) {
+        nsCOMPtr<nsIRunnable> updateRunnable =
+          new RegistrationUpdateRunnable(mRegistration, false /* time check */);
+        NS_DispatchToMainThread(runnable.forget());
+
         runnable = new CancelChannelRunnable(mInterceptedChannel,
                                              NS_ERROR_INTERCEPTION_FAILED);
-      } else {
-        runnable = new ResumeRequest(mInterceptedChannel);
       }
 
       MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
     }
 
     RefPtr<Promise> waitUntilPromise = event->GetPromise();
     if (waitUntilPromise) {
       RefPtr<KeepAliveHandler> keepAliveHandler =
         new KeepAliveHandler(mKeepAliveToken);
       waitUntilPromise->AppendNativeHandler(keepAliveHandler);
     }
 
-    // 9.8.22 If request is a non-subresource request, then: Invoke Soft Update algorithm
-    if (internalReq->IsNavigationRequest()) {
-      nsCOMPtr<nsIRunnable> runnable= new SoftUpdateRequest(mRegistration);
-
-      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable.forget())));
-    }
     return true;
   }
 
   nsresult
   HandleBodyWithHeaders(nsIInputStream* aUploadStream)
   {
     // We are dealing with an nsMIMEInputStream which uses string input streams
     // under the hood, so all of the data is available synchronously.
--- a/dom/workers/ServiceWorkerRegistration.cpp
+++ b/dom/workers/ServiceWorkerRegistration.cpp
@@ -332,16 +332,25 @@ public:
       promise->MaybeReject(mStatus);
     } else {
       promise->MaybeResolve(JS::UndefinedHandleValue);
     }
     mStatus.SuppressException();
     mPromiseProxy->CleanUp(aCx);
     return true;
   }
+
+  void
+  PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
+               bool aSuccess) override
+  {
+    if (!aSuccess) {
+      mStatus.SuppressException();
+    }
+  }
 };
 
 class WorkerThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallback
 {
   RefPtr<PromiseWorkerProxy> mPromiseProxy;
 
   ~WorkerThreadUpdateCallback()
   {
@@ -986,16 +995,24 @@ ServiceWorkerRegistrationWorkerThread::U
   MOZ_ASSERT(worker);
   worker->AssertIsOnWorkerThread();
 
   RefPtr<Promise> promise = Promise::Create(worker->GlobalScope(), aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
+  // Avoid infinite update loops by ignoring update() calls during top
+  // level script evaluation.  See:
+  // https://github.com/slightlyoff/ServiceWorker/issues/800
+  if (worker->LoadScriptAsPartOfLoadingServiceWorkerScript()) {
+    promise->MaybeResolve(JS::UndefinedHandleValue);
+    return promise.forget();
+  }
+
   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_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
--- a/dom/workers/ServiceWorkerScriptCache.cpp
+++ b/dom/workers/ServiceWorkerScriptCache.cpp
@@ -258,30 +258,33 @@ public:
     // the cache even if there isn't an existing one.
     AutoJSAPI jsapi;
     jsapi.Init();
     ErrorResult result;
     mSandbox.init(jsapi.cx());
     mCacheStorage = CreateCacheStorage(jsapi.cx(), aPrincipal, result, &mSandbox);
     if (NS_WARN_IF(result.Failed())) {
       MOZ_ASSERT(!result.IsErrorWithMessage());
+      Cleanup();
       return result.StealNSResult();
     }
 
     mCN = new CompareNetwork(this);
     nsresult rv = mCN->Initialize(aPrincipal, aURL, aLoadGroup);
     if (NS_WARN_IF(NS_FAILED(rv))) {
+      Cleanup();
       return rv;
     }
 
     if (!aCacheName.IsEmpty()) {
       mCC = new CompareCache(this);
       rv = mCC->Initialize(aPrincipal, aURL, aCacheName);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         mCN->Abort();
+        Cleanup();
         return rv;
       }
     }
 
     return NS_OK;
   }
 
   const nsAString&
--- a/dom/workers/test/serviceworkers/test_serviceworkerregistrationinfo.xul
+++ b/dom/workers/test/serviceworkers/test_serviceworkerregistrationinfo.xul
@@ -42,63 +42,58 @@
           promise = waitForRegister(EXAMPLE_URL, function (registration) {
             is(registration.scriptSpec, "");
             ok(registration.installingWorker === null);
             ok(registration.waitingWorker === null);
             ok(registration.activeWorker === null);
 
             return waitForServiceWorkerRegistrationChange(registration, function  () {
               is(registration.scriptSpec, EXAMPLE_URL + "worker.js");
+              ok(registration.installingWorker !== null);
+              is(registration.installingWorker.scriptSpec, EXAMPLE_URL + "worker.js");
+              ok(registration.waitingWorker === null);
+              ok(registration.activeWorker === null);
 
               return waitForServiceWorkerRegistrationChange(registration, function () {
-                ok(registration.installingWorker !== null);
-                ok(registration.waitingWorker === null);
+                ok(registration.installingWorker === null);
+                ok(registration.waitingWorker !== null);
                 ok(registration.activeWorker === null);
 
                 return waitForServiceWorkerRegistrationChange(registration, function () {
                   ok(registration.installingWorker === null);
-                  ok(registration.waitingWorker !== null);
-                  ok(registration.activeWorker === null);
+                  ok(registration.waitingWorker === null);
+                  ok(registration.activeWorker !== null);
 
-                  return waitForServiceWorkerRegistrationChange(registration, function () {
-                    ok(registration.installingWorker === null);
-                    ok(registration.waitingWorker === null);
-                    ok(registration.activeWorker !== null);
-
-                    return registration;
-                  });
+                  return registration;
                 });
               });
             });
           });
           iframe.contentWindow.postMessage("register", "*");
           let registration = yield promise;
 
           promise = waitForServiceWorkerRegistrationChange(registration, function () {
             is(registration.scriptSpec, EXAMPLE_URL + "worker2.js");
+            ok(registration.installingWorker !== null);
+            is(registration.installingWorker.scriptSpec, EXAMPLE_URL + "worker2.js");
+            ok(registration.waitingWorker === null);
+            ok(registration.activeWorker !== null);
 
             return waitForServiceWorkerRegistrationChange(registration, function () {
-              ok(registration.installingWorker !== null);
-              ok(registration.waitingWorker === null);
+              ok(registration.installingWorker === null);
+              ok(registration.waitingWorker !== null);
               ok(registration.activeWorker !== null);
 
               return waitForServiceWorkerRegistrationChange(registration, function () {
                 ok(registration.installingWorker === null);
-                ok(registration.waitingWorker !== null);
+                ok(registration.waitingWorker === null);
                 ok(registration.activeWorker !== null);
 
-                return waitForServiceWorkerRegistrationChange(registration, function () {
-                  ok(registration.installingWorker === null);
-                  ok(registration.waitingWorker === null);
-                  ok(registration.activeWorker !== null);
-
-                  return registration;
-                });
+                return registration;
               });
-
             });
           });
           iframe.contentWindow.postMessage("register", "*");
           yield promise;
 
           SimpleTest.finish();
         });
       });
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -1612,16 +1612,27 @@ nsXULElement::GetFrameLoader()
     if (!slots)
         return nullptr;
 
     RefPtr<nsFrameLoader> loader = slots->mFrameLoader;
     return loader.forget();
 }
 
 nsresult
+nsXULElement::GetParentApplication(mozIApplication** aApplication)
+{
+    if (!aApplication) {
+        return NS_ERROR_FAILURE;
+    }
+
+    *aApplication = nullptr;
+    return NS_OK;
+}
+
+nsresult
 nsXULElement::SetIsPrerendered()
 {
   return SetAttr(kNameSpaceID_None, nsGkAtoms::prerendered, nullptr,
                  NS_LITERAL_STRING("true"), true);
 }
 
 nsresult
 nsXULElement::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherOwner)
--- a/dom/xul/nsXULElement.h
+++ b/dom/xul/nsXULElement.h
@@ -407,16 +407,17 @@ public:
 
     // nsIDOMXULElement
     NS_DECL_NSIDOMXULELEMENT
 
     virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override;
     virtual mozilla::EventStates IntrinsicState() const override;
 
     nsresult GetFrameLoader(nsIFrameLoader** aFrameLoader);
+    nsresult GetParentApplication(mozIApplication** aApplication);
     nsresult SetIsPrerendered();
     nsresult SwapFrameLoaders(nsIFrameLoaderOwner* aOtherOwner);
 
     virtual void RecompileScriptEventListeners() override;
 
     // This function should ONLY be used by BindToTree implementations.
     // The function exists solely because XUL elements store the binding
     // parent as a member instead of in the slots, as Element does.
--- a/gfx/2d/DataSurfaceHelpers.cpp
+++ b/gfx/2d/DataSurfaceHelpers.cpp
@@ -16,27 +16,27 @@ namespace mozilla {
 namespace gfx {
 
 uint8_t*
 DataAtOffset(DataSourceSurface* aSurface,
              const DataSourceSurface::MappedSurface* aMap,
              IntPoint aPoint)
 {
   if (!SurfaceContainsPoint(aSurface, aPoint)) {
-    MOZ_CRASH("sample position needs to be inside surface!");
+    MOZ_CRASH("GFX: sample position needs to be inside surface!");
   }
 
   MOZ_ASSERT(Factory::CheckSurfaceSize(aSurface->GetSize()),
              "surface size overflows - this should have been prevented when the surface was created");
 
   uint8_t* data = aMap->mData + aPoint.y * aMap->mStride +
     aPoint.x * BytesPerPixel(aSurface->GetFormat());
 
   if (data < aMap->mData) {
-    MOZ_CRASH("out-of-range data access");
+    MOZ_CRASH("GFX: out-of-range data access");
   }
 
   return data;
 }
 
 // This check is safe against integer overflow.
 bool
 SurfaceContainsPoint(SourceSurface* aSurface, const IntPoint& aPoint)
@@ -233,17 +233,17 @@ BufferSizeFromStrideAndHeight(int32_t aS
  * aDestPoint: Point inside aDest surface
  */
 void
 CopyRect(DataSourceSurface* aSrc, DataSourceSurface* aDest,
          IntRect aSrcRect, IntPoint aDestPoint)
 {
   if (aSrcRect.Overflows() ||
       IntRect(aDestPoint, aSrcRect.Size()).Overflows()) {
-    MOZ_CRASH("we should never be getting invalid rects at this point");
+    MOZ_CRASH("GFX: we should never be getting invalid rects at this point");
   }
 
   MOZ_RELEASE_ASSERT(aSrc->GetFormat() == aDest->GetFormat(),
                      "different surface formats");
   MOZ_RELEASE_ASSERT(IntRect(IntPoint(), aSrc->GetSize()).Contains(aSrcRect),
                      "source rect too big for source surface");
   MOZ_RELEASE_ASSERT(IntRect(IntPoint(), aDest->GetSize()).Contains(IntRect(aDestPoint, aSrcRect.Size())),
                      "dest surface too small");
--- a/gfx/2d/DrawTargetCG.cpp
+++ b/gfx/2d/DrawTargetCG.cpp
@@ -252,17 +252,17 @@ GetRetainedImageFromSourceSurface(Source
 
     case SurfaceType::COREGRAPHICS_CGCONTEXT:
       return CGImageRetain(static_cast<SourceSurfaceCGContext*>(aSurface)->GetImage());
 
     default:
     {
       RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
       if (!data) {
-        MOZ_CRASH("unsupported source surface");
+        MOZ_CRASH("GFX: unsupported source CG surface");
       }
       data.get()->AddRef();
       return CreateCGImage(releaseDataSurface, data.get(),
                            data->GetData(), data->GetSize(),
                            data->Stride(), data->GetFormat());
     }
   }
 }
@@ -1549,17 +1549,18 @@ DrawTargetCG::FillGlyphs(ScaledFont *aFo
   // This code can execute millions of times in short periods, so we want to
   // avoid heap allocation whenever possible. So we use an inline vector
   // capacity of 64 elements, which is enough to typically avoid heap
   // allocation in ~99% of cases.
   Vector<CGGlyph, 64> glyphs;
   Vector<CGPoint, 64> positions;
   if (!glyphs.resizeUninitialized(aBuffer.mNumGlyphs) ||
       !positions.resizeUninitialized(aBuffer.mNumGlyphs)) {
-    MOZ_CRASH("glyphs/positions allocation failed");
+    gfxDevCrash(LogReason::GlyphAllocFailedCG) << "glyphs/positions allocation failed";
+    return;
   }
 
   // Handle the flip
   CGContextScaleCTM(cg, 1, -1);
   // CGContextSetTextMatrix works differently with kCGTextClip && kCGTextFill
   // It seems that it transforms the positions with TextFill and not with TextClip
   // Therefore we'll avoid it. See also:
   // http://cgit.freedesktop.org/cairo/commit/?id=9c0d761bfcdd28d52c83d74f46dd3c709ae0fa69
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -651,17 +651,17 @@ DrawTargetCairo::GetType() const
     case CAIRO_SURFACE_TYPE_QUARTZ_IMAGE:
     case CAIRO_SURFACE_TYPE_SCRIPT:
     case CAIRO_SURFACE_TYPE_RECORDING:
     case CAIRO_SURFACE_TYPE_DRM:
     case CAIRO_SURFACE_TYPE_SUBSURFACE:
     case CAIRO_SURFACE_TYPE_TEE: // included to silence warning about unhandled enum value
       return DrawTargetType::SOFTWARE_RASTER;
     default:
-      MOZ_CRASH("Unsupported cairo surface type");
+      MOZ_CRASH("GFX: Unsupported cairo surface type");
     }
   }
   MOZ_ASSERT(false, "Could not determine DrawTargetType for DrawTargetCairo");
   return DrawTargetType::SOFTWARE_RASTER;
 }
 
 IntSize
 DrawTargetCairo::GetSize()
@@ -1274,17 +1274,18 @@ DrawTargetCairo::FillGlyphs(ScaledFont *
   // Convert our GlyphBuffer into a vector of Cairo glyphs. This code can
   // execute millions of times in short periods, so we want to avoid heap
   // allocation whenever possible. So we use an inline vector capacity of 1024
   // bytes (the maximum allowed by mozilla::Vector), which gives an inline
   // length of 1024 / 24 = 42 elements, which is enough to typically avoid heap
   // allocation in ~99% of cases.
   Vector<cairo_glyph_t, 1024 / sizeof(cairo_glyph_t)> glyphs;
   if (!glyphs.resizeUninitialized(aBuffer.mNumGlyphs)) {
-    MOZ_CRASH("glyphs allocation failed");
+    gfxDevCrash(LogReason::GlyphAllocFailedCairo) << "glyphs allocation failed";
+    return;
   }
   for (uint32_t i = 0; i < aBuffer.mNumGlyphs; ++i) {
     glyphs[i].index = aBuffer.mGlyphs[i].mIndex;
     glyphs[i].x = aBuffer.mGlyphs[i].mPosition.x;
     glyphs[i].y = aBuffer.mGlyphs[i].mPosition.y;
   }
 
   cairo_show_glyphs(mContext, &glyphs[0], aBuffer.mNumGlyphs);
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -95,17 +95,18 @@ GetBitmapForSurface(SourceSurface* aSurf
 
   if (aSurface->GetType() == SurfaceType::SKIA) {
     result.mBitmap = static_cast<SourceSurfaceSkia*>(aSurface)->GetBitmap();
     return result;
   }
 
   RefPtr<DataSourceSurface> surf = aSurface->GetDataSurface();
   if (!surf) {
-    MOZ_CRASH("Non-skia SourceSurfaces need to be DataSourceSurfaces");
+    gfxDevCrash(LogReason::SourceSurfaceIncompatible) << "Non-skia SourceSurfaces need to be DataSourceSurfaces";
+    return result;
   }
 
   SkAlphaType alphaType = (surf->GetFormat() == SurfaceFormat::B8G8R8X8) ?
     kOpaque_SkAlphaType : kPremul_SkAlphaType;
 
   SkImageInfo info = SkImageInfo::Make(surf->GetSize().width,
                                        surf->GetSize().height,
                                        GfxFormatToSkiaColorType(surf->GetFormat()),
--- a/gfx/2d/FilterNodeD2D1.cpp
+++ b/gfx/2d/FilterNodeD2D1.cpp
@@ -20,33 +20,33 @@ namespace gfx {
 D2D1_COLORMATRIX_ALPHA_MODE D2DAlphaMode(uint32_t aMode)
 {
   switch (aMode) {
   case ALPHA_MODE_PREMULTIPLIED:
     return D2D1_COLORMATRIX_ALPHA_MODE_PREMULTIPLIED;
   case ALPHA_MODE_STRAIGHT:
     return D2D1_COLORMATRIX_ALPHA_MODE_STRAIGHT;
   default:
-    MOZ_CRASH("Unknown enum value!");
+    MOZ_CRASH("GFX: Unknown enum value D2DAlphaMode!");
   }
 
   return D2D1_COLORMATRIX_ALPHA_MODE_PREMULTIPLIED;
 }
 
 D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE D2DAffineTransformInterpolationMode(Filter aFilter)
 {
   switch (aFilter) {
   case Filter::GOOD:
     return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_LINEAR;
   case Filter::LINEAR:
     return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_LINEAR;
   case Filter::POINT:
     return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
   default:
-    MOZ_CRASH("Unknown enum value!");
+    MOZ_CRASH("GFX: Unknown enum value D2DAffineTIM!");
   }
 
   return D2D1_2DAFFINETRANSFORM_INTERPOLATION_MODE_LINEAR;
 }
 
 D2D1_BLEND_MODE D2DBlendMode(uint32_t aMode)
 {
   switch (aMode) {
@@ -77,45 +77,45 @@ D2D1_BLEND_MODE D2DBlendMode(uint32_t aM
   case BLEND_MODE_SATURATION:
     return D2D1_BLEND_MODE_SATURATION;
   case BLEND_MODE_COLOR:
     return D2D1_BLEND_MODE_COLOR;
   case BLEND_MODE_LUMINOSITY:
     return D2D1_BLEND_MODE_LUMINOSITY;
 
   default:
-    MOZ_CRASH("Unknown enum value!");
+    MOZ_CRASH("GFX: Unknown enum value D2DBlendMode!");
   }
 
   return D2D1_BLEND_MODE_DARKEN;
 }
 
 D2D1_MORPHOLOGY_MODE D2DMorphologyMode(uint32_t aMode)
 {
   switch (aMode) {
   case MORPHOLOGY_OPERATOR_DILATE:
     return D2D1_MORPHOLOGY_MODE_DILATE;
   case MORPHOLOGY_OPERATOR_ERODE:
     return D2D1_MORPHOLOGY_MODE_ERODE;
   }
 
-  MOZ_CRASH("Unknown enum value!");
+  MOZ_CRASH("GFX: Unknown enum value D2DMorphologyMode!");
   return D2D1_MORPHOLOGY_MODE_DILATE;
 }
 
 D2D1_TURBULENCE_NOISE D2DTurbulenceNoise(uint32_t aMode)
 {
   switch (aMode) {
   case TURBULENCE_TYPE_FRACTAL_NOISE:
     return D2D1_TURBULENCE_NOISE_FRACTAL_SUM;
   case TURBULENCE_TYPE_TURBULENCE:
     return D2D1_TURBULENCE_NOISE_TURBULENCE;
   }
 
-  MOZ_CRASH("Unknown enum value!");
+  MOZ_CRASH("GFX: Unknown enum value D2DTurbulenceNoise!");
   return D2D1_TURBULENCE_NOISE_TURBULENCE;
 }
 
 D2D1_COMPOSITE_MODE D2DFilterCompositionMode(uint32_t aMode)
 {
   switch (aMode) {
   case COMPOSITE_OPERATOR_OVER:
     return D2D1_COMPOSITE_MODE_SOURCE_OVER;
@@ -124,50 +124,50 @@ D2D1_COMPOSITE_MODE D2DFilterComposition
   case COMPOSITE_OPERATOR_OUT:
     return D2D1_COMPOSITE_MODE_SOURCE_OUT;
   case COMPOSITE_OPERATOR_ATOP:
     return D2D1_COMPOSITE_MODE_SOURCE_ATOP;
   case COMPOSITE_OPERATOR_XOR:
     return D2D1_COMPOSITE_MODE_XOR;
   }
 
-  MOZ_CRASH("Unknown enum value!");
+  MOZ_CRASH("GFX: Unknown enum value D2DFilterCompositionMode!");
   return D2D1_COMPOSITE_MODE_SOURCE_OVER;
 }
 
 D2D1_CHANNEL_SELECTOR D2DChannelSelector(uint32_t aMode)
 {
   switch (aMode) {
   case COLOR_CHANNEL_R:
     return D2D1_CHANNEL_SELECTOR_R;
   case COLOR_CHANNEL_G:
     return D2D1_CHANNEL_SELECTOR_G;
   case COLOR_CHANNEL_B:
     return D2D1_CHANNEL_SELECTOR_B;
   case COLOR_CHANNEL_A:
     return D2D1_CHANNEL_SELECTOR_A;
   }
 
-  MOZ_CRASH("Unknown enum value!");
+  MOZ_CRASH("GFX: Unknown enum value D2DChannelSelector!");
   return D2D1_CHANNEL_SELECTOR_R;
 }
 
 already_AddRefed<ID2D1Image> GetImageForSourceSurface(DrawTarget *aDT, SourceSurface *aSurface)
 {
   if (aDT->IsTiledDrawTarget() || aDT->IsDualDrawTarget()) {
-      MOZ_CRASH("Incompatible draw target type!");
-      return nullptr;
+    gfxDevCrash(LogReason::FilterNodeD2D1Target) << "Incompatible draw target type! " << (int)aDT->IsTiledDrawTarget() << " " << (int)aDT->IsDualDrawTarget();
+    return nullptr;
   }
   switch (aDT->GetBackendType()) {
     case BackendType::DIRECT2D1_1:
       return static_cast<DrawTargetD2D1*>(aDT)->GetImageForSurface(aSurface, ExtendMode::CLAMP);
     case BackendType::DIRECT2D:
       return static_cast<DrawTargetD2D*>(aDT)->GetImageForSurface(aSurface);
     default:
-      MOZ_CRASH("Unknown draw target type!");
+      gfxDevCrash(LogReason::FilterNodeD2D1Backend) << "Unknown draw target type! " << (int)aDT->GetBackendType();
       return nullptr;
   }
 }
 
 uint32_t ConvertValue(FilterType aType, uint32_t aAttribute, uint32_t aValue)
 {
   switch (aType) {
   case FilterType::COLOR_MATRIX:
--- a/gfx/2d/FilterNodeSoftware.cpp
+++ b/gfx/2d/FilterNodeSoftware.cpp
@@ -641,17 +641,18 @@ void
 FilterNodeSoftware::RequestInputRect(uint32_t aInputEnumIndex, const IntRect &aRect)
 {
   if (aRect.Overflows()) {
     return;
   }
 
   int32_t inputIndex = InputIndex(aInputEnumIndex);
   if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
-    MOZ_CRASH();
+    gfxDevCrash(LogReason::FilterInputError) << "Invalid input " << inputIndex << " vs. " << NumberOfSetInputs();
+    return;
   }
   if (mInputSurfaces[inputIndex]) {
     return;
   }
   RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
   MOZ_ASSERT(filter, "missing input");
   filter->RequestRect(filter->GetOutputRectInRect(aRect));
 }
@@ -678,17 +679,17 @@ FilterNodeSoftware::GetInputDataSourceSu
   }
 
 #ifdef DEBUG_DUMP_SURFACES
   printf("<section><h1>GetInputDataSourceSurface with aRect: %d, %d, %d, %d</h1>\n",
          aRect.x, aRect.y, aRect.width, aRect.height);
 #endif
   int32_t inputIndex = InputIndex(aInputEnumIndex);
   if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
-    MOZ_CRASH();
+    gfxDevCrash(LogReason::FilterInputData) << "Invalid data " << inputIndex << " vs. " << NumberOfSetInputs();
     return nullptr;
   }
 
   if (aRect.IsEmpty()) {
     return nullptr;
   }
 
   RefPtr<SourceSurface> surface;
@@ -789,17 +790,17 @@ FilterNodeSoftware::GetInputRectInRect(u
                                        const IntRect &aInRect)
 {
   if (aInRect.Overflows()) {
     return IntRect();
   }
 
   int32_t inputIndex = InputIndex(aInputEnumIndex);
   if (inputIndex < 0 || (uint32_t)inputIndex >= NumberOfSetInputs()) {
-    MOZ_CRASH();
+    gfxDevCrash(LogReason::FilterInputRect) << "Invalid rect " << inputIndex << " vs. " << NumberOfSetInputs();
     return IntRect();
   }
   if (mInputSurfaces[inputIndex]) {
     return aInRect.Intersect(IntRect(IntPoint(0, 0),
                                      mInputSurfaces[inputIndex]->GetSize()));
   }
   RefPtr<FilterNodeSoftware> filter = mInputFilters[inputIndex];
   MOZ_ASSERT(filter, "missing input");
@@ -876,17 +877,17 @@ FilterNodeSoftware::SetInput(uint32_t aI
 
 void
 FilterNodeSoftware::SetInput(uint32_t aInputEnumIndex,
                              SourceSurface *aSurface,
                              FilterNodeSoftware *aFilter)
 {
   int32_t inputIndex = InputIndex(aInputEnumIndex);
   if (inputIndex < 0) {
-    MOZ_CRASH();
+    gfxDevCrash(LogReason::FilterInputSet) << "Invalid set " << inputIndex;
     return;
   }
   if ((uint32_t)inputIndex >= NumberOfSetInputs()) {
     mInputSurfaces.resize(inputIndex + 1);
     mInputFilters.resize(inputIndex + 1);
   }
   mInputSurfaces[inputIndex] = aSurface;
   if (mInputFilters[inputIndex]) {
@@ -1494,17 +1495,18 @@ FilterNodeFloodSoftware::Render(const In
     uint8_t alpha = NS_lround(mColor.a * 255.0f);
     for (int32_t y = 0; y < aRect.height; y++) {
       for (int32_t x = 0; x < aRect.width; x++) {
         targetData[x] = alpha;
       }
       targetData += stride;
     }
   } else {
-    MOZ_CRASH();
+    gfxDevCrash(LogReason::FilterInputFormat) << "Bad format in flood render " << (int)format;
+    return nullptr;
   }
 
   return target.forget();
 }
 
 // Override GetOutput to get around caching. Rendering simple floods is
 // comparatively fast.
 already_AddRefed<DataSourceSurface>
@@ -1662,17 +1664,17 @@ FilterNodeComponentTransferSoftware::Set
       break;
     case ATT_TRANSFER_DISABLE_B:
       mDisableB = aDisable;
       break;
     case ATT_TRANSFER_DISABLE_A:
       mDisableA = aDisable;
       break;
     default:
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: FilterNodeComponentTransferSoftware::SetAttribute");
   }
   Invalidate();
 }
 
 void
 FilterNodeComponentTransferSoftware::GenerateLookupTable(ptrdiff_t aComponent,
                                                          uint8_t aTables[4][256],
                                                          bool aDisabled)
@@ -1829,17 +1831,17 @@ FilterNodeTableTransferSoftware::SetAttr
       break;
     case ATT_TABLE_TRANSFER_TABLE_B:
       mTableB = table;
       break;
     case ATT_TABLE_TRANSFER_TABLE_A:
       mTableA = table;
       break;
     default:
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: FilterNodeTableTransferSoftware::SetAttribute");
   }
   Invalidate();
 }
 
 void
 FilterNodeTableTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
                                                  uint8_t aTable[256])
 {
@@ -1898,17 +1900,17 @@ FilterNodeDiscreteTransferSoftware::SetA
       break;
     case ATT_DISCRETE_TRANSFER_TABLE_B:
       mTableB = discrete;
       break;
     case ATT_DISCRETE_TRANSFER_TABLE_A:
       mTableA = discrete;
       break;
     default:
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: FilterNodeDiscreteTransferSoftware::SetAttribute");
   }
   Invalidate();
 }
 
 void
 FilterNodeDiscreteTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
                                                     uint8_t aTable[256])
 {
@@ -1987,17 +1989,17 @@ FilterNodeLinearTransferSoftware::SetAtt
       break;
     case ATT_LINEAR_TRANSFER_SLOPE_A:
       mSlopeA = aValue;
       break;
     case ATT_LINEAR_TRANSFER_INTERCEPT_A:
       mInterceptA = aValue;
       break;
     default:
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: FilterNodeLinearTransferSoftware::SetAttribute");
   }
   Invalidate();
 }
 
 void
 FilterNodeLinearTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
                                                   uint8_t aTable[256])
 {
@@ -2081,17 +2083,17 @@ FilterNodeGammaTransferSoftware::SetAttr
       break;
     case ATT_GAMMA_TRANSFER_EXPONENT_A:
       mExponentA = aValue;
       break;
     case ATT_GAMMA_TRANSFER_OFFSET_A:
       mOffsetA = aValue;
       break;
     default:
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: FilterNodeGammaTransferSoftware::SetAttribute");
   }
   Invalidate();
 }
 
 void
 FilterNodeGammaTransferSoftware::FillLookupTable(ptrdiff_t aComponent,
                                                  uint8_t aTable[256])
 {
@@ -2169,30 +2171,30 @@ FilterNodeConvolveMatrixSoftware::SetAtt
   switch (aIndex) {
     case ATT_CONVOLVE_MATRIX_DIVISOR:
       mDivisor = aValue;
       break;
     case ATT_CONVOLVE_MATRIX_BIAS:
       mBias = aValue;
       break;
     default:
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
   }
   Invalidate();
 }
 
 void
 FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength)
 {
   switch (aIndex) {
     case ATT_CONVOLVE_MATRIX_KERNEL_UNIT_LENGTH:
       mKernelUnitLength = aKernelUnitLength;
       break;
     default:
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: FilterNodeConvolveMatrixSoftware::SetAttribute");
   }
   Invalidate();
 }
 
 void
 FilterNodeConvolveMatrixSoftware::SetAttribute(uint32_t aIndex,
                                                const IntPoint &aTarget)
 {
@@ -2395,17 +2397,17 @@ MaxVectorSum(const std::vector<Float> &a
 // Returns shiftL and shiftR in such a way that
 // a << shiftL >> shiftR is roughly a * aFloat.
 static void
 TranslateDoubleToShifts(double aDouble, int32_t &aShiftL, int32_t &aShiftR)
 {
   aShiftL = 0;
   aShiftR = 0;
   if (aDouble <= 0) {
-    MOZ_CRASH();
+    MOZ_CRASH("GFX: TranslateDoubleToShifts");
   }
   if (aDouble < 1) {
     while (1 << (aShiftR + 1) < 1 / aDouble) {
       aShiftR++;
     }
   } else {
     while (1 << (aShiftL + 1) < aDouble) {
       aShiftL++;
@@ -2575,17 +2577,17 @@ FilterNodeDisplacementMapSoftware::SetAt
   switch (aIndex) {
     case ATT_DISPLACEMENT_MAP_X_CHANNEL:
       mChannelX = static_cast<ColorChannel>(aValue);
       break;
     case ATT_DISPLACEMENT_MAP_Y_CHANNEL:
       mChannelY = static_cast<ColorChannel>(aValue);
       break;
     default:
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: FilterNodeDisplacementMapSoftware::SetAttribute");
   }
   Invalidate();
 }
 
 already_AddRefed<DataSourceSurface>
 FilterNodeDisplacementMapSoftware::Render(const IntRect& aRect)
 {
   IntRect srcRect = InflatedSourceOrDestRect(aRect);
@@ -2681,31 +2683,31 @@ FilterNodeTurbulenceSoftware::InputIndex
 void
 FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, const Size &aBaseFrequency)
 {
   switch (aIndex) {
     case ATT_TURBULENCE_BASE_FREQUENCY:
       mBaseFrequency = aBaseFrequency;
       break;
     default:
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
       break;
   }
   Invalidate();
 }
 
 void
 FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, const IntRect &aRect)
 {
   switch (aIndex) {
     case ATT_TURBULENCE_RECT:
       mRenderRect = aRect;
       break;
     default:
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
       break;
   }
   Invalidate();
 }
 
 void
 FilterNodeTurbulenceSoftware::SetAttribute(uint32_t aIndex, bool aStitchable)
 {
@@ -2723,17 +2725,17 @@ FilterNodeTurbulenceSoftware::SetAttribu
       break;
     case ATT_TURBULENCE_SEED:
       mSeed = aValue;
       break;
     case ATT_TURBULENCE_TYPE:
       mType = static_cast<TurbulenceType>(aValue);
       break;
     default:
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: FilterNodeTurbulenceSoftware::SetAttribute");
       break;
   }
   Invalidate();
 }
 
 already_AddRefed<DataSourceSurface>
 FilterNodeTurbulenceSoftware::Render(const IntRect& aRect)
 {
@@ -3029,17 +3031,17 @@ void
 FilterNodeGaussianBlurSoftware::SetAttribute(uint32_t aIndex,
                                              float aStdDeviation)
 {
   switch (aIndex) {
     case ATT_GAUSSIAN_BLUR_STD_DEVIATION:
       mStdDeviation = ClampStdDeviation(aStdDeviation);
       break;
     default:
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: FilterNodeGaussianBlurSoftware::SetAttribute");
   }
   Invalidate();
 }
 
 Size
 FilterNodeGaussianBlurSoftware::StdDeviationXY()
 {
   return Size(mStdDeviation, mStdDeviation);
@@ -3053,31 +3055,31 @@ void
 FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
                                                 Float aStdDeviation)
 {
   switch (aIndex) {
     case ATT_DIRECTIONAL_BLUR_STD_DEVIATION:
       mStdDeviation = ClampStdDeviation(aStdDeviation);
       break;
     default:
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
   }
   Invalidate();
 }
 
 void
 FilterNodeDirectionalBlurSoftware::SetAttribute(uint32_t aIndex,
                                                 uint32_t aBlurDirection)
 {
   switch (aIndex) {
     case ATT_DIRECTIONAL_BLUR_DIRECTION:
       mBlurDirection = (BlurDirection)aBlurDirection;
       break;
     default:
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: FilterNodeDirectionalBlurSoftware::SetAttribute");
   }
   Invalidate();
 }
 
 Size
 FilterNodeDirectionalBlurSoftware::StdDeviationXY()
 {
   float sigmaX = mBlurDirection == BLUR_DIRECTION_X ? mStdDeviation : 0;
@@ -3284,48 +3286,48 @@ FilterNodeLightingSoftware<LightType, Li
 template<typename LightType, typename LightingType>
 void
 FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Point3D &aPoint)
 {
   if (mLight.SetAttribute(aIndex, aPoint)) {
     Invalidate();
     return;
   }
-  MOZ_CRASH();
+  MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute point");
 }
 
 template<typename LightType, typename LightingType>
 void
 FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, Float aValue)
 {
   if (mLight.SetAttribute(aIndex, aValue) ||
       mLighting.SetAttribute(aIndex, aValue)) {
     Invalidate();
     return;
   }
   switch (aIndex) {
     case ATT_LIGHTING_SURFACE_SCALE:
       mSurfaceScale = aValue;
       break;
     default:
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute float");
   }
   Invalidate();
 }
 
 template<typename LightType, typename LightingType>
 void
 FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Size &aKernelUnitLength)
 {
   switch (aIndex) {
     case ATT_LIGHTING_KERNEL_UNIT_LENGTH:
       mKernelUnitLength = aKernelUnitLength;
       break;
     default:
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: FilterNodeLightingSoftware::SetAttribute size");
   }
   Invalidate();
 }
 
 template<typename LightType, typename LightingType>
 void
 FilterNodeLightingSoftware<LightType, LightingType>::SetAttribute(uint32_t aIndex, const Color &aColor)
 {
--- a/gfx/2d/JobScheduler.cpp
+++ b/gfx/2d/JobScheduler.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * 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 "JobScheduler.h"
+#include "Logging.h"
 
 namespace mozilla {
 namespace gfx {
 
 JobScheduler* JobScheduler::sSingleton = nullptr;
 
 bool JobScheduler::Init(uint32_t aNumThreads, uint32_t aNumQueues)
 {
@@ -265,15 +266,15 @@ WorkerThread::Run()
       return;
     }
 
     JobStatus status = JobScheduler::ProcessJob(commands);
 
     if (status == JobStatus::Error) {
       // Don't try to handle errors for now, but that's open to discussions.
       // I expect errors to be mostly OOM issues.
-      MOZ_CRASH();
+      gfxDevCrash(LogReason::JobStatusError) << "Invalid job status " << (int)status;
     }
   }
 }
 
 } //namespace
 } //namespace
--- a/gfx/2d/Logging.h
+++ b/gfx/2d/Logging.h
@@ -135,16 +135,30 @@ enum class LogReason : int {
   // make sure the other items retain their values.
   D3D11InvalidCallDeviceRemoved = 0,
   D3D11InvalidCall,
   D3DLockTimeout,
   D3D10FinalizeFrame,
   D3D11FinalizeFrame,
   D3D10SyncLock,
   D3D11SyncLock,
+  D2D1NoWriteMap,
+  JobStatusError,
+  FilterInputError,
+  FilterInputData, // 10
+  FilterInputRect,
+  FilterInputSet,
+  FilterInputFormat,
+  FilterNodeD2D1Target,
+  FilterNodeD2D1Backend,
+  SourceSurfaceIncompatible,
+  GlyphAllocFailedCairo,
+  GlyphAllocFailedCG,
+  InvalidRect,
+  CannotDraw3D, // 20
   // End
   MustBeLessThanThis = 101,
 };
 
 struct BasicLogger
 {
   // For efficiency, this method exists and copies the logic of the
   // OutputMessage below.  If making any changes here, also make it
--- a/gfx/2d/ScaledFontBase.cpp
+++ b/gfx/2d/ScaledFontBase.cpp
@@ -160,17 +160,17 @@ ScaledFontBase::CopyGlyphsToBuilder(cons
     RefPtr<PathCairo> cairoPath = new PathCairo(ctx);
     cairo_destroy(ctx);
 
     cairoPath->AppendPathToBuilder(builder);
     return;
   }
 #endif
 
-  MOZ_CRASH("The specified backend type is not supported by CopyGlyphsToBuilder");
+  MOZ_CRASH("GFX: The specified backend type is not supported by CopyGlyphsToBuilder");
 }
 
 #ifdef USE_CAIRO_SCALED_FONT
 void
 ScaledFontBase::SetCairoScaledFont(cairo_scaled_font_t* font)
 {
   MOZ_ASSERT(!mScaledFont);
 
--- a/gfx/2d/ScaledFontMac.cpp
+++ b/gfx/2d/ScaledFontMac.cpp
@@ -100,17 +100,17 @@ ScaledFontMac::GetPathForGlyphs(const Gl
           CGPathAddPath(path, &matrix, glyphPath);
           CGPathRelease(glyphPath);
       }
       RefPtr<Path> ret = new PathCG(path, FillRule::FILL_WINDING);
       CGPathRelease(path);
       return ret.forget();
 #else
       //TODO: probably want CTFontCreatePathForGlyph
-      MOZ_CRASH("This needs implemented");
+      MOZ_CRASH("GFX: This needs implemented 1");
 #endif
   }
   return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget);
 }
 
 void
 ScaledFontMac::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint)
 {
@@ -130,17 +130,17 @@ ScaledFontMac::CopyGlyphsToBuilder(const
     CGAffineTransform matrix = CGAffineTransformMake(mSize, 0, 0, mSize,
                                                      aBuffer.mGlyphs[i].mPosition.x,
                                                      aBuffer.mGlyphs[i].mPosition.y);
     CGPathAddPath(pathBuilderCG->mCGPath, &matrix, glyphPath);
     CGPathRelease(glyphPath);
   }
 #else
     //TODO: probably want CTFontCreatePathForGlyph
-    MOZ_CRASH("This needs implemented");
+    MOZ_CRASH("GFX: This needs implemented 2");
 #endif
 }
 
 uint32_t
 CalcTableChecksum(const uint32_t *tableStart, uint32_t length, bool skipChecksumAdjust = false)
 {
     uint32_t sum = 0L;
     const uint32_t *table = tableStart;
--- a/gfx/2d/SourceSurfaceCG.cpp
+++ b/gfx/2d/SourceSurfaceCG.cpp
@@ -100,17 +100,17 @@ CreateCGImage(CGDataProviderReleaseDataC
 
     case SurfaceFormat::A8:
       // XXX: why don't we set a colorspace here?
       bitsPerComponent = 8;
       bitsPerPixel = 8;
       break;
 
     default:
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: CreateCGImage");
   }
 
   size_t bufLen = BufferSizeFromStrideAndHeight(aStride, aSize.height);
   if (bufLen == 0) {
     return nullptr;
   }
   CGDataProviderRef dataProvider = CGDataProviderCreateWithData(aInfo,
                                                                 aData,
--- a/gfx/2d/SourceSurfaceD2D1.cpp
+++ b/gfx/2d/SourceSurfaceD2D1.cpp
@@ -184,17 +184,18 @@ DataSourceSurfaceD2D1::Map(MapType aMapT
   // DataSourceSurfaces used with the new Map API should not be used with GetData!!
   MOZ_ASSERT(!mMapped);
   MOZ_ASSERT(!mIsMapped);
 
   D2D1_MAP_OPTIONS options;
   if (aMapType == MapType::READ) {
     options = D2D1_MAP_OPTIONS_READ;
   } else {
-    MOZ_CRASH("No support for Write maps on D2D1 DataSourceSurfaces yet!");
+    gfxDevCrash(LogReason::D2D1NoWriteMap) << "No support for Write maps on D2D1 DataSourceSurfaces yet!";
+    return false;
   }
 
   D2D1_MAPPED_RECT map;
   if (FAILED(mBitmap->Map(D2D1_MAP_OPTIONS_READ, &map))) {
     gfxCriticalError() << "Failed to map bitmap.";
     return false;
   }
   aMappedSurface->mData = map.bits;
--- a/gfx/gl/GLBlitHelper.cpp
+++ b/gfx/gl/GLBlitHelper.cpp
@@ -950,17 +950,17 @@ GLBlitHelper::DrawBlitTextureToFramebuff
     switch (srcTarget) {
     case LOCAL_GL_TEXTURE_2D:
         type = BlitTex2D;
         break;
     case LOCAL_GL_TEXTURE_RECTANGLE_ARB:
         type = BlitTexRect;
         break;
     default:
-        MOZ_CRASH("Fatal Error: Bad `srcTarget`.");
+        MOZ_CRASH("GFX: Fatal Error: Bad `srcTarget`.");
         break;
     }
 
     ScopedGLDrawState autoStates(mGL);
     if (internalFBs) {
         mGL->Screen()->BindFB_Internal(destFB);
     } else {
         mGL->BindFB(destFB);
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -2773,17 +2773,17 @@ GLContext::Readback(SharedSurface* src, 
                 fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
                                       src->ProdTextureTarget(), src->ProdTexture(), 0);
                 break;
             case AttachmentType::GLRenderbuffer:
                 fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
                                          LOCAL_GL_RENDERBUFFER, src->ProdRenderbuffer());
                 break;
             default:
-                MOZ_CRASH("bad `src->mAttachType`.");
+                MOZ_CRASH("GFX: bad `src->mAttachType`.");
             }
 
             DebugOnly<GLenum> status = fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
             MOZ_ASSERT(status == LOCAL_GL_FRAMEBUFFER_COMPLETE);
         }
 
         if (src->NeedsIndirectReads()) {
             fGenTextures(1, &tempTex);
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -388,17 +388,17 @@ GLContextEGL::IsCurrent() {
 }
 
 bool
 GLContextEGL::RenewSurface() {
     if (!mOwnsContext) {
         return false;
     }
 #ifndef MOZ_WIDGET_ANDROID
-    MOZ_CRASH("unimplemented");
+    MOZ_CRASH("GFX: unimplemented");
     // to support this on non-Android platforms, need to keep track of the nsIWidget that
     // this GLContext was created for (with CreateForWindow) so that we know what to
     // pass again to CreateSurfaceForWindow below.
     // The reason why Android doesn't need this is that it delegates EGLSurface creation to
     // Java code which is the only thing that knows about our actual widget.
 #endif
     // unconditionally release the surface and create a new one. Don't try to optimize this away.
     // If we get here, then by definition we know that we want to get a new surface.
@@ -453,29 +453,29 @@ void
 GLContextEGL::HoldSurface(gfxASurface *aSurf) {
     mThebesSurface = aSurf;
 }
 
 /* static */ EGLSurface
 GLContextEGL::CreateSurfaceForWindow(nsIWidget* aWidget)
 {
     if (!sEGLLibrary.EnsureInitialized()) {
-        MOZ_CRASH("Failed to load EGL library!\n");
+        MOZ_CRASH("GFX: Failed to load EGL library!\n");
         return nullptr;
     }
 
     EGLConfig config;
     if (!CreateConfig(&config, aWidget)) {
-        MOZ_CRASH("Failed to create EGLConfig!\n");
+        MOZ_CRASH("GFX: Failed to create EGLConfig!\n");
         return nullptr;
     }
 
     EGLSurface surface = mozilla::gl::CreateSurfaceForWindow(aWidget, config);
     if (!surface) {
-        MOZ_CRASH("Failed to create EGLSurface for window!\n");
+        MOZ_CRASH("GFX: Failed to create EGLSurface for window!\n");
         return nullptr;
     }
     return surface;
 }
 
 /* static */ void
 GLContextEGL::DestroySurface(EGLSurface aSurface)
 {
@@ -736,17 +736,17 @@ CreateConfig(EGLConfig* aConfig, nsIWidg
         return true;
     }
 }
 
 already_AddRefed<GLContext>
 GLContextProviderEGL::CreateWrappingExisting(void* aContext, void* aSurface)
 {
     if (!sEGLLibrary.EnsureInitialized()) {
-        MOZ_CRASH("Failed to load EGL library!\n");
+        MOZ_CRASH("GFX: Failed to load EGL library 2!\n");
         return nullptr;
     }
 
     if (aContext && aSurface) {
         SurfaceCaps caps = SurfaceCaps::Any();
         EGLConfig config = EGL_NO_CONFIG;
         RefPtr<GLContextEGL> glContext =
             new GLContextEGL(caps,
@@ -761,81 +761,81 @@ GLContextProviderEGL::CreateWrappingExis
 
     return nullptr;
 }
 
 already_AddRefed<GLContext>
 GLContextProviderEGL::CreateForWindow(nsIWidget *aWidget)
 {
     if (!sEGLLibrary.EnsureInitialized()) {
-        MOZ_CRASH("Failed to load EGL library!\n");
+        MOZ_CRASH("GFX: Failed to load EGL library 3!\n");
         return nullptr;
     }
 
     bool doubleBuffered = true;
 
     EGLConfig config;
     if (!CreateConfig(&config, aWidget)) {
-        MOZ_CRASH("Failed to create EGLConfig!\n");
+        MOZ_CRASH("GFX: Failed to create EGLConfig!\n");
         return nullptr;
     }
 
     EGLSurface surface = mozilla::gl::CreateSurfaceForWindow(aWidget, config);
     if (!surface) {
-        MOZ_CRASH("Failed to create EGLSurface!\n");
+        MOZ_CRASH("GFX: Failed to create EGLSurface!\n");
         return nullptr;
     }
 
     SurfaceCaps caps = SurfaceCaps::Any();
     RefPtr<GLContextEGL> glContext =
         GLContextEGL::CreateGLContext(CreateContextFlags::NONE, caps,
                                       nullptr, false,
                                       config, surface);
 
     if (!glContext) {
-        MOZ_CRASH("Failed to create EGLContext!\n");
+        MOZ_CRASH("GFX: Failed to create EGLContext!\n");
         mozilla::gl::DestroySurface(surface);
         return nullptr;
     }
 
     glContext->MakeCurrent();
     glContext->SetIsDoubleBuffered(doubleBuffered);
 
     return glContext.forget();
 }
 
 #if defined(ANDROID)
 EGLSurface
 GLContextProviderEGL::CreateEGLSurface(void* aWindow)
 {
     if (!sEGLLibrary.EnsureInitialized()) {
-        MOZ_CRASH("Failed to load EGL library!\n");
+        MOZ_CRASH("GFX: Failed to load EGL library 4!\n");
     }
 
     EGLConfig config;
     if (!CreateConfig(&config, static_cast<nsIWidget*>(aWindow))) {
-        MOZ_CRASH("Failed to create EGLConfig!\n");
+        MOZ_CRASH("GFX: Failed to create EGLConfig 2!\n");
     }
 
     MOZ_ASSERT(aWindow);
 
     EGLSurface surface = sEGLLibrary.fCreateWindowSurface(EGL_DISPLAY(), config, aWindow, 0);
 
     if (surface == EGL_NO_SURFACE) {
-        MOZ_CRASH("Failed to create EGLSurface!\n");
+        MOZ_CRASH("GFX: Failed to create EGLSurface 2!\n");
     }
 
     return surface;
 }
 
 void
 GLContextProviderEGL::DestroyEGLSurface(EGLSurface surface)
 {
     if (!sEGLLibrary.EnsureInitialized()) {
-        MOZ_CRASH("Failed to load EGL library!\n");
+        MOZ_CRASH("GFX: Failed to load EGL library 5!\n");
     }
 
     sEGLLibrary.fDestroySurface(EGL_DISPLAY(), surface);
 }
 #endif // defined(ANDROID)
 
 static void
 FillContextAttribs(bool alpha, bool depth, bool stencil, bool bpp16,
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -184,17 +184,17 @@ GLLibraryEGL::EnsureInitialized(bool for
     if (mInitialized) {
         return true;
     }
 
     mozilla::ScopedGfxFeatureReporter reporter("EGL");
 
 #ifdef MOZ_B2G
     if (!sCurrentContext.init())
-      MOZ_CRASH("Tls init failed");
+      MOZ_CRASH("GFX: Tls init failed");
 #endif
 
 #ifdef XP_WIN
     if (!mEGLLibrary) {
         // On Windows, the GLESv2, EGL and DXSDK libraries are shipped with libxul and
         // we should look for them there. We have to load the libs in this
         // order, because libEGL.dll depends on libGLESv2.dll which depends on the DXSDK
         // libraries. This matters especially for WebRT apps which are in a different directory.
--- a/gfx/gl/GLReadTexImageHelper.cpp
+++ b/gfx/gl/GLReadTexImageHelper.cpp
@@ -394,17 +394,17 @@ ReadPixelsIntoDataSurface(GLContext* gl,
         destFormat = LOCAL_GL_RGBA;
         destType = LOCAL_GL_UNSIGNED_BYTE;
         break;
     case SurfaceFormat::R5G6B5_UINT16:
         destFormat = LOCAL_GL_RGB;
         destType = LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV;
         break;
     default:
-        MOZ_CRASH("Bad format.");
+        MOZ_CRASH("GFX: Bad format, read pixels.");
     }
     destPixelSize = BytesPerPixel(dest->GetFormat());
     MOZ_ASSERT(dest->GetSize().width * destPixelSize <= dest->Stride());
 
     GLenum readFormat = destFormat;
     GLenum readType = destType;
     bool needsTempSurf = !GetActualReadFormats(gl,
                                                destFormat, destType,
@@ -437,17 +437,17 @@ ReadPixelsIntoDataSurface(GLContext* gl,
             }
             case LOCAL_GL_RGB: {
                 MOZ_ASSERT(destPixelSize == 2);
                 MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV);
                 readFormatGFX = SurfaceFormat::R5G6B5_UINT16;
                 break;
             }
             default: {
-                MOZ_CRASH("Bad read format.");
+                MOZ_CRASH("GFX: Bad read format, read format.");
             }
         }
 
         switch (readType) {
             case LOCAL_GL_UNSIGNED_BYTE: {
                 MOZ_ASSERT(readFormat == LOCAL_GL_RGBA);
                 readAlignment = 1;
                 break;
@@ -458,17 +458,17 @@ ReadPixelsIntoDataSurface(GLContext* gl,
                 break;
             }
             case LOCAL_GL_UNSIGNED_SHORT_5_6_5_REV: {
                 MOZ_ASSERT(readFormat == LOCAL_GL_RGB);
                 readAlignment = 2;
                 break;
             }
             default: {
-                MOZ_CRASH("Bad read type.");
+                MOZ_CRASH("GFX: Bad read type, read type.");
             }
         }
 
         int32_t stride = dest->GetSize().width * BytesPerPixel(readFormatGFX);
         tempSurf = Factory::CreateDataSourceSurfaceWithStride(dest->GetSize(),
                                                               readFormatGFX,
                                                               stride);
         if (NS_WARN_IF(!tempSurf)) {
--- a/gfx/gl/GLScreenBuffer.cpp
+++ b/gfx/gl/GLScreenBuffer.cpp
@@ -167,17 +167,17 @@ GLScreenBuffer::BindAsFramebuffer(GLCont
         gl->raw_fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, drawFB);
         break;
 
     case LOCAL_GL_READ_FRAMEBUFFER_EXT:
         gl->raw_fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, readFB);
         break;
 
     default:
-        MOZ_CRASH("Bad `target` for BindFramebuffer.");
+        MOZ_CRASH("GFX: Bad `target` for BindFramebuffer.");
     }
 }
 
 void
 GLScreenBuffer::BindFB(GLuint fb)
 {
     GLuint drawFB = DrawFB();
     GLuint readFB = ReadFB();
@@ -438,17 +438,17 @@ GLScreenBuffer::AssureBlitted()
 
             mGL->raw_fBlitFramebuffer(0, 0,  srcSize.width,  srcSize.height,
                                       0, 0, destSize.width, destSize.height,
                                       LOCAL_GL_COLOR_BUFFER_BIT,
                                       LOCAL_GL_NEAREST);
         } else if (mGL->IsExtensionSupported(GLContext::APPLE_framebuffer_multisample)) {
             mGL->fResolveMultisampleFramebufferAPPLE();
         } else {
-            MOZ_CRASH("No available blit methods.");
+            MOZ_CRASH("GFX: No available blit methods.");
         }
         // Done!
     }
 
     mNeedsBlit = false;
 }
 
 void
@@ -652,17 +652,17 @@ GLScreenBuffer::SetDrawBuffer(GLenum mod
                                  : LOCAL_GL_COLOR_ATTACHMENT0;
         break;
 
     case LOCAL_GL_NONE:
         internalMode = LOCAL_GL_NONE;
         break;
 
     default:
-        MOZ_CRASH("Bad value.");
+        MOZ_CRASH("GFX: Bad value.");
     }
 
     mGL->MakeCurrent();
     mGL->fDrawBuffers(1, &internalMode);
 }
 
 void
 GLScreenBuffer::SetReadBuffer(GLenum mode)
@@ -889,17 +889,17 @@ ReadBuffer::Create(GLContext* gl,
     case AttachmentType::GLTexture:
         colorTex = surf->ProdTexture();
         target = surf->ProdTextureTarget();
         break;
     case AttachmentType::GLRenderbuffer:
         colorRB = surf->ProdRenderbuffer();
         break;
     default:
-        MOZ_CRASH("Unknown attachment type?");
+        MOZ_CRASH("GFX: Unknown attachment type, create?");
     }
     MOZ_ASSERT(colorTex || colorRB);
 
     GLuint fb = 0;
     gl->fGenFramebuffers(1, &fb);
     gl->AttachBuffersToFB(colorTex, colorRB, depthRB, stencilRB, fb, target);
     gl->mFBOMapping[fb] = surf;
 
@@ -947,17 +947,17 @@ ReadBuffer::Attach(SharedSurface* surf)
         case AttachmentType::GLTexture:
             colorTex = surf->ProdTexture();
             target = surf->ProdTextureTarget();
             break;
         case AttachmentType::GLRenderbuffer:
             colorRB = surf->ProdRenderbuffer();
             break;
         default:
-            MOZ_CRASH("Unknown attachment type?");
+            MOZ_CRASH("GFX: Unknown attachment type, attach?");
         }
 
         mGL->AttachBuffersToFB(colorTex, colorRB, 0, 0, mFB, target);
         mGL->mFBOMapping[mFB] = surf;
         MOZ_ASSERT(mGL->IsFramebufferComplete(mFB));
     }
 
     mSurf = surf;
@@ -984,17 +984,17 @@ ReadBuffer::SetReadBuffer(GLenum userMod
                                   : LOCAL_GL_COLOR_ATTACHMENT0;
         break;
 
     case LOCAL_GL_NONE:
         internalMode = LOCAL_GL_NONE;
         break;
 
     default:
-        MOZ_CRASH("Bad value.");
+        MOZ_CRASH("GFX: Bad value.");
     }
 
     mGL->MakeCurrent();
     mGL->fReadBuffer(internalMode);
 }
 
 } /* namespace gl */
 } /* namespace mozilla */
--- a/gfx/gl/SharedSurface.cpp
+++ b/gfx/gl/SharedSurface.cpp
@@ -73,17 +73,17 @@ SharedSurface::ProdCopy(SharedSurface* s
             ScopedFramebufferForRenderbuffer destWrapper(gl, destRB);
 
             gl->BlitHelper()->BlitFramebufferToFramebuffer(0,
                                                            destWrapper.FB(),
                                                            src->mSize,
                                                            dest->mSize,
                                                            true);
         } else {
-            MOZ_CRASH("Unhandled dest->mAttachType.");
+            MOZ_CRASH("GFX: Unhandled dest->mAttachType 1.");
         }
 
         if (srcNeedsUnlock)
             src->UnlockProd();
 
         if (origNeedsRelock)
             origLocked->LockProd();
 
@@ -118,17 +118,17 @@ SharedSurface::ProdCopy(SharedSurface* s
             ScopedFramebufferForRenderbuffer srcWrapper(gl, srcRB);
 
             gl->BlitHelper()->BlitFramebufferToFramebuffer(srcWrapper.FB(),
                                                            0,
                                                            src->mSize,
                                                            dest->mSize,
                                                            true);
         } else {
-            MOZ_CRASH("Unhandled src->mAttachType.");
+            MOZ_CRASH("GFX: Unhandled src->mAttachType 2.");
         }
 
         if (destNeedsUnlock)
             dest->UnlockProd();
 
         if (origNeedsRelock)
             origLocked->LockProd();
 
@@ -158,17 +158,17 @@ SharedSurface::ProdCopy(SharedSurface* s
             ScopedFramebufferForRenderbuffer destWrapper(gl, destRB);
 
             gl->BlitHelper()->BlitTextureToFramebuffer(srcTex, destWrapper.FB(),
                                                        src->mSize, dest->mSize, srcTarget);
 
             return;
         }
 
-        MOZ_CRASH("Unhandled dest->mAttachType.");
+        MOZ_CRASH("GFX: Unhandled dest->mAttachType 3.");
     }
 
     if (src->mAttachType == AttachmentType::GLRenderbuffer) {
         GLuint srcRB = src->ProdRenderbuffer();
         ScopedFramebufferForRenderbuffer srcWrapper(gl, srcRB);
 
         if (dest->mAttachType == AttachmentType::GLTexture) {
             GLuint destTex = dest->ProdTexture();
@@ -185,20 +185,20 @@ SharedSurface::ProdCopy(SharedSurface* s
             ScopedFramebufferForRenderbuffer destWrapper(gl, destRB);
 
             gl->BlitHelper()->BlitFramebufferToFramebuffer(srcWrapper.FB(), destWrapper.FB(),
                                                            src->mSize, dest->mSize);
 
             return;
         }
 
-        MOZ_CRASH("Unhandled dest->mAttachType.");
+        MOZ_CRASH("GFX: Unhandled dest->mAttachType 4.");
     }
 
-    MOZ_CRASH("Unhandled src->mAttachType.");
+    MOZ_CRASH("GFX: Unhandled src->mAttachType 5.");
 }
 
 ////////////////////////////////////////////////////////////////////////
 // SharedSurface
 
 
 SharedSurface::SharedSurface(SharedSurfaceType type,
                              AttachmentType attachType,
@@ -465,17 +465,17 @@ ScopedReadbackFB::ScopedReadbackFB(Share
 
             // TODO: This should just be BindFB, but we don't have
             // the patch for this yet. (bug 1045955)
             MOZ_ASSERT(mGL->Screen());
             mGL->Screen()->BindReadFB_Internal(0);
             break;
         }
     default:
-        MOZ_CRASH("Unhandled `mAttachType`.");
+        MOZ_CRASH("GFX: Unhandled `mAttachType`.");
     }
 
     if (src->NeedsIndirectReads()) {
         mGL->fGenTextures(1, &mTempTex);
 
         {
             ScopedBindTexture autoTex(mGL, mTempTex);
 
--- a/gfx/gl/SharedSurfaceANGLE.cpp
+++ b/gfx/gl/SharedSurfaceANGLE.cpp
@@ -153,17 +153,17 @@ SharedSurface_ANGLEShareHandle::PollSync
 }
 
 void
 SharedSurface_ANGLEShareHandle::ProducerAcquireImpl()
 {
     if (mKeyedMutex) {
         HRESULT hr = mKeyedMutex->AcquireSync(0, 10000);
         if (hr == WAIT_TIMEOUT) {
-            MOZ_CRASH();
+            MOZ_CRASH("GFX: ANGLE share handle timeout");
         }
     }
 }
 
 void
 SharedSurface_ANGLEShareHandle::ProducerReleaseImpl()
 {
     if (mKeyedMutex) {
@@ -209,17 +209,17 @@ SharedSurface_ANGLEShareHandle::Consumer
                 mConsumerKeyedMutex = mutex;
             }
         }
     }
 
     if (mConsumerKeyedMutex) {
       HRESULT hr = mConsumerKeyedMutex->AcquireSync(0, 10000);
       if (hr == WAIT_TIMEOUT) {
-        MOZ_CRASH();
+        MOZ_CRASH("GFX: ANGLE consumer mutex timeout");
       }
     }
 }
 
 void
 SharedSurface_ANGLEShareHandle::ConsumerReleaseImpl()
 {
     if (mConsumerKeyedMutex) {
@@ -284,17 +284,17 @@ public:
         MOZ_ASSERT(succeeded);
         *succeeded = false;
 
         HRESULT hr;
         mTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mMutex));
         if (mMutex) {
             hr = mMutex->AcquireSync(0, 10000);
             if (hr == WAIT_TIMEOUT) {
-                MOZ_CRASH();
+                MOZ_CRASH("GFX: ANGLE scoped lock timeout");
             }
 
             if (FAILED(hr)) {
                 NS_WARNING("Failed to lock the texture");
                 return;
             }
         }
 
--- a/gfx/layers/CopyableCanvasLayer.cpp
+++ b/gfx/layers/CopyableCanvasLayer.cpp
@@ -64,17 +64,17 @@ CopyableCanvasLayer::Initialize(const Da
       mBufferProvider = aData.mBufferProvider;
     }
   } else if (aData.mBufferProvider) {
     mBufferProvider = aData.mBufferProvider;
   } else if (aData.mRenderer) {
     mAsyncRenderer = aData.mRenderer;
     mOriginPos = gl::OriginPos::BottomLeft;
   } else {
-    MOZ_CRASH("CanvasLayer created without mSurface, mDrawTarget or mGLContext?");
+    MOZ_CRASH("GFX: CanvasLayer created without mSurface, mDrawTarget or mGLContext?");
   }
 
   mBounds.SetRect(0, 0, aData.mSize.width, aData.mSize.height);
 }
 
 bool
 CopyableCanvasLayer::IsDataValid(const Data& aData)
 {
--- a/gfx/layers/GLImages.cpp
+++ b/gfx/layers/GLImages.cpp
@@ -66,17 +66,17 @@ GLImage::GetAsSourceSurface()
   sSnapshotContext->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA,
                                 size.width, size.height, 0,
                                 LOCAL_GL_RGBA,
                                 LOCAL_GL_UNSIGNED_BYTE,
                                 nullptr);
 
   ScopedFramebufferForTexture autoFBForTex(sSnapshotContext, scopedTex.Texture());
   if (!autoFBForTex.IsComplete()) {
-      MOZ_CRASH("ScopedFramebufferForTexture failed.");
+      MOZ_CRASH("GFX: ScopedFramebufferForTexture failed.");
   }
 
   const gl::OriginPos destOrigin = gl::OriginPos::TopLeft;
 
   if (!sSnapshotContext->BlitHelper()->BlitImageToFramebuffer(this, size,
                                                               autoFBForTex.FB(),
                                                               destOrigin))
   {
--- a/gfx/layers/IMFYCbCrImage.cpp
+++ b/gfx/layers/IMFYCbCrImage.cpp
@@ -32,17 +32,17 @@ IMFYCbCrImage::~IMFYCbCrImage()
 
 struct AutoLockTexture
 {
   AutoLockTexture(ID3D11Texture2D* aTexture)
   {
     aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mMutex));
     HRESULT hr = mMutex->AcquireSync(0, 10000);
     if (hr == WAIT_TIMEOUT) {
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: IMFYCbCrImage timeout");
     }
 
     if (FAILED(hr)) {
       NS_WARNING("Failed to lock the texture");
     }
   }
 
   ~AutoLockTexture()
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -189,18 +189,21 @@ using mozilla::gfx::PointTyped;
  * also be set in order for the curving to take effect, as it defines the upper
  * bound of the velocity curve.\n
  * The points (x1, y1) and (x2, y2) used as the two intermediate control points
  * in the cubic bezier curve; the first and last points are (0,0) and (1,1).\n
  * Some example values for these prefs can be found at\n
  * http://mxr.mozilla.org/mozilla-central/source/layout/style/nsStyleStruct.cpp?rev=21282be9ad95#2462
  *
  * \li\b apz.fling_friction
- * Amount of friction applied during flings.
- *
+ * Amount of friction applied during flings. This is used in the following
+ * formula: v(t1) = v(t0) * (1 - f)^(t1 - t0), where v(t1) is the velocity
+ * for a new sample, v(t0) is the velocity at the previous sample, f is the
+ * value of this pref, and (t1 - t0) is the amount of time, in milliseconds,
+ * that has elapsed between the two samples.
  *
  * \li\b apz.fling_repaint_interval
  * Maximum amount of time flinging before sending a viewport change. This will
  * asynchronously repaint the page.\n
  * Units: milliseconds
  *
  * \li\b apz.fling_stop_on_tap_threshold
  * When flinging, if the velocity is above this number, then a tap on the
--- a/gfx/layers/apz/src/Axis.cpp
+++ b/gfx/layers/apz/src/Axis.cpp
@@ -189,44 +189,44 @@ void Axis::OverscrollBy(ParentLayerCoord
   EndOverscrollAnimation();
   aOverscroll = ApplyResistance(aOverscroll);
   if (aOverscroll > 0) {
 #ifdef DEBUG
     if (!FuzzyEqualsCoordinate(GetCompositionEnd().value, GetPageEnd().value)) {
       nsPrintfCString message("composition end (%f) is not equal (within error) to page end (%f)\n",
                               GetCompositionEnd().value, GetPageEnd().value);
       NS_ASSERTION(false, message.get());
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: Overscroll issue > 0");
     }
 #endif
     MOZ_ASSERT(mOverscroll >= 0);
   } else if (aOverscroll < 0) {
 #ifdef DEBUG
     if (!FuzzyEqualsCoordinate(GetOrigin().value, GetPageStart().value)) {
       nsPrintfCString message("composition origin (%f) is not equal (within error) to page origin (%f)\n",
                               GetOrigin().value, GetPageStart().value);
       NS_ASSERTION(false, message.get());
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: Overscroll issue < 0");
     }
 #endif
     MOZ_ASSERT(mOverscroll <= 0);
   }
   mOverscroll += aOverscroll;
 }
 
 ParentLayerCoord Axis::GetOverscroll() const {
   ParentLayerCoord result = (mOverscroll - mLastOverscrollPeak) / mOverscrollScale;
 
   // Assert that we return overscroll in the correct direction
 #ifdef DEBUG
   if ((result.value * mFirstOverscrollAnimationSample.value) < 0.0f) {
     nsPrintfCString message("GetOverscroll() (%f) and first overscroll animation sample (%f) have different signs\n",
                             result.value, mFirstOverscrollAnimationSample.value);
     NS_ASSERTION(false, message.get());
-    MOZ_CRASH();
+    MOZ_CRASH("GFX: Overscroll issue");
   }
 #endif
 
   return result;
 }
 
 void Axis::StartOverscrollAnimation(float aVelocity) {
   // Make sure any state from a previous animation has been cleared.
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -143,17 +143,17 @@ BasicCompositor::CreateRenderTarget(cons
   return rt.forget();
 }
 
 already_AddRefed<CompositingRenderTarget>
 BasicCompositor::CreateRenderTargetFromSource(const IntRect &aRect,
                                               const CompositingRenderTarget *aSource,
                                               const IntPoint &aSourcePoint)
 {
-  MOZ_CRASH("Shouldn't be called!");
+  MOZ_CRASH("GFX: Shouldn't be called!");
   return nullptr;
 }
 
 already_AddRefed<DataTextureSource>
 BasicCompositor::CreateDataTextureSource(TextureFlags aFlags)
 {
   RefPtr<DataTextureSource> result = new DataTextureSourceBasic();
   return result.forget();
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -950,18 +950,17 @@ InstallLayerClipPreserves3D(gfxContext* 
 
   Layer* parent = aLayer->GetParent();
   Matrix4x4 transform3d =
     parent && parent->Extend3DContext() ?
     parent->GetEffectiveTransform() :
     Matrix4x4();
   Matrix transform;
   if (!transform3d.CanDraw2D(&transform)) {
-    MOZ_CRASH("We should not have a 3D transform that CanDraw2D() is false!");
-    return;
+    gfxDevCrash(LogReason::CannotDraw3D) << "GFX: We should not have a 3D transform that CanDraw2D() is false!";
   }
   gfxMatrix oldTransform = aTarget->CurrentMatrix();
   transform *= ToMatrix(oldTransform);
   aTarget->SetMatrix(ThebesMatrix(transform));
 
   aTarget->NewPath();
   aTarget->SnappedRectangle(gfxRect(clipRect->x, clipRect->y,
                                     clipRect->width, clipRect->height));
--- a/gfx/layers/basic/GrallocTextureHostBasic.cpp
+++ b/gfx/layers/basic/GrallocTextureHostBasic.cpp
@@ -39,17 +39,17 @@ HalFormatToSurfaceFormat(int aHalFormat,
       // Needs convert to RGB565
       return gfx::SurfaceFormat::R5G6B5_UINT16;
   default:
     if (aHalFormat >= 0x100 && aHalFormat <= 0x1FF) {
       // Reserved range for HAL specific formats.
       // Needs convert to RGB565
       return gfx::SurfaceFormat::R5G6B5_UINT16;
     } else {
-      MOZ_CRASH("Unhandled HAL pixel format");
+      MOZ_CRASH("GFX: Unhandled HAL pixel format");
       return gfx::SurfaceFormat::UNKNOWN; // not reached
     }
   }
 }
 
 static bool
 NeedsConvertFromYUVtoRGB565(int aHalFormat)
 {
@@ -69,17 +69,17 @@ NeedsConvertFromYUVtoRGB565(int aHalForm
   case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
 #endif
       return true;
   default:
     if (aHalFormat >= 0x100 && aHalFormat <= 0x1FF) {
       // Reserved range for HAL specific formats.
       return true;
     } else {
-      MOZ_CRASH("Unhandled HAL pixel format");
+      MOZ_CRASH("GFX: Unhandled HAL pixel format YUV");
       return false; // not reached
     }
   }
 }
 
 GrallocTextureHostBasic::GrallocTextureHostBasic(
   TextureFlags aFlags,
   const SurfaceDescriptorGralloc& aDescriptor)
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -272,17 +272,17 @@ TexClientFromReadback(SharedSurface* src
       texClient = factory.CreateB8G8R8AX8();
 
     } else if (readFormat == LOCAL_GL_RGBA &&
                readType == LOCAL_GL_UNSIGNED_BYTE)
     {
       // [RR, GG, BB, AA]
       texClient = factory.CreateR8G8B8AX8();
     } else {
-      MOZ_CRASH("Bad `read{Format,Type}`.");
+      MOZ_CRASH("GFX: Bad `read{Format,Type}`.");
     }
 
     MOZ_ASSERT(texClient);
     if (!texClient)
         return nullptr;
 
     // With a texClient, we can lock for writing.
     TextureClientAutoLock autoLock(texClient, OpenMode::OPEN_WRITE);
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -56,17 +56,17 @@ ImageClient::CreateImageClient(Composita
     result = nullptr;
     break;
 #ifdef MOZ_WIDGET_GONK
   case CompositableType::IMAGE_OVERLAY:
     result = new ImageClientOverlay(aForwarder, aFlags);
     break;
 #endif
   default:
-    MOZ_CRASH("unhandled program type");
+    MOZ_CRASH("GFX: unhandled program type image");
   }
 
   NS_ASSERTION(result, "Failed to create ImageClient");
 
   return result.forget();
 }
 
 void
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -1015,26 +1015,26 @@ void PadDrawTargetOutFromRegion(RefPtr<D
       if (x > max)
         x = max;
       return x;
     }
 
     static void ensure_memcpy(uint8_t *dst, uint8_t *src, size_t n, uint8_t *bitmap, int stride, int height)
     {
         if (src + n > bitmap + stride*height) {
-            MOZ_CRASH("long src memcpy");
+            MOZ_CRASH("GFX: long src memcpy");
         }
         if (src < bitmap) {
-            MOZ_CRASH("short src memcpy");
+            MOZ_CRASH("GFX: short src memcpy");
         }
         if (dst + n > bitmap + stride*height) {
-            MOZ_CRASH("long dst mempcy");
+            MOZ_CRASH("GFX: long dst mempcy");
         }
         if (dst < bitmap) {
-            MOZ_CRASH("short dst mempcy");
+            MOZ_CRASH("GFX: short dst mempcy");
         }
     }
 
     static void visitor(void *closure, VisitSide side, int x1, int y1, int x2, int y2) {
       LockedBits *lb = static_cast<LockedBits*>(closure);
       uint8_t *bitmap = lb->data;
       const int bpp = gfx::BytesPerPixel(lb->format);
       const int stride = lb->stride;
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -236,17 +236,17 @@ TextureHost::Create(const SurfaceDescrip
     case SurfaceDescriptor::TSurfaceDescriptorDXGIYCbCr:
       if (aBackend == LayersBackend::LAYERS_D3D9) {
         return CreateTextureHostD3D9(aDesc, aDeallocator, aFlags);
       } else {
         return CreateTextureHostD3D11(aDesc, aDeallocator, aFlags);
       }
 #endif
     default:
-      MOZ_CRASH("Unsupported Surface type");
+      MOZ_CRASH("GFX: Unsupported Surface type host");
   }
 }
 
 already_AddRefed<TextureHost>
 CreateBackendIndependentTextureHost(const SurfaceDescriptor& aDesc,
                                     ISurfaceAllocator* aDeallocator,
                                     TextureFlags aFlags)
 {
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -1089,17 +1089,17 @@ CompositorD3D11::BeginFrame(const nsIntR
 
   if (mAttachments->mSyncTexture) {
     RefPtr<IDXGIKeyedMutex> mutex;
     mAttachments->mSyncTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
 
     MOZ_ASSERT(mutex);
     HRESULT hr = mutex->AcquireSync(0, 10000);
     if (hr == WAIT_TIMEOUT) {
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: D3D11 timeout");
     }
 
     mutex->ReleaseSync(0);
   }
 }
 
 void
 CompositorD3D11::EndFrame()
@@ -1513,17 +1513,17 @@ CompositorD3D11::Failed(HRESULT hr, cons
 void
 CompositorD3D11::HandleError(HRESULT hr, Severity aSeverity)
 {
   if (SUCCEEDED(hr)) {
     return;
   }
 
   if (aSeverity == Critical) {
-    MOZ_CRASH("Unrecoverable D3D11 error");
+    MOZ_CRASH("GFX: Unrecoverable D3D11 error");
   }
 
   if (mDevice != gfxWindowsPlatform::GetPlatform()->GetD3D11Device()) {
     gfxCriticalError() << "Out of sync D3D11 devices in HandleError, " << (int)mVerifyBuffersFailed;
   }
 
   HRESULT hrOnReset = S_OK;
   bool deviceRemoved = hr == DXGI_ERROR_DEVICE_REMOVED;
--- a/gfx/layers/d3d9/CompositorD3D9.cpp
+++ b/gfx/layers/d3d9/CompositorD3D9.cpp
@@ -215,17 +215,17 @@ ShaderModeForEffectType(EffectTypes aEff
   case EffectTypes::RGB:
     if (aFormat == SurfaceFormat::B8G8R8A8 || aFormat == SurfaceFormat::R8G8B8A8)
       return DeviceManagerD3D9::RGBALAYER;
     return DeviceManagerD3D9::RGBLAYER;
   case EffectTypes::YCBCR:
     return DeviceManagerD3D9::YCBCRLAYER;
   }
 
-  MOZ_CRASH("Bad effect type");
+  MOZ_CRASH("GFX: Bad effect type");
 }
 
 void
 CompositorD3D9::ClearRect(const gfx::Rect& aRect)
 {
   D3DRECT rect;
   rect.x1 = aRect.X();
   rect.y1 = aRect.Y();
@@ -605,17 +605,17 @@ CompositorD3D9::Ready()
 }
 
 void
 CompositorD3D9::FailedToResetDevice() {
   mFailedResetAttempts += 1;
   // 10 is a totally arbitrary number that we may want to increase or decrease
   // depending on how things behave in the wild.
   if (mFailedResetAttempts > 10) {
-    MOZ_CRASH("Unable to get a working D3D9 Compositor");
+    MOZ_CRASH("GFX: Unable to get a working D3D9 Compositor");
   }
 }
 
 void
 CompositorD3D9::BeginFrame(const nsIntRegion& aInvalidRegion,
                            const Rect *aClipRectIn,
                            const Rect& aRenderBounds,
                            Rect *aClipRectOut,
--- a/gfx/layers/ipc/SharedBufferManagerParent.cpp
+++ b/gfx/layers/ipc/SharedBufferManagerParent.cpp
@@ -290,17 +290,17 @@ void SharedBufferManagerParent::DropGral
   }
 
   MutexAutoLock mgrlock(mgr->mLock);
   if (mgr->mDestroyed) {
     return;
   }
 
   if (PlatformThread::CurrentId() == mgr->mThread->thread_id()) {
-    MOZ_CRASH("SharedBufferManagerParent::DropGrallocBuffer should not be called on SharedBufferManagerParent thread");
+    MOZ_CRASH("GFX: SharedBufferManagerParent::DropGrallocBuffer should not be called on SharedBufferManagerParent thread");
   } else {
     mgr->mThread->message_loop()->PostTask(FROM_HERE,
                                       NewRunnableFunction(&DropGrallocBufferSync, mgr, aDesc));
   }
   return;
 }
 
 void SharedBufferManagerParent::DropGrallocBufferImpl(mozilla::layers::SurfaceDescriptor aDesc)
--- a/gfx/layers/opengl/GLManager.cpp
+++ b/gfx/layers/opengl/GLManager.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * 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 "GLManager.h"
 #include "CompositorOGL.h"              // for CompositorOGL
 #include "GLContext.h"                  // for GLContext
-#include "mozilla/Assertions.h"         // for MOZ_CRASH
 #include "mozilla/Attributes.h"         // for override
 #include "mozilla/RefPtr.h"             // for RefPtr
 #include "mozilla/layers/Compositor.h"  // for Compositor
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/mozalloc.h"           // for operator new, etc
 #include "nsAutoPtr.h"                  // for nsRefPtr
 
--- a/gfx/src/FilterSupport.cpp
+++ b/gfx/src/FilterSupport.cpp
@@ -1660,17 +1660,17 @@ SourceNeededRegionForPrimitive(const Fil
                                int32_t aInputIndex)
 {
   const AttributeMap& atts = aDescription.Attributes();
   switch (aDescription.Type()) {
 
     case PrimitiveType::Flood:
     case PrimitiveType::Turbulence:
     case PrimitiveType::Image:
-      MOZ_CRASH("this shouldn't be called for filters without inputs");
+      MOZ_CRASH("GFX: this shouldn't be called for filters without inputs");
       return nsIntRegion();
 
     case PrimitiveType::Empty:
       return nsIntRegion();
 
     case PrimitiveType::Blend:
     case PrimitiveType::Composite:
     case PrimitiveType::Merge:
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -809,17 +809,17 @@ gfxPlatform::CreateDrawTargetForUpdateSu
 {
 #ifdef XP_MACOSX
   // this is a bit of a hack that assumes that the buffer associated with the CGContext
   // will live around long enough that nothing bad will happen.
   if (aSurface->GetType() == gfxSurfaceType::Quartz) {
     return Factory::CreateDrawTargetForCairoCGContext(static_cast<gfxQuartzSurface*>(aSurface)->GetCGContext(), aSize);
   }
 #endif
-  MOZ_CRASH("unused function");
+  MOZ_CRASH("GFX: unused function");
   return nullptr;
 }
 
 
 cairo_user_data_key_t kSourceSurface;
 
 /**
  * Record the backend that was used to construct the SourceSurface.
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -197,16 +197,17 @@ private:
   DECL_GFX_PREF(Live, "dom.ipc.plugins.asyncdrawing.enabled",  PluginAsyncDrawingEnabled, bool, false);
   DECL_GFX_PREF(Live, "dom.meta-viewport.enabled",             MetaViewportEnabled, bool, false);
   DECL_GFX_PREF(Once, "dom.vr.enabled",                        VREnabled, bool, false);
   DECL_GFX_PREF(Once, "dom.vr.oculus.enabled",                 VROculusEnabled, bool, true);
   DECL_GFX_PREF(Once, "dom.vr.oculus050.enabled",              VROculus050Enabled, bool, true);
   DECL_GFX_PREF(Once, "dom.vr.cardboard.enabled",              VRCardboardEnabled, bool, false);
   DECL_GFX_PREF(Once, "dom.vr.add-test-devices",               VRAddTestDevices, int32_t, 1);
   DECL_GFX_PREF(Live, "dom.w3c_pointer_events.enabled",        PointerEventsEnabled, bool, false);
+  DECL_GFX_PREF(Live, "dom.w3c_touch_events.enabled",          TouchEventsEnabled, int32_t, 0);
 
   DECL_GFX_PREF(Live, "general.smoothScroll",                  SmoothScrollEnabled, bool, true);
   DECL_GFX_PREF(Live, "general.smoothScroll.durationToIntervalRatio",
                 SmoothScrollDurationToIntervalRatio, int32_t, 200);
   DECL_GFX_PREF(Live, "general.smoothScroll.mouseWheel",       WheelSmoothScrollEnabled, bool, true);
   DECL_GFX_PREF(Live, "general.smoothScroll.mouseWheel.durationMaxMS",
                 WheelSmoothScrollMaxDurationMs, int32_t, 400);
   DECL_GFX_PREF(Live, "general.smoothScroll.mouseWheel.durationMinMS",
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -2966,17 +2966,17 @@ gfxWindowsPlatform::CreateHardwareVsyncS
 
   RefPtr<VsyncSource> d3dVsyncSource = new D3DVsyncSource();
   return d3dVsyncSource.forget();
 }
 
 bool
 gfxWindowsPlatform::SupportsApzTouchInput() const
 {
-  int value = Preferences::GetInt("dom.w3c_touch_events.enabled", 0);
+  int value = gfxPrefs::TouchEventsEnabled();
   return value == 1 || value == 2;
 }
 
 void
 gfxWindowsPlatform::GetAcceleratedCompositorBackends(nsTArray<LayersBackend>& aBackends)
 {
   if (gfxPrefs::LayersPreferOpenGL()) {
     aBackends.AppendElement(LayersBackend::LAYERS_OPENGL);
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -982,26 +982,18 @@ TokenStream::putIdentInTokenbuf(const ch
     }
     userbuf.setAddressOfNextRawChar(tmp);
     return true;
 }
 
 bool
 TokenStream::checkForKeyword(const KeywordInfo* kw, TokenKind* ttp)
 {
-    if (kw->tokentype == TOK_RESERVED
-#ifndef JS_HAS_CLASSES
-        || kw->tokentype == TOK_CLASS
-        || kw->tokentype == TOK_EXTENDS
-        || kw->tokentype == TOK_SUPER
-#endif
-        )
-    {
+    if (kw->tokentype == TOK_RESERVED)
         return reportError(JSMSG_RESERVED_ID, kw->chars);
-    }
 
     if (kw->tokentype == TOK_STRICT_RESERVED)
         return reportStrictModeError(JSMSG_RESERVED_ID, kw->chars);
 
     // Treat 'let' as an identifier and contextually a keyword in sloppy mode.
     // It is always a keyword in strict mode.
     if (kw->tokentype == TOK_LET && !strictMode())
         return true;
deleted file mode 100644
--- a/js/src/jit-test/lib/class.js
+++ /dev/null
@@ -1,1 +0,0 @@
-load(libdir + "../../tests/ecma_6/shell.js");
--- a/js/src/jit-test/lib/syntax.js
+++ b/js/src/jit-test/lib/syntax.js
@@ -1,10 +1,8 @@
-load(libdir + "class.js");
-
 function test_syntax(postfixes, check_error, ignore_opts) {
   function test_reflect(code, module) {
     var options = undefined;
     if (module) {
       options = {target: "module"};
     }
     for (var postfix of postfixes) {
       var cur_code = code + postfix;
@@ -486,28 +484,26 @@ function test_syntax(postfixes, check_er
   test("export const a = ", opts);
   test("export const a = 1 ", opts);
   test("export const a = 1, ", opts);
   test("export const a = 1, b ", opts);
   test("export const a = 1, b = ", opts);
   test("export const a = 1, b = 2 ", opts);
   test("export const a = 1, b = 2; ", opts);
 
-  if (classesEnabled()) {
-    test("export class ", opts);
-    test("export class Foo ", opts);
-    test("export class Foo {  ", opts);
-    test("export class Foo { constructor ", opts);
-    test("export class Foo { constructor( ", opts);
-    test("export class Foo { constructor() ", opts);
-    test("export class Foo { constructor() { ", opts);
-    test("export class Foo { constructor() {} ", opts);
-    test("export class Foo { constructor() {} } ", opts);
-    test("export class Foo { constructor() {} }; ", opts);
-  }
+  test("export class ", opts);
+  test("export class Foo ", opts);
+  test("export class Foo {  ", opts);
+  test("export class Foo { constructor ", opts);
+  test("export class Foo { constructor( ", opts);
+  test("export class Foo { constructor() ", opts);
+  test("export class Foo { constructor() { ", opts);
+  test("export class Foo { constructor() {} ", opts);
+  test("export class Foo { constructor() {} } ", opts);
+  test("export class Foo { constructor() {} }; ", opts);
 
   test("export default ", opts);
   test("export default 1 ", opts);
   test("export default 1; ", opts);
 
   test("export default function ", opts);
   test("export default function() ", opts);
   test("export default function() { ", opts);
@@ -516,37 +512,35 @@ function test_syntax(postfixes, check_er
 
   test("export default function foo ", opts);
   test("export default function foo( ", opts);
   test("export default function foo() ", opts);
   test("export default function foo() { ", opts);
   test("export default function foo() {} ", opts);
   test("export default function foo() {}; ", opts);
 
-  if (classesEnabled()) {
-    test("export default class ", opts);
-    test("export default class { ", opts);
-    test("export default class { constructor ", opts);
-    test("export default class { constructor( ", opts);
-    test("export default class { constructor() ", opts);
-    test("export default class { constructor() { ", opts);
-    test("export default class { constructor() {} ", opts);
-    test("export default class { constructor() {} } ", opts);
-    test("export default class { constructor() {} }; ", opts);
+  test("export default class ", opts);
+  test("export default class { ", opts);
+  test("export default class { constructor ", opts);
+  test("export default class { constructor( ", opts);
+  test("export default class { constructor() ", opts);
+  test("export default class { constructor() { ", opts);
+  test("export default class { constructor() {} ", opts);
+  test("export default class { constructor() {} } ", opts);
+  test("export default class { constructor() {} }; ", opts);
 
-    test("export default class Foo ", opts);
-    test("export default class Foo { ", opts);
-    test("export default class Foo { constructor ", opts);
-    test("export default class Foo { constructor( ", opts);
-    test("export default class Foo { constructor() ", opts);
-    test("export default class Foo { constructor() { ", opts);
-    test("export default class Foo { constructor() {} ", opts);
-    test("export default class Foo { constructor() {} } ", opts);
-    test("export default class Foo { constructor() {} }; ", opts);
-  }
+  test("export default class Foo ", opts);
+  test("export default class Foo { ", opts);
+  test("export default class Foo { constructor ", opts);
+  test("export default class Foo { constructor( ", opts);
+  test("export default class Foo { constructor() ", opts);
+  test("export default class Foo { constructor() { ", opts);
+  test("export default class Foo { constructor() {} ", opts);
+  test("export default class Foo { constructor() {} } ", opts);
+  test("export default class Foo { constructor() {} }; ", opts);
 
   // import
 
   test("import ", opts);
   test("import x ", opts);
   test("import x from ", opts);
   test("import x from 'a' ", opts);
   test("import x from 'a'; ", opts);
@@ -1000,125 +994,123 @@ function test_syntax(postfixes, check_er
   test("({a}) => ");
   test("({a: ");
   test("({a: b ");
   test("({a: b, ");
   test("({a: b} ");
   test("({a: b}) ");
   test("({a: b}) => ");
 
-  if (classesEnabled()) {
-    // ---- Class declaration ----
+  // ---- Class declaration ----
 
-    test("class ");
-    test("class a ");
-    test("class a { ");
-    test("class a { constructor ");
-    test("class a { constructor( ");
-    test("class a { constructor() ");
-    test("class a { constructor() { ");
-    test("class a { constructor() { } ");
-    test("class a { constructor() { } } ");
+  test("class ");
+  test("class a ");
+  test("class a { ");
+  test("class a { constructor ");
+  test("class a { constructor( ");
+  test("class a { constructor() ");
+  test("class a { constructor() { ");
+  test("class a { constructor() { } ");
+  test("class a { constructor() { } } ");
 
-    test("class a { constructor() { } static ");
-    test("class a { constructor() { } static m ");
-    test("class a { constructor() { } static m( ");
-    test("class a { constructor() { } static m() ");
-    test("class a { constructor() { } static m() { ");
-    test("class a { constructor() { } static m() {} ");
-    test("class a { constructor() { } static m() {} } ");
+  test("class a { constructor() { } static ");
+  test("class a { constructor() { } static m ");
+  test("class a { constructor() { } static m( ");
+  test("class a { constructor() { } static m() ");
+  test("class a { constructor() { } static m() { ");
+  test("class a { constructor() { } static m() {} ");
+  test("class a { constructor() { } static m() {} } ");
 
-    test("class a { constructor() { } static ( ");
-    test("class a { constructor() { } static () ");
-    test("class a { constructor() { } static () { ");
-    test("class a { constructor() { } static () {} ");
-    test("class a { constructor() { } static () {} } ");
+  test("class a { constructor() { } static ( ");
+  test("class a { constructor() { } static () ");
+  test("class a { constructor() { } static () { ");
+  test("class a { constructor() { } static () {} ");
+  test("class a { constructor() { } static () {} } ");
 
-    test("class a { constructor() { } static get ");
-    test("class a { constructor() { } static get p ");
-    test("class a { constructor() { } static get p( ");
-    test("class a { constructor() { } static get p() ");
-    test("class a { constructor() { } static get p() { ");
-    test("class a { constructor() { } static get p() {} ");
-    test("class a { constructor() { } static get p() {} } ");
+  test("class a { constructor() { } static get ");
+  test("class a { constructor() { } static get p ");
+  test("class a { constructor() { } static get p( ");
+  test("class a { constructor() { } static get p() ");
+  test("class a { constructor() { } static get p() { ");
+  test("class a { constructor() { } static get p() {} ");
+  test("class a { constructor() { } static get p() {} } ");
 
-    test("class a { constructor() { } static set ");
-    test("class a { constructor() { } static set p ");
-    test("class a { constructor() { } static set p( ");
-    test("class a { constructor() { } static set p(v ");
-    test("class a { constructor() { } static set p(v) ");
-    test("class a { constructor() { } static set p(v) { ");
-    test("class a { constructor() { } static set p(v) {} ");
-    test("class a { constructor() { } static set p(v) {} } ");
+  test("class a { constructor() { } static set ");
+  test("class a { constructor() { } static set p ");
+  test("class a { constructor() { } static set p( ");
+  test("class a { constructor() { } static set p(v ");
+  test("class a { constructor() { } static set p(v) ");
+  test("class a { constructor() { } static set p(v) { ");
+  test("class a { constructor() { } static set p(v) {} ");
+  test("class a { constructor() { } static set p(v) {} } ");
 
-    test("class a { constructor() { } * ");
-    test("class a { constructor() { } *m ");
-    test("class a { constructor() { } *m( ");
-    test("class a { constructor() { } *m() ");
-    test("class a { constructor() { } *m() { ");
-    test("class a { constructor() { } *m() {} ");
-    test("class a { constructor() { } *m() {} } ");
+  test("class a { constructor() { } * ");
+  test("class a { constructor() { } *m ");
+  test("class a { constructor() { } *m( ");
+  test("class a { constructor() { } *m() ");
+  test("class a { constructor() { } *m() { ");
+  test("class a { constructor() { } *m() {} ");
+  test("class a { constructor() { } *m() {} } ");
 
-    test("class a { constructor() { } static * ");
-    test("class a { constructor() { } static *m ");
-    test("class a { constructor() { } static *m( ");
-    test("class a { constructor() { } static *m() ");
-    test("class a { constructor() { } static *m() { ");
-    test("class a { constructor() { } static *m() {} ");
-    test("class a { constructor() { } static *m() {} } ");
+  test("class a { constructor() { } static * ");
+  test("class a { constructor() { } static *m ");
+  test("class a { constructor() { } static *m( ");
+  test("class a { constructor() { } static *m() ");
+  test("class a { constructor() { } static *m() { ");
+  test("class a { constructor() { } static *m() {} ");
+  test("class a { constructor() { } static *m() {} } ");
 
-    test("class a extends ");
-    test("class a extends b ");
-    test("class a extends b { ");
+  test("class a extends ");
+  test("class a extends b ");
+  test("class a extends b { ");
 
-    test("class a extends ( ");
-    test("class a extends ( b ");
-    test("class a extends ( b ) ");
-    test("class a extends ( b ) { ");
+  test("class a extends ( ");
+  test("class a extends ( b ");
+  test("class a extends ( b ) ");
+  test("class a extends ( b ) { ");
 
-    // ---- Class expression ----
+  // ---- Class expression ----
 
-    test("( class ");
-    test("( class a ");
-    test("( class a { ");
-    test("( class a { constructor ");
-    test("( class a { constructor( ");
-    test("( class a { constructor() ");
-    test("( class a { constructor() { ");
-    test("( class a { constructor() { } ");
-    test("( class a { constructor() { } } ");
-    test("( class a { constructor() { } } ) ");
+  test("( class ");
+  test("( class a ");
+  test("( class a { ");
+  test("( class a { constructor ");
+  test("( class a { constructor( ");
+  test("( class a { constructor() ");
+  test("( class a { constructor() { ");
+  test("( class a { constructor() { } ");
+  test("( class a { constructor() { } } ");
+  test("( class a { constructor() { } } ) ");
 
-    test("(class a extends ");
-    test("(class a extends b ");
-    test("(class a extends b { ");
+  test("(class a extends ");
+  test("(class a extends b ");
+  test("(class a extends b { ");
 
-    test("(class a extends ( ");
-    test("(class a extends ( b ");
-    test("(class a extends ( b ) ");
-    test("(class a extends ( b ) { ");
+  test("(class a extends ( ");
+  test("(class a extends ( b ");
+  test("(class a extends ( b ) ");
+  test("(class a extends ( b ) { ");
 
-    test("( class { ");
-    test("( class { constructor ");
-    test("( class { constructor( ");
-    test("( class { constructor() ");
-    test("( class { constructor() { ");
-    test("( class { constructor() { } ");
-    test("( class { constructor() { } } ");
-    test("( class { constructor() { } } ) ");
+  test("( class { ");
+  test("( class { constructor ");
+  test("( class { constructor( ");
+  test("( class { constructor() ");
+  test("( class { constructor() { ");
+  test("( class { constructor() { } ");
+  test("( class { constructor() { } } ");
+  test("( class { constructor() { } } ) ");
 
-    test("(class extends ");
-    test("(class extends b ");
-    test("(class extends b { ");
+  test("(class extends ");
+  test("(class extends b ");
+  test("(class extends b { ");
 
-    test("(class extends ( ");
-    test("(class extends ( b ");
-    test("(class extends ( b ) ");
-    test("(class extends ( b ) { ");
-  }
+  test("(class extends ( ");
+  test("(class extends ( b ");
+  test("(class extends ( b ) ");
+  test("(class extends ( b ) { ");
 
   // ---- Other ----
 
   // Literals
 
   test("a ");
   test("1 ");
   test("1. ");
--- a/js/src/jit-test/tests/asm.js/testBasic.js
+++ b/js/src/jit-test/tests/asm.js/testBasic.js
@@ -1,12 +1,11 @@
 // |jit-test| test-also-noasmjs
 load(libdir + "asm.js");
 load(libdir + "asserts.js");
-load(libdir + "class.js");
 
 assertAsmTypeFail(USE_ASM);
 assertAsmTypeFail(USE_ASM + 'return');
 assertAsmTypeFail(USE_ASM + 'function f() 0');
 assertAsmTypeFail(USE_ASM + 'function f(){}');
 assertAsmTypeFail(USE_ASM + 'function f(){} return 0');
 assertAsmTypeFail(USE_ASM + 'function f() 0; return 0');
 assertAsmTypeFail(USE_ASM + 'function f(){} return g');
@@ -129,20 +128,18 @@ function assertTypeFailInEval(str)
 assertTypeFailInEval('function f({}) { "use asm"; function g() {} return g }');
 assertTypeFailInEval('function f({global}) { "use asm"; function g() {} return g }');
 assertTypeFailInEval('function f(global, {imports}) { "use asm"; function g() {} return g }');
 assertTypeFailInEval('function f(g = 2) { "use asm"; function g() {} return g }');
 assertTypeFailInEval('function *f() { "use asm"; function g() {} return g }');
 assertTypeFailInEval('f => { "use asm"; function g() {} return g }');
 assertTypeFailInEval('var f = { method() {"use asm"; return {}} }');
 assertAsmTypeFail(USE_ASM + 'return {m() {}};');
-if (classesEnabled()) {
-    assertTypeFailInEval('class f { constructor() {"use asm"; return {}} }');
-    assertAsmTypeFail(USE_ASM + 'class c { constructor() {}}; return c;');
-}
+assertTypeFailInEval('class f { constructor() {"use asm"; return {}} }');
+assertAsmTypeFail(USE_ASM + 'class c { constructor() {}}; return c;');
 
 assertAsmTypeFail(USE_ASM + 'function f(i) {i=i|0; (i for (x in [1,2,3])) } return f');
 assertAsmTypeFail(USE_ASM + 'function f(i) {i=i|0; [i for (x in [1,2,3])] } return f');
 assertAsmTypeFail(USE_ASM + 'function f() { (x for (x in [1,2,3])) } return f');
 assertAsmTypeFail(USE_ASM + 'function f() { [x for (x in [1,2,3])] } return f');
 assertTypeFailInEval('function f() { "use asm"; function g(i) {i=i|0; (i for (x in [1,2,3])) } return g }');
 assertTypeFailInEval('function f() { "use asm"; function g(i) {i=i|0; [i for (x in [1,2,3])] } return g }');
 assertTypeFailInEval('function f() { "use asm"; function g() { (x for (x in [1,2,3])) } return g }');
--- a/js/src/jit-test/tests/baseline/classConstructor-AnyScripted.js
+++ b/js/src/jit-test/tests/baseline/classConstructor-AnyScripted.js
@@ -1,12 +1,8 @@
-load(libdir + "class.js");
-
-var test = `
-
 function test(fun) {
     fun();
 }
 
 // Generate a CallAnyScripted stub in test()
 for (let i = 0; i < 20; i++) {
     test(function() { /* wheeee */ });
 }
@@ -18,13 +14,8 @@ class foo {
 // Compile foo()
 for (let i = 0; i < 11; i++)
     new foo();
 
 try {
     test(foo);
     throw new Error("Invoking a class constructor without new must throw");
 } catch (e if e instanceof TypeError) { }
-
-`;
-
-if (classesEnabled())
-    eval(test);
--- a/js/src/jit-test/tests/basic/statement-after-return.js
+++ b/js/src/jit-test/tests/basic/statement-after-return.js
@@ -1,12 +1,10 @@
 // Warning should be shown for unreachable statement after return (bug 1151931).
 
-load(libdir + "class.js");
-
 function testWarn(code, lineNumber, columnNumber) {
   enableLastWarning();
   eval(code);
   var warning = getLastWarning();
   assertEq(warning !== null, true, "warning should be caught for " + code);
   assertEq(warning.name, "None");
   assertEq(warning.lineNumber, lineNumber);
   assertEq(warning.columnNumber, columnNumber);
@@ -208,24 +206,22 @@ function f() {
 testWarn(`
 function* f() {
   return
     yield 1;
 }
 `, 4, 4);
 
 // class
-if (classesEnabled()) {
-  testWarn(`
+testWarn(`
 function f() {
   return
     class A { constructor() {} };
 }
 `, 4, 4);
-}
 
 // unary expression
 testWarn(`
 function f() {
   return
     +1;
 }
 `, 4, 4);
--- a/js/src/jit-test/tests/ion/bug1185957.js
+++ b/js/src/jit-test/tests/ion/bug1185957.js
@@ -1,18 +1,9 @@
 // |jit-test| error: TypeError
 
-load(libdir + "class.js");
-
-var test = `
 class test {
     constructor() {};
 }
+
 (function() {
     test()
 })();
-`;
-
-// Throw, even if we cannot run the test
-if (classesEnabled())
-    eval(test);
-else
-    throw new TypeError();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1228397.js
@@ -0,0 +1,7 @@
+(function() {
+    f = (function(y) {
+        return y ? (2147483648 >>> 0) / 1 == -2147483648 : 2147483648;
+    })
+    assertEq(f(0), 2147483648);
+    assertEq(f(1), false);
+})()
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/inline-Math-random-before-called.js
@@ -0,0 +1,11 @@
+// |jit-test| ion-eager
+
+function ionCompiledEagerly() {
+    Math.random; // establish Math.random's identity for inlining
+    return function() {
+        return +Math.random(); // call will be inlined
+    };
+}
+
+var alreadyIonCompiled = ionCompiledEagerly();
+assertEq(alreadyIonCompiled() < 1, true);
--- a/js/src/jit-test/tests/modules/duplicate-exports.js
+++ b/js/src/jit-test/tests/modules/duplicate-exports.js
@@ -1,22 +1,19 @@
 // Test errors due to duplicate exports
-
 load(libdir + "asserts.js");
 
 function testSyntaxError(source) {
     assertThrowsInstanceOf(function () {
         parseModule(source);
     }, SyntaxError);
 }
 
 testSyntaxError("export var v; export var v;");
 testSyntaxError("export var x, y, z; export var y;");
 testSyntaxError("export default 1; export default 2;");
 testSyntaxError("export var default; export default 1;");
 testSyntaxError("export var default; export default function() {};");
 testSyntaxError("export var default; export default function foo() {};");
 testSyntaxError("var v; export {v}; export {v};");
 testSyntaxError("var v, x; export {v}; export {x as v};");
-if (classesEnabled()) {
-    testSyntaxError("export var default; export default export class { constructor() {} };");
-    testSyntaxError("export var default; export default export class foo { constructor() {} };");
-}
+testSyntaxError("export var default; export default export class { constructor() {} };");
+testSyntaxError("export var default; export default export class foo { constructor() {} };");
--- a/js/src/jit-test/tests/modules/export-declaration.js
+++ b/js/src/jit-test/tests/modules/export-declaration.js
@@ -1,11 +1,10 @@
 load(libdir + "match.js");
 load(libdir + "asserts.js");
-load(libdir + "class.js");
 
 var { Pattern, MatchError } = Match;
 
 program = (elts) => Pattern({
     type: "Program",
     body: elts
 })
 exportDeclaration = (declaration, specifiers, source, isDefault) => Pattern({
@@ -203,28 +202,26 @@ program([
             blockStatement([])
         ),
         null,
         null,
         false
     )
 ]).assert(parseAsModule("export function f() {}"));
 
-if (classesEnabled()) {
-    program([
-        exportDeclaration(
-            classDeclaration(
-                ident("Foo")
-            ),
-            null,
-            null,
-            false
-        )
-    ]).assert(parseAsModule("export class Foo { constructor() {} }"));
-}
+program([
+    exportDeclaration(
+        classDeclaration(
+            ident("Foo")
+        ),
+        null,
+        null,
+        false
+    )
+]).assert(parseAsModule("export class Foo { constructor() {} }"));
 
 program([
     exportDeclaration(
         variableDeclaration([
             {
                 id: ident("a"),
                 init: lit(1)
             }, {
@@ -293,39 +290,37 @@ program([
             blockStatement([])
         ),
         null,
         null,
         true
     )
 ]).assert(parseAsModule("export default function foo() {}"));
 
-if (classesEnabled()) {
-    program([
-        exportDeclaration(
-            classDeclaration(
-                ident("*default*")
-            ),
-            null,
-            null,
-            true
-        )
-    ]).assert(parseAsModule("export default class { constructor() {} }"));
+program([
+    exportDeclaration(
+        classDeclaration(
+            ident("*default*")
+        ),
+        null,
+        null,
+        true
+    )
+]).assert(parseAsModule("export default class { constructor() {} }"));
 
-    program([
-        exportDeclaration(
-            classDeclaration(
-                ident("Foo")
-            ),
-            null,
-            null,
-            true
-        )
-    ]).assert(parseAsModule("export default class Foo { constructor() {} }"));
-}
+program([
+    exportDeclaration(
+        classDeclaration(
+            ident("Foo")
+        ),
+        null,
+        null,
+        true
+    )
+]).assert(parseAsModule("export default class Foo { constructor() {} }"));
 
 program([
     exportDeclaration(
         lit(1234),
         null,
         null,
         true
     )
--- a/js/src/jit-test/tests/modules/export-entries.js
+++ b/js/src/jit-test/tests/modules/export-entries.js
@@ -1,10 +1,8 @@
-load(libdir + "class.js");
-
 // Test localExportEntries property
 
 function testArrayContents(actual, expected) {
     assertEq(actual.length, expected.length);
     for (var i = 0; i < actual.length; i++) {
         for (var property in expected[i]) {
             assertEq(actual[i][property], expected[i][property]);
         }
@@ -27,21 +25,19 @@ testLocalExportEntries(
 testLocalExportEntries(
     'export let x = 1;',
     [{exportName: 'x', moduleRequest: null, importName: null, localName: 'x'}]);
 
 testLocalExportEntries(
     'export const x = 1;',
     [{exportName: 'x', moduleRequest: null, importName: null, localName: 'x'}]);
 
-if (classesEnabled()) {
-    testLocalExportEntries(
-        'export class foo { constructor() {} };',
-        [{exportName: 'foo', moduleRequest: null, importName: null, localName: 'foo'}]);
-}
+testLocalExportEntries(
+    'export class foo { constructor() {} };',
+    [{exportName: 'foo', moduleRequest: null, importName: null, localName: 'foo'}]);
 
 testLocalExportEntries(
     'export default function f() {};',
     [{exportName: 'default', moduleRequest: null, importName: null, localName: 'f'}]);
 
 testLocalExportEntries(
     'export default function() {};',
     [{exportName: 'default', moduleRequest: null, importName: null, localName: '*default*'}]);
--- a/js/src/jit-test/tests/modules/module-environment.js
+++ b/js/src/jit-test/tests/modules/module-environment.js
@@ -1,10 +1,8 @@
-load(libdir + "class.js");
-
 // Test top-level module environment
 
 function testInitialEnvironment(source, expected) {
     let module = parseModule(source);
     let names = getModuleEnvironmentNames(module);
     assertEq(names.length, expected.length);
     expected.forEach(function(name) {
         assertEq(names.includes(name), true);
@@ -17,14 +15,11 @@ testInitialEnvironment('let x = 1;', ['x
 testInitialEnvironment("if (true) { let x = 1; }", []);
 testInitialEnvironment("if (true) { var x = 1; }", ['x']);
 testInitialEnvironment('function x() {}', ['x']);
 testInitialEnvironment('export var x = 1;', ['x']);
 testInitialEnvironment('export let x = 1;', ['x']);
 testInitialEnvironment('export default function x() {};', ['x']);
 testInitialEnvironment('export default 1;', ['*default*']);
 testInitialEnvironment('export default function() {};', ['*default*']);
-
-if (classesEnabled()) {
-    testInitialEnvironment("class x { constructor() {} }", ['x']);
-    testInitialEnvironment('export default class x { constructor() {} };', ['x']);
-    testInitialEnvironment('export default class { constructor() {} };', ['*default*']);
-}
+testInitialEnvironment("class x { constructor() {} }", ['x']);
+testInitialEnvironment('export default class x { constructor() {} };', ['x']);
+testInitialEnvironment('export default class { constructor() {} };', ['*default*']);
--- a/js/src/jit-test/tests/modules/module-evaluation.js
+++ b/js/src/jit-test/tests/modules/module-evaluation.js
@@ -37,35 +37,32 @@ parseModule(`var x = 1;
              export default 2;
              export function f(x) { return x + 1; }`);
 
 // Check we can evaluate top level definitions.
 parseAndEvaluate("var foo = 1;");
 parseAndEvaluate("let foo = 1;");
 parseAndEvaluate("const foo = 1");
 parseAndEvaluate("function foo() {}");
-if (classesEnabled())
-    parseAndEvaluate("class foo { constructor() {} }");
+parseAndEvaluate("class foo { constructor() {} }");
 
 // Check we can evaluate all module-related syntax.
 parseAndEvaluate("export var foo = 1;");
 parseAndEvaluate("export let foo = 1;");
 parseAndEvaluate("export const foo = 1;");
 parseAndEvaluate("var x = 1; export { x };");
 parseAndEvaluate("export default 1");
 parseAndEvaluate("export default function() {};");
 parseAndEvaluate("export default function foo() {};");
 parseAndEvaluate("import a from 'a';");
 parseAndEvaluate("import { x } from 'a';");
 parseAndEvaluate("import * as ns from 'a';");
 parseAndEvaluate("export * from 'a'");
-if (classesEnabled()) {
-    parseAndEvaluate("export default class { constructor() {} };");
-    parseAndEvaluate("export default class foo { constructor() {} };");
-}
+parseAndEvaluate("export default class { constructor() {} };");
+parseAndEvaluate("export default class foo { constructor() {} };");
 
 // Test default import
 m = parseModule("import a from 'a'; a;")
 m.declarationInstantiation();
 assertEq(m.evaluation(), 2);
 
 // Test named import
 m = parseModule("import { x as y } from 'a'; y;")
@@ -79,17 +76,17 @@ assertEq(m.evaluation(), 4);
 
 // Test importing an indirect export
 moduleRepo['b'] = parseModule("export { x as z } from 'a';");
 assertEq(parseAndEvaluate("import { z } from 'b'; z"), 1);
 
 // Test cyclic dependencies
 moduleRepo['c1'] = parseModule("export var x = 1; export {y} from 'c2'");
 moduleRepo['c2'] = parseModule("export var y = 2; export {x} from 'c1'");
-assertDeepEq(parseAndEvaluate(`import { x as x1, y as y1 } from 'c1'; 
+assertDeepEq(parseAndEvaluate(`import { x as x1, y as y1 } from 'c1';
                                import { x as x2, y as y2 } from 'c2';
                                [x1, y1, x2, y2]`),
              [1, 2, 1, 2]);
 
 // Import access in functions
 m = parseModule("import { x } from 'a'; function f() { return x; }")
 m.declarationInstantiation();
 m.evaluation();
--- a/js/src/jit-test/tests/parser/arrow-rest.js
+++ b/js/src/jit-test/tests/parser/arrow-rest.js
@@ -1,13 +1,11 @@
 // The parser should throw SyntaxError immediately if it finds "..." in a
 // context where it's not allowed.
 
-load(libdir + "class.js");
-
 function testThrow(code, column) {
   var caught = false;
   try {
     eval(code);
   } catch (e) {
     caught = true;
     assertEq(e.columnNumber, column);
   }
@@ -138,21 +136,19 @@ throw ...a) =>
 `, 6);
 
 testThrow(`
 try {} catch (x if ...a) =>
 `, 19);
 
 // class
 
-if (classesEnabled()) {
 testThrow(`
 class A extends ...a) =>
 `, 16);
-}
 
 // conditional expression
 
 testThrow(`
 1 ? ...a) =>
 `, 4);
 
 testThrow(`
--- a/js/src/jit-test/tests/xdr/classes.js
+++ b/js/src/jit-test/tests/xdr/classes.js
@@ -1,11 +1,7 @@
 load(libdir + 'bytecode-cache.js');
-load(libdir + 'class.js');
-
-if (!classesEnabled())
-    quit();
 
 var test = "new class extends class { } { constructor() { super(); } }()";
 evalWithCache(test, { assertEqBytecode : true });
 
 var test = "new class { method() { super.toString(); } }().method()";
 evalWithCache(test, { assertEqBytecode : true });
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -1355,18 +1355,20 @@ IonBuilder::inlineMathRandom(CallInfo& c
     if (callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
     if (getInlineReturnType() != MIRType_Double)
         return InliningStatus_NotInlined;
 
-    MOZ_ASSERT(script()->compartment()->randomNumberGenerator.isSome(),
-               "MRandom JIT code depends on RNG being initialized");
+    // MRandom JIT code directly accesses the RNG. It's (barely) possible to
+    // inline Math.random without it having been called yet, so ensure RNG
+    // state that isn't guaranteed to be initialized already.
+    script()->compartment()->ensureRandomNumberGenerator();
 
     callInfo.setImplicitlyUsedUnchecked();
 
     MRandom* rand = MRandom::New(alloc());
     current->add(rand);
     current->push(rand);
     return InliningStatus_Inlined;
 }
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
@@ -1106,21 +1106,30 @@ CodeGeneratorX86Shared::visitDivPowTwoI(
                 masm.shrl(Imm32(32 - shift), lhs);
                 masm.addl(lhsCopy, lhs);
             }
             masm.sarl(Imm32(shift), lhs);
 
             if (negativeDivisor)
                 masm.negl(lhs);
         }
-    } else if (shift == 0 && negativeDivisor) {
-        // INT32_MIN / -1 overflows.
-        masm.negl(lhs);
-        if (!mir->isTruncated())
-            bailoutIf(Assembler::Overflow, ins->snapshot());
+    } else if (shift == 0) {
+        if (negativeDivisor) {
+            // INT32_MIN / -1 overflows.
+            masm.negl(lhs);
+            if (!mir->isTruncated())
+                bailoutIf(Assembler::Overflow, ins->snapshot());
+        }
+
+        else if (mir->isUnsigned() && !mir->isTruncated()) {
+            // Unsigned division by 1 can overflow if output is not
+            // truncated.
+            masm.test32(lhs, lhs);
+            bailoutIf(Assembler::Signed, ins->snapshot());
+        }
     }
 }
 
 void
 CodeGeneratorX86Shared::visitDivOrModConstantI(LDivOrModConstantI* ins) {
     Register lhs = ToRegister(ins->numerator());
     Register output = ToRegister(ins->output());
     int32_t d = ins->denominator();
--- a/js/src/jsversion.h
+++ b/js/src/jsversion.h
@@ -34,17 +34,14 @@
 /*
  * Feature for Object.prototype.__{define,lookup}{G,S}etter__ legacy support;
  * support likely to be made opt-in at some future time.
  */
 #define JS_OLD_GETTER_SETTER_METHODS    1
 
 #ifdef NIGHTLY_BUILD
 
-/* Support for ES6 Classes. */
-#define JS_HAS_CLASSES 1
-
 /* Support for ES7 Exponentiation proposal. */
 #define JS_HAS_EXPONENTIATION 1
 
 #endif // NIGHTLY_BUILD
 
 #endif /* jsversion_h */
--- a/js/src/tests/ecma_6/Class/boundFunctionSubclassing.js
+++ b/js/src/tests/ecma_6/Class/boundFunctionSubclassing.js
@@ -1,24 +1,17 @@
-var test = `
-
 class func extends Function { }
 let inst = new func("x", "return this.bar + x");
 
 // First, ensure that we get sane prototype chains for the bound instance
 let bound = inst.bind({bar: 3}, 4);
 assertEq(bound instanceof func, true);
 assertEq(bound(), 7);
 
 // Check the corner case for Function.prototype.bind where the function has
 // a null [[Prototype]]
 Object.setPrototypeOf(inst, null);
 bound = Function.prototype.bind.call(inst, {bar:1}, 3);
 assertEq(Object.getPrototypeOf(bound), null);
 assertEq(bound(), 4);
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/bytecodePatternMatching.js
+++ b/js/src/tests/ecma_6/Class/bytecodePatternMatching.js
@@ -1,11 +1,10 @@
 // Constructors can't be called so we can't pattern match
 // them in replace and sort.
-var test = `
 function a() {
     var b = {a: "A"};
 
     class X {
         constructor(a) {
             return b[a]
         }
     };
@@ -20,15 +19,11 @@ function b() {
         }
     }
 
     assertThrowsInstanceOf(() => [1, 2, 3].sort(X), TypeError);
 }
 
 a();
 b();
-`;
-
-if (classesEnabled())
-    eval(test);
 
 if (typeof reportCompare === "function")
     reportCompare(0, 0, "OK");
--- a/js/src/tests/ecma_6/Class/classConstructorNoCall.js
+++ b/js/src/tests/ecma_6/Class/classConstructorNoCall.js
@@ -1,11 +1,9 @@
 // Class constructors don't have a [[Call]]
-var test = `
-
 class Foo {
     constructor() { }
 }
 
 assertThrowsInstanceOf(Foo, TypeError);
 
 class Bar extends Foo {
     constructor() { }
@@ -14,15 +12,10 @@ class Bar extends Foo {
 assertThrowsInstanceOf(Bar, TypeError);
 
 assertThrowsInstanceOf(class { constructor() { } }, TypeError);
 assertThrowsInstanceOf(class extends Foo { constructor() { } }, TypeError);
 
 assertThrowsInstanceOf(class foo { constructor() { } }, TypeError);
 assertThrowsInstanceOf(class foo extends Foo { constructor() { } }, TypeError);
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/classHeritage.js
+++ b/js/src/tests/ecma_6/Class/classHeritage.js
@@ -1,17 +1,15 @@
-var test = `
-
 // It's an error to have a non-constructor as your heritage
-assertThrowsInstanceOf(() => eval(\`class a extends Math.sin {
+assertThrowsInstanceOf(() => eval(`class a extends Math.sin {
                                         constructor() { }
-                                    }\`), TypeError);
-assertThrowsInstanceOf(() => eval(\`(class a extends Math.sin {
+                                    }`), TypeError);
+assertThrowsInstanceOf(() => eval(`(class a extends Math.sin {
                                         constructor() { }
-                                    })\`), TypeError);
+                                    })`), TypeError);
 
 // Unless it's null, in which case it works like a normal class, except that
 // the prototype object does not inherit from Object.prototype.
 class basic {
     constructor() { }
 }
 class nullExtends extends null {
     constructor() { }
@@ -90,15 +88,10 @@ function stillNo() {
 function stillNoExpr() {
     (class extends nope {
         constructor() { }
      });
 }
 assertThrowsInstanceOf(stillNo, TypeError);
 assertThrowsInstanceOf(stillNoExpr, TypeError);
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === "function")
     reportCompare(0, 0, "OK");
--- a/js/src/tests/ecma_6/Class/className.js
+++ b/js/src/tests/ecma_6/Class/className.js
@@ -51,17 +51,16 @@ function testName(C, name, hasValue, has
     } else {
         assertEq("set" in desc, true);
         assertEq(desc.set, undefined);
         // assertEq(desc.enumerable, false);
         assertEq(desc.configurable, true);
     }
 }
 
-var test = `
 // ---- declaration ---
 
 class Decl {
     constructor() {}
 }
 testName(Decl, "Decl", true, false, false);
 
 class DeclWithFunc {
@@ -238,15 +237,11 @@ testName(ExtendedExpr4, "", false, false
 
 let ExtendedExpr5 = class ExtendedExpr5 extends AnonWithGetterSetter {
     constructor() {}
     static get name() { return "extend"; }
 }
 testName(ExtendedExpr5, "extend", false, true, false);
 delete ExtendedExpr5.name;
 testName(ExtendedExpr5, "base", false, false, false);
-`;
-
-if (classesEnabled())
-    eval(test);
 
 if (typeof reportCompare === "function")
     reportCompare(0, 0, "OK");
--- a/js/src/tests/ecma_6/Class/classPrototype.js
+++ b/js/src/tests/ecma_6/Class/classPrototype.js
@@ -1,10 +1,8 @@
-var test = `
-
 // The prototype of a class is a non-writable, non-configurable, non-enumerable data property.
 class a { constructor() { } }
 let b = class { constructor() { } };
 for (let test of [a,b]) {
     var protoDesc = Object.getOwnPropertyDescriptor(test, "prototype");
     assertEq(protoDesc.writable, false);
     assertEq(protoDesc.configurable, false);
     assertEq(protoDesc.enumerable, false);
@@ -20,52 +18,48 @@ for (let test of [a,b]) {
                                                             enumerable: false,
                                                             value: test });
     assertDeepEq(prototype, desiredPrototype);
 }
 
 // As such, it should by a TypeError to try and overwrite "prototype" with a
 // static member. The only way to try is with a computed property name; the rest
 // are early errors.
-assertThrowsInstanceOf(() => eval(\`
+assertThrowsInstanceOf(() => eval(`
                                   class a {
                                     constructor() { };
                                     static ["prototype"]() { }
                                   }
-                                  \`), TypeError);
-assertThrowsInstanceOf(() => eval(\`
+                                  `), TypeError);
+assertThrowsInstanceOf(() => eval(`
                                   class a {
                                     constructor() { };
                                     static get ["prototype"]() { }
                                   }
-                                  \`), TypeError);
-assertThrowsInstanceOf(() => eval(\`
+                                  `), TypeError);
+assertThrowsInstanceOf(() => eval(`
                                   class a {
                                     constructor() { };
                                     static set ["prototype"](x) { }
                                   }
-                                  \`), TypeError);
+                                  `), TypeError);
 
-assertThrowsInstanceOf(() => eval(\`(
+assertThrowsInstanceOf(() => eval(`(
                                   class a {
                                     constructor() { };
                                     static ["prototype"]() { }
                                   }
-                                  )\`), TypeError);
-assertThrowsInstanceOf(() => eval(\`(
+                                  )`), TypeError);
+assertThrowsInstanceOf(() => eval(`(
                                   class a {
                                     constructor() { };
                                     static get ["prototype"]() { }
                                   }
-                                  )\`), TypeError);
-assertThrowsInstanceOf(() => eval(\`(
+                                  )`), TypeError);
+assertThrowsInstanceOf(() => eval(`(
                                   class a {
                                     constructor() { };
                                     static set ["prototype"](x) { }
                                   }
-                                  )\`), TypeError);
-`;
-
-if (classesEnabled())
-    eval(test);
+                                  )`), TypeError);
 
 if (typeof reportCompare === "function")
     reportCompare(0, 0, "OK");
--- a/js/src/tests/ecma_6/Class/compPropDestr.js
+++ b/js/src/tests/ecma_6/Class/compPropDestr.js
@@ -2,10 +2,10 @@ var BUGNUMBER = 924688;
 var summary = 'Computed Property Names';
 
 print(BUGNUMBER + ": " + summary);
 
 var key = "z";
 var { [key]: foo } = { z: "bar" };
 assertEq(foo, "bar");
 
-
-reportCompare(0, 0, "ok");
+if (typeof reportCompare === 'function')
+    reportCompare(0, 0, "ok");
--- a/js/src/tests/ecma_6/Class/compPropNames.js
+++ b/js/src/tests/ecma_6/Class/compPropNames.js
@@ -234,10 +234,10 @@ obj = {
     get [a = "hey"]() { return 1; },
     get [a = {b : 4}.b]() { return 2; },
     set [/x/.source](a) { throw 3; }
 }
 assertEq(obj.hey, 1);
 assertEq(obj[4], 2);
 assertThrowsValue(() => { obj.x = 7; }, 3);
 
-
-reportCompare(0, 0, "ok");
+if (typeof reportCompare === 'function')
+    reportCompare(0, 0, "ok");
--- a/js/src/tests/ecma_6/Class/constructorCalled.js
+++ b/js/src/tests/ecma_6/Class/constructorCalled.js
@@ -1,13 +1,11 @@
 // The constructor specified should get called, regardless of order, or
 // other distractions
 
-var test = `
-
 var called = false;
 class a { constructor(x) { assertEq(x, 4); called = true } }
 new a(4);
 assertEq(called, true);
 
 called = false;
 var aExpr = class { constructor(x) { assertEq(x, 4); called = true } };
 new aExpr(4);
@@ -29,23 +27,19 @@ new c();
 assertEq(called, true);
 
 called = false;
 var cExpr = class { method() { } constructor() { called = true; } }
 new cExpr();
 assertEq(called, true);
 
 called = false;
-class d { [\"constructor\"]() { throw new Error(\"NO\"); } constructor() { called = true; } }
+class d { ["constructor"]() { throw new Error("NO"); } constructor() { called = true; } }
 new d();
 assertEq(called, true);
 
 called = false;
-var dExpr = class { [\"constructor\"]() { throw new Error(\"NO\"); } constructor() { called = true; } }
+var dExpr = class { ["constructor"]() { throw new Error("NO"); } constructor() { called = true; } }
 new dExpr();
 assertEq(called, true);
-`;
-
-if (classesEnabled())
-    eval(test);
 
 if (typeof reportCompare === "function")
     reportCompare(0, 0, "OK");
--- a/js/src/tests/ecma_6/Class/defaultConstructorBase.js
+++ b/js/src/tests/ecma_6/Class/defaultConstructorBase.js
@@ -1,25 +1,18 @@
-var test = `
-
 class base {
     method() { return 1; }
     *gen() { return 2; }
     static sMethod() { return 3; }
     get answer() { return 42; }
 }
 
 // Having a default constructor should work, and also not make you lose
 // everything for no good reason
 
 assertEq(Object.getPrototypeOf(new base()), base.prototype);
 assertEq(new base().method(), 1);
 assertEq(new base().gen().next().value, 2);
 assertEq(base.sMethod(), 3);
 assertEq(new base().answer, 42);
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/defaultConstructorNotCallable.js
+++ b/js/src/tests/ecma_6/Class/defaultConstructorNotCallable.js
@@ -1,15 +1,8 @@
-var test = `
-
 class badBase {}
 assertThrowsInstanceOf(badBase, TypeError);
 
 class badSub extends (class {}) {}
 assertThrowsInstanceOf(badSub, TypeError);
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/derivedConstructorArrowEvalBinding.js
+++ b/js/src/tests/ecma_6/Class/derivedConstructorArrowEvalBinding.js
@@ -1,19 +1,12 @@
 // Make sure it doesn't matter when we make the arrow function
-var test = `
-
 new class extends class { } {
     constructor() {
         let arrow = () => this;
         assertThrowsInstanceOf(arrow, ReferenceError);
         super();
         assertEq(arrow(), this);
     }
 }();
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/derivedConstructorArrowEvalClosed.js
+++ b/js/src/tests/ecma_6/Class/derivedConstructorArrowEvalClosed.js
@@ -1,18 +1,11 @@
-var test = `
-
 new class extends class { } {
     constructor() {
         let a1 = () => this;
         let a2 = (() => super());
         assertThrowsInstanceOf(a1, ReferenceError);
         assertEq(a2(), a1());
     }
 }();
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/derivedConstructorArrowEvalEscape.js
+++ b/js/src/tests/ecma_6/Class/derivedConstructorArrowEvalEscape.js
@@ -1,20 +1,13 @@
-var test = `
-
 let arrow;
 
 class foo extends class { } {
     constructor() {
         arrow = () => this;
         super();
     }
 }
 
 assertEq(new foo(), arrow());
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/derivedConstructorArrowEvalEscapeUninitialized.js
+++ b/js/src/tests/ecma_6/Class/derivedConstructorArrowEvalEscapeUninitialized.js
@@ -1,10 +1,8 @@
-var test = `
-
 let superArrow;
 let thisArrow;
 
 let thisStash;
 
 class base {
     constructor() {
         // We run this constructor twice as part of the double init check
@@ -31,15 +29,10 @@ assertThrowsInstanceOf(thisArrow, Refere
 superArrow();
 
 // Can't call it twice
 assertThrowsInstanceOf(superArrow, ReferenceError);
 
 // Oh look, |this| is populated, now.
 assertEq(thisArrow(), thisStash);
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/derivedConstructorArrowEvalGetThis.js
+++ b/js/src/tests/ecma_6/Class/derivedConstructorArrowEvalGetThis.js
@@ -1,17 +1,10 @@
-var test = `
-
 new class extends class { } {
     constructor() {
         super();
         assertEq(this, (()=>this)());
         assertEq(this, eval("this"));
     }
 }();
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/derivedConstructorArrowEvalNestedSuperCall.js
+++ b/js/src/tests/ecma_6/Class/derivedConstructorArrowEvalNestedSuperCall.js
@@ -1,10 +1,8 @@
-var test = `
-
 new class extends class { } {
     constructor() {
         (()=>eval("super()"))();
         assertEq(this, eval("this"));
         assertEq(this, (()=>this)());
     }
 }();
 
@@ -27,15 +25,10 @@ new class extends class { } {
 new class extends class { } {
     constructor() {
         eval("eval('super()')");
         assertEq(this, eval("this"));
         assertEq(this, (()=>this)());
     }
 }();
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/derivedConstructorArrowEvalSuperCall.js
+++ b/js/src/tests/ecma_6/Class/derivedConstructorArrowEvalSuperCall.js
@@ -1,25 +1,18 @@
-var test = `
-
 new class extends class { } {
     constructor() {
         assertEq(eval("super(); this"), this);
         assertEq(this, eval("this"));
         assertEq(this, (()=>this)());
     }
 }();
 
 new class extends class { } {
     constructor() {
         (()=>super())();
         assertEq(this, eval("this"));
         assertEq(this, (()=>this)());
     }
 }();
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/derivedConstructorInlining.js
+++ b/js/src/tests/ecma_6/Class/derivedConstructorInlining.js
@@ -1,10 +1,8 @@
-var test = `
-
 // Since we (for now!) can't emit jitcode for derived class statements. Make
 // sure we can correctly invoke derived class constructors.
 
 class foo extends null {
     constructor() {
         // Anything that tests |this| should throw, so just let it run off the
         // end.
     }
@@ -12,15 +10,10 @@ class foo extends null {
 
 function intermediate() {
     new foo();
 }
 
 for (let i = 0; i < 1100; i++)
     assertThrownErrorContains(intermediate, "|this|");
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/derivedConstructorReturnPrimitive.js
+++ b/js/src/tests/ecma_6/Class/derivedConstructorReturnPrimitive.js
@@ -1,22 +1,15 @@
-var test = `
-
 class foo extends null {
     constructor() {
         // Returning a primitive is a TypeError in derived constructors. This
         // ensures that super() can take the return value directly, without
         // checking it. Use |null| here, as a tricky check to make sure we
         // didn't lump it in with the object check, somehow.
         return null;
     }
 }
 
 for (let i = 0; i < 1100; i++)
     assertThrownErrorContains(() => new foo(), "return");
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/derivedConstructorTDZExplicitThis.js
+++ b/js/src/tests/ecma_6/Class/derivedConstructorTDZExplicitThis.js
@@ -1,19 +1,12 @@
-var test = `
-
 class foo extends null {
     constructor() {
         this;
         assertEq(false, true);
     }
 }
 
 for (let i = 0; i < 1100; i++)
     assertThrownErrorContains(() => new foo(), "|this|");
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/derivedConstructorTDZOffEdge.js
+++ b/js/src/tests/ecma_6/Class/derivedConstructorTDZOffEdge.js
@@ -1,18 +1,11 @@
-var test = `
-
 class foo extends null {
     constructor() {
         // Let it fall off the edge and throw.
     }
 }
 
 for (let i = 0; i < 1100; i++)
     assertThrownErrorContains(() => new foo(), "|this|");
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/derivedConstructorTDZReturnObject.js
+++ b/js/src/tests/ecma_6/Class/derivedConstructorTDZReturnObject.js
@@ -1,20 +1,13 @@
-var test = `
-
 class foo extends null {
     constructor() {
         // If you return an object, we don't care that |this| went
         // uninitialized
         return {};
     }
 }
 
 for (let i = 0; i < 1100; i++)
     new foo();
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/derivedConstructorTDZReturnUndefined.js
+++ b/js/src/tests/ecma_6/Class/derivedConstructorTDZReturnUndefined.js
@@ -1,20 +1,13 @@
-var test = `
-
 class foo extends null {
     constructor() {
         // Explicit returns of undefined should act the same as falling off the
         // end of the function. That is to say, they should throw.
         return undefined;
     }
 }
 
 for (let i = 0; i < 1100; i++)
     assertThrownErrorContains(() => new foo(), "|this|");
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/extendBuiltinConstructors.js
+++ b/js/src/tests/ecma_6/Class/extendBuiltinConstructors.js
@@ -1,10 +1,8 @@
-var test = `
-
 function testBuiltinInstanceIsInstanceOf(instance, builtin, class_) {
     assertEq(instance instanceof class_, true);
     assertEq(instance instanceof builtin, true);
 
     if (builtin === Array)
         assertEq(Array.isArray(instance), true);
 }
 
@@ -98,15 +96,10 @@ testBuiltin(DataView, new ArrayBuffer())
 testBuiltin(DataView, new (newGlobal().ArrayBuffer)());
 testBuiltin(String);
 testBuiltin(Array);
 testBuiltin(Array, 15);
 testBuiltin(Array, 3.0);
 testBuiltin(Array, "non-length one-arg");
 testBuiltin(Array, 5, 10, 15, "these are elements");
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/geterNoExprClosure.js
+++ b/js/src/tests/ecma_6/Class/geterNoExprClosure.js
@@ -1,24 +1,20 @@
 // getter/setter with expression closure is allowed only in object literal.
 
-function test() {
-  assertThrowsInstanceOf(() => eval(`
-class foo {
-  constructor() {}
+assertThrowsInstanceOf(() => eval(`
+  class foo {
+    constructor() {}
 
-  get a() 1
-}
+    get a() 1
+  }
 `), SyntaxError);
-  assertThrowsInstanceOf(() => eval(`
-class foo {
-  constructor() {}
 
-  set a(v) 1
-}
+assertThrowsInstanceOf(() => eval(`
+  class foo {
+    constructor() {}
+
+    set a(v) 1
+  }
 `), SyntaxError);
-}
-
-if (classesEnabled())
-    test();
 
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/innerBinding.js
+++ b/js/src/tests/ecma_6/Class/innerBinding.js
@@ -1,35 +1,31 @@
 // Named class definitions should create an immutable inner binding.
 // Since all code in classes is in strict mode, attempts to mutate it
 // should throw.
-
-if (classesEnabled()) {
-
-var test = `
 class Foof { constructor() { }; tryBreak() { Foof = 4; } }
 for (let result of [Foof, class Bar { constructor() { }; tryBreak() { Bar = 4; } }])
     assertThrowsInstanceOf(() => new result().tryBreak(), TypeError);
 
 {
     class foo { constructor() { }; tryBreak() { foo = 4; } }
     for (let result of [foo, class Bar { constructor() { }; tryBreak() { Bar = 4 } }])
         assertThrowsInstanceOf(() => new result().tryBreak(), TypeError);
 }
 
 // TDZ applies to inner bindings
-assertThrowsInstanceOf(()=>eval(\`class Bar {
+assertThrowsInstanceOf(()=>eval(`class Bar {
                                     constructor() { };
                                     [Bar] () { };
-                                 }\`), ReferenceError);
+                                 }`), ReferenceError);
 
-assertThrowsInstanceOf(()=>eval(\`(class Bar {
+assertThrowsInstanceOf(()=>eval(`(class Bar {
                                     constructor() { };
                                     [Bar] () { };
-                                 })\`), ReferenceError);
+                                 })`), ReferenceError);
 
 // There's no magic "inner binding" global
 {
     class Foo {
         constructor() { };
         test() {
             class Bar {
                 constructor() { }
@@ -81,17 +77,10 @@ assertEq(new class Foo {
     }
 
     orig_X = X;
     X = 13;
     assertEq(X, 13);
     new orig_X().f();
 }
 
-
-`;
-
-eval(test);
-
-}
-
 if (typeof reportCompare === "function")
     reportCompare(0, 0, "OK");
--- a/js/src/tests/ecma_6/Class/methDefn.js
+++ b/js/src/tests/ecma_6/Class/methDefn.js
@@ -195,10 +195,10 @@ function testStrictMode() {
     obj.static = 4;
 }
 testStrictMode();
 
 // Tests provided by benvie in the bug to distinguish from ES5 desugar.
 assertEq(({ method() {} }).method.name, "method");
 assertThrowsInstanceOf(function() {({ method() { method() } }).method() }, ReferenceError);
 
-
-reportCompare(0, 0, "ok");
+if (typeof reportCompare === 'function')
+    reportCompare(0, 0, "ok");
--- a/js/src/tests/ecma_6/Class/methDefnGen.js
+++ b/js/src/tests/ecma_6/Class/methDefnGen.js
@@ -74,9 +74,10 @@ a = {*b(c){"use strict";yield c;}};
 assertEq(a.b(1).next().value, 1);
 a = {*["b"](c){"use strict";return c;}};
 assertEq(a.b(1).next().value, 1);
 
 // Generators should not have [[Construct]]
 a = {*g() { yield 1; }}
 assertThrowsInstanceOf(() => { new a.g }, TypeError);
 
-reportCompare(0, 0, "ok");
+if (typeof reportCompare === 'function')
+    reportCompare(0, 0, "ok");
--- a/js/src/tests/ecma_6/Class/methodInstallation.js
+++ b/js/src/tests/ecma_6/Class/methodInstallation.js
@@ -1,12 +1,10 @@
 // Do the things we write in classes actually appear as they are supposed to?
 
-var test= `
-
 var methodCalled;
 var getterCalled;
 var setterCalled;
 var constructorCalled;
 var staticMethodCalled;
 var staticGetterCalled;
 var staticSetterCalled;
 class testClass {
@@ -35,73 +33,69 @@ for (let a of [testClass,
     methodCalled = false;
     getterCalled = false;
     setterCalled = false;
     constructorCalled = false;
     staticMethodCalled = false;
     staticGetterCalled = false;
     staticSetterCalled = false;
 
-    var aConstDesc = Object.getOwnPropertyDescriptor(a.prototype, \"constructor\");
+    var aConstDesc = Object.getOwnPropertyDescriptor(a.prototype, "constructor");
     assertEq(aConstDesc.writable, true);
     assertEq(aConstDesc.configurable, true);
     assertEq(aConstDesc.enumerable, false);
     new aConstDesc.value();
     assertEq(constructorCalled, true);
 
     // __proto__ is just an identifier for classes. No prototype changes are made.
     assertEq(Object.getPrototypeOf(a.prototype), Object.prototype);
-    var aMethDesc = Object.getOwnPropertyDescriptor(a.prototype, \"__proto__\");
+    var aMethDesc = Object.getOwnPropertyDescriptor(a.prototype, "__proto__");
     assertEq(aMethDesc.writable, true);
     assertEq(aMethDesc.configurable, true);
     assertEq(aMethDesc.enumerable, false);
     aMethDesc.value();
     assertEq(methodCalled, true);
 
-    var aGetDesc = Object.getOwnPropertyDescriptor(a.prototype, \"getter\");
+    var aGetDesc = Object.getOwnPropertyDescriptor(a.prototype, "getter");
     assertEq(aGetDesc.configurable, true);
     assertEq(aGetDesc.enumerable, false);
     aGetDesc.get();
     assertThrowsInstanceOf(() => new aGetDesc.get, TypeError);
     assertEq(getterCalled, true);
 
-    var aSetDesc = Object.getOwnPropertyDescriptor(a.prototype, \"setter\");
+    var aSetDesc = Object.getOwnPropertyDescriptor(a.prototype, "setter");
     assertEq(aSetDesc.configurable, true);
     assertEq(aSetDesc.enumerable, false);
     aSetDesc.set();
     assertThrowsInstanceOf(() => new aSetDesc.set, TypeError);
     assertEq(setterCalled, true);
-    assertDeepEq(aSetDesc, Object.getOwnPropertyDescriptor(a.prototype, \"setter\"));
+    assertDeepEq(aSetDesc, Object.getOwnPropertyDescriptor(a.prototype, "setter"));
 
-    assertEq(Object.getOwnPropertyDescriptor(new a(), \"staticMethod\"), undefined);
-    var aStaticMethDesc = Object.getOwnPropertyDescriptor(a, \"staticMethod\");
+    assertEq(Object.getOwnPropertyDescriptor(new a(), "staticMethod"), undefined);
+    var aStaticMethDesc = Object.getOwnPropertyDescriptor(a, "staticMethod");
     assertEq(aStaticMethDesc.configurable, true);
     assertEq(aStaticMethDesc.enumerable, false);
     assertEq(aStaticMethDesc.writable, true);
     aStaticMethDesc.value();
     assertThrowsInstanceOf(() => new aStaticMethDesc.value, TypeError);
     assertEq(staticMethodCalled, true);
 
-    assertEq(Object.getOwnPropertyDescriptor(new a(), \"staticGetter\"), undefined);
-    var aStaticGetDesc = Object.getOwnPropertyDescriptor(a, \"staticGetter\");
+    assertEq(Object.getOwnPropertyDescriptor(new a(), "staticGetter"), undefined);
+    var aStaticGetDesc = Object.getOwnPropertyDescriptor(a, "staticGetter");
     assertEq(aStaticGetDesc.configurable, true);
     assertEq(aStaticGetDesc.enumerable, false);
     aStaticGetDesc.get();
     assertThrowsInstanceOf(() => new aStaticGetDesc.get, TypeError);
     assertEq(staticGetterCalled, true);
 
-    assertEq(Object.getOwnPropertyDescriptor(new a(), \"staticSetter\"), undefined);
-    var aStaticSetDesc = Object.getOwnPropertyDescriptor(a, \"staticSetter\");
+    assertEq(Object.getOwnPropertyDescriptor(new a(), "staticSetter"), undefined);
+    var aStaticSetDesc = Object.getOwnPropertyDescriptor(a, "staticSetter");
     assertEq(aStaticSetDesc.configurable, true);
     assertEq(aStaticSetDesc.enumerable, false);
     aStaticSetDesc.set();
     assertThrowsInstanceOf(() => new aStaticSetDesc.set, TypeError);
     assertEq(staticSetterCalled, true);
 
     assertEq([...new a()].join(), "cow,pig");
 }
-`;
-
-if (classesEnabled())
-    eval(test);
 
 if (typeof reportCompare === "function")
     reportCompare(0, 0, "OK");
--- a/js/src/tests/ecma_6/Class/methodOverwrites.js
+++ b/js/src/tests/ecma_6/Class/methodOverwrites.js
@@ -1,23 +1,20 @@
 // Ensure that we can overwrite methods when more tha one is present.
-
-var test = `
-
 {
     var result = 0;
     // Regardless of order, the constructor is overridden by any CPN, because it's
     // processed seperately.
-    class a { [\"constructor\"]() { result += 1; }; constructor() { result += 2; } }
+    class a { ["constructor"]() { result += 1; }; constructor() { result += 2; } }
     var aInst = new a();
     assertEq(result, 2);
     aInst.constructor();
     assertEq(result, 3);
 
-    class b { constructor() { result += 2; } [\"constructor\"]() { result += 1; }; }
+    class b { constructor() { result += 2; } ["constructor"]() { result += 1; }; }
     var bInst = new b();
     assertEq(result, 5);
     bInst.constructor();
     assertEq(result, 6);
 
     class c { constructor() { } method() { result += 1 } get method() { result += 2 } }
     new c().method;
     assertEq(result, 8);
@@ -40,23 +37,23 @@ var test = `
     assertEq(result, 12);
 }
 
 // Try again with expressions.
 {
     var result = 0;
     // Regardless of order, the constructor is overridden by any CPN, because it's
     // processed seperately.
-    let a = class { [\"constructor\"]() { result += 1; }; constructor() { result += 2; } };
+    let a = class { ["constructor"]() { result += 1; }; constructor() { result += 2; } };
     var aInst = new a();
     assertEq(result, 2);
     aInst.constructor();
     assertEq(result, 3);
 
-    let b = class { constructor() { result += 2; } [\"constructor\"]() { result += 1; }; };
+    let b = class { constructor() { result += 2; } ["constructor"]() { result += 1; }; };
     var bInst = new b();
     assertEq(result, 5);
     bInst.constructor();
     assertEq(result, 6);
 
     let c = class { constructor() { } method() { result += 1 } get method() { result += 2 } };
     new c().method;
     assertEq(result, 8);
@@ -73,15 +70,11 @@ var test = `
     let f = class { constructor() { }
                     set method(x) { throw "NO"; }
                     method() { throw "NO" }
                     get method() { return new Function("result += 1"); }
                   };
     new f().method();
     assertEq(result, 12);
 }
-`;
-
-if (classesEnabled())
-    eval(test);
 
 if (typeof reportCompare === "function")
     reportCompare(0, 0, "OK");
--- a/js/src/tests/ecma_6/Class/methodsPrototype.js
+++ b/js/src/tests/ecma_6/Class/methodsPrototype.js
@@ -1,9 +1,8 @@
-var test = `
 class TestClass {
     constructor() { }
     method() { }
     get getter() { }
     set setter(x) { }
     *generator() { }
     static staticMethod() { }
     static get staticGetter() { }
@@ -31,15 +30,10 @@ var hasNoPrototype = [
     Object.getOwnPropertyDescriptor(TestClass, 'staticGetter').get,
     Object.getOwnPropertyDescriptor(TestClass, 'staticSetter').set,
 ]
 
 for (var fun of hasNoPrototype) {
     assertEq(fun.hasOwnProperty('prototype'), false);
 }
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === "function")
     reportCompare(0, 0, "OK");
--- a/js/src/tests/ecma_6/Class/newTargetMethods.js
+++ b/js/src/tests/ecma_6/Class/newTargetMethods.js
@@ -1,10 +1,8 @@
-var test = `
-
 // Just like newTargetDirectInvoke, except to prove it works in functions
 // defined with method syntax as well. Note that methods, getters, and setters
 // are not constructible.
 
 let ol = {
     olTest(arg) { assertEq(arg, 4); assertEq(new.target, undefined); },
     get ol() { assertEq(new.target, undefined); },
     set ol(arg) { assertEq(arg, 4); assertEq(new.target, undefined); }
@@ -43,15 +41,11 @@ for (let i = 0; i < TEST_ITERATIONS; i++
 let clInst = new cl();
 
 for (let i = 0; i < TEST_ITERATIONS; i++)
     clInst.clTest(4);
 for (let i = 0; i < TEST_ITERATIONS; i++)
     clInst.cl;
 for (let i = 0; i < TEST_ITERATIONS; i++)
     clInst.cl = 4;
-`;
-
-if (classesEnabled())
-    eval(test);
 
 if (typeof reportCompare === "function")
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/outerBinding.js
+++ b/js/src/tests/ecma_6/Class/outerBinding.js
@@ -1,24 +1,23 @@
 // |reftest| skip-if(!xulRuntime.shell)
 //
 // The above skip-if is because global lexicals aren't yet implemented. Remove
 // that and the |evaluate| call below once they are.
 //
 // A class statement creates a mutable lexical outer binding.
 
-var test = `
 class Foo { constructor() { } }
-assertEq(typeof Foo, \"function\");
+assertEq(typeof Foo, "function");
 Foo = 5;
 assertEq(Foo, 5);
 
 {
     class foo { constructor() { } }
-    assertEq(typeof foo, \"function\");
+    assertEq(typeof foo, "function");
     foo = 4;
     assertEq(foo, 4);
 }
 
 {
     class PermanentBinding { constructor() { } }
     delete PermanentBinding;
     // That...didn't actually work, right?
@@ -30,21 +29,17 @@ evaluate("const globalConstant = 0; var 
 try {
     evaluate("earlyError = false; class globalConstant { constructor() { } }");
 } catch (e if e instanceof TypeError) { }
 assertEq(earlyError, true);
 
 function strictEvalShadows() {
     "use strict";
     let x = 4;
-    eval(\`class x { constructor() { } }
+    eval(`class x { constructor() { } }
            assertEq(typeof x, "function");
-         \`);
+         `);
     assertEq(x, 4);
 }
 strictEvalShadows()
-`;
-
-if (classesEnabled())
-    eval(test);
 
 if (typeof reportCompare === "function")
     reportCompare(0, 0, "OK");
--- a/js/src/tests/ecma_6/Class/staticConstructor.js
+++ b/js/src/tests/ecma_6/Class/staticConstructor.js
@@ -1,27 +1,22 @@
-var test = `
-class test {
+class testBasic {
     constructor() { }
     static constructor() { }
 }
 
-class testWithExtends {
+class testWithExtends extends null {
     constructor() { };
     static constructor() { };
 }
 
 class testOrder {
     static constructor() { };
     constructor() { };
 }
 
 class testOrderWithExtends extends null {
     static constructor() { };
     constructor() { };
 }
-`;
-
-if (classesEnabled())
-    eval(test);
 
 if (typeof reportCompare === "function")
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/staticMethods.js
+++ b/js/src/tests/ecma_6/Class/staticMethods.js
@@ -1,22 +1,15 @@
-var test = `
-
 // basic static method test
 class X {
     static count() { return ++this.hits; }
     constructor() { }
 }
 X.hits = 0;
 assertEq(X.count(), 1);
 
 // A static method is just a function.
 assertEq(X.count instanceof Function, true);
 assertEq(X.count.length, 0);
 assertEq(X.count.bind({hits: 77})(), 78);
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === "function")
     reportCompare(0, 0, "OK");
--- a/js/src/tests/ecma_6/Class/strictExecution.js
+++ b/js/src/tests/ecma_6/Class/strictExecution.js
@@ -1,12 +1,11 @@
 // Classes are always strict mode. Check computed property names and heritage
 // expressions as well.
 
-var test = `
 class a { constructor() { Object.preventExtensions({}).prop = 0; } }
 assertThrowsInstanceOf(() => new a(), TypeError);
 var aExpr = class { constructor() { Object.preventExtensions().prop = 0; } };
 assertThrowsInstanceOf(() => new aExpr(), TypeError);
 
 function shouldThrowCPN() {
     class b {
         [Object.preventExtensions({}).prop = 4]() { }
@@ -29,15 +28,11 @@ function shouldThrowHeritage() {
 }
 function shouldThrowHeritageExpr() {
     var b = class extends (Object.preventExtensions({}).prop = 4) {
         constructor() { }
     };
 }
 assertThrowsInstanceOf(shouldThrowHeritage, TypeError);
 assertThrowsInstanceOf(shouldThrowHeritageExpr, TypeError);
-`;
-
-if (classesEnabled())
-    eval(test);
 
 if (typeof reportCompare === "function")
     reportCompare(0, 0, "OK");
--- a/js/src/tests/ecma_6/Class/subclassedArrayUnboxed.js
+++ b/js/src/tests/ecma_6/Class/subclassedArrayUnboxed.js
@@ -1,10 +1,8 @@
-var test = `
-
 class foo extends Array { }
 
 function testArrs(arrs) {
     for (let arr of arrs) {
         assertEq(Object.getPrototypeOf(arr), foo.prototype);
     }
 }
 
@@ -15,15 +13,10 @@ for (var i = 0; i < 25; i++)
 testArrs(arrs);
 
 arrs[0].nonIndexedProp = "uhoh";
 
 arrs.push(new foo(1));
 
 testArrs(arrs);
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superCallBadDynamicSuperClass.js
+++ b/js/src/tests/ecma_6/Class/superCallBadDynamicSuperClass.js
@@ -1,19 +1,12 @@
-var test = `
-
 class base { constructor() { } }
 
 class inst extends base { constructor() { super(); } }
 Object.setPrototypeOf(inst, Math.sin);
 assertThrowsInstanceOf(() => new inst(), TypeError);
 
 class defaultInst extends base { }
 Object.setPrototypeOf(inst, Math.sin);
 assertThrowsInstanceOf(() => new inst(), TypeError);
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superCallBadNewTargetPrototype.js
+++ b/js/src/tests/ecma_6/Class/superCallBadNewTargetPrototype.js
@@ -1,10 +1,8 @@
-var test = `
-
 class base { constructor() { } }
 
 // lies and the lying liars who tell them
 function lies() { }
 lies.prototype = 4;
 
 assertThrowsInstanceOf(()=>Reflect.consruct(base, [], lies), TypeError);
 
@@ -18,15 +16,10 @@ function get(target, property, receiver)
 class inst extends base {
     constructor() { super(); }
 }
 assertThrowsInstanceOf(()=>new new Proxy(inst, {get})(), TypeError);
 
 class defaultInst extends base {}
 assertThrowsInstanceOf(()=>new new Proxy(defaultInst, {get})(), TypeError);
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superCallBaseInvoked.js
+++ b/js/src/tests/ecma_6/Class/superCallBaseInvoked.js
@@ -1,10 +1,8 @@
-var test = `
-
 function testBase(base) {
     class instance extends base {
         constructor(inst, one) {
             super(inst, one);
         }
     }
 
     let inst = new instance(instance, 1);
@@ -48,15 +46,10 @@ testBase(baseFunc);
 
 let handler = {};
 let p = new Proxy(baseFunc, handler);
 testBase(p);
 
 handler.construct = (target, args, nt) => Reflect.construct(target, args, nt);
 testBase(p);
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superCallInvalidBase.js
+++ b/js/src/tests/ecma_6/Class/superCallInvalidBase.js
@@ -1,17 +1,9 @@
-var test = `
-
 class instance extends null {
     constructor() { super(); }
 }
 
 assertThrowsInstanceOf(() => new instance(), TypeError);
 assertThrowsInstanceOf(() => new class extends null { }(), TypeError);
 
-
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superCallOrder.js
+++ b/js/src/tests/ecma_6/Class/superCallOrder.js
@@ -1,10 +1,8 @@
-var test = `
-
 function base() { }
 
 class beforeSwizzle extends base {
     constructor() {
         super(Object.setPrototypeOf(beforeSwizzle, null));
     }
 }
 
@@ -19,15 +17,10 @@ class beforeThrow extends base {
     }
 }
 
 Object.setPrototypeOf(beforeThrow, Math.sin);
 
 // Will throw that Math.sin is not a constructor before evaluating the args
 assertThrowsInstanceOf(() => new beforeThrow(), TypeError);
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superCallProperBase.js
+++ b/js/src/tests/ecma_6/Class/superCallProperBase.js
@@ -1,10 +1,8 @@
-var test = `
-
 class base1 {
     constructor() {
         this.base = 1;
     }
 }
 
 class base2 {
     constructor() {
@@ -27,15 +25,10 @@ assertEq(new inst().base, 2);
 // Still works with default constructor
 
 class defaultInst extends base1 { }
 
 assertEq(new defaultInst().base, 1);
 Object.setPrototypeOf(defaultInst, base2);
 assertEq(new defaultInst().base, 2);
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superCallSpreadCall.js
+++ b/js/src/tests/ecma_6/Class/superCallSpreadCall.js
@@ -1,34 +1,27 @@
-var test = `
-
 class base {
     constructor(a, b, c) {
         assertEq(a, 1);
         assertEq(b, 2);
         assertEq(c, 3);
         this.calledBase = true;
     }
 }
 
-class test extends base {
+class doTest extends base {
     constructor(arr) {
         super(...arr);
     }
 }
 
-assertEq(new test([1,2,3]).calledBase, true);
+assertEq(new doTest([1,2,3]).calledBase, true);
 
 class testRest extends base {
    constructor(...args) {
        super(...args);
    }
 }
 
 assertEq(new testRest(1,2,3).calledBase, true);
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superCallThisInit.js
+++ b/js/src/tests/ecma_6/Class/superCallThisInit.js
@@ -1,10 +1,8 @@
-var test = `
-
 function base() { this.prop = 42; }
 
 class testInitialize extends base {
     constructor() {
         // A poor man's assertThrowsInstanceOf, as arrow functions are currently
         // disabled in this context
         try {
             this;
@@ -38,15 +36,10 @@ assertThrowsInstanceOf(()=>new willStill
 class canCatchThrow extends base {
     constructor() {
         super();
         try { super(); } catch(e) { }
     }
 }
 assertEq(new canCatchThrow().prop, 42);
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropBasicCalls.js
+++ b/js/src/tests/ecma_6/Class/superPropBasicCalls.js
@@ -1,10 +1,8 @@
-var test = `
-
 // Super property (and calls) works in non-extending classes and object
 // litterals.
 class toStringTest {
     constructor() {
         // Install a property to make it plausible that it's the same this
         this.foo = "rhinoceros";
     }
 
@@ -20,15 +18,10 @@ let toStrOL = {
     test() {
         assertEq(super.toSource(), super["toSource"]());
         assertEq(super.toSource(), this.toSource());
     }
 }
 
 toStrOL.test();
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropBasicChain.js
+++ b/js/src/tests/ecma_6/Class/superPropBasicChain.js
@@ -1,18 +1,11 @@
-var test = `
-
 var o = {
     access() {
         super.foo.bar;
     }
 };
 
 // Delazify
 assertThrowsInstanceOf(o.access, TypeError);
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropBasicGetter.js
+++ b/js/src/tests/ecma_6/Class/superPropBasicGetter.js
@@ -1,10 +1,8 @@
-var test = `
-
 class base {
     constructor() {}
 
     getValue() {
         return this._prop;
     }
 
     setValue(v) {
@@ -29,15 +27,10 @@ class derived extends base {
         this.b = 30;
         assertEq(this.b, 30);
     }
 }
 
 var derivedInstance = new derived();
 derivedInstance.test();
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropBasicNew.js
+++ b/js/src/tests/ecma_6/Class/superPropBasicNew.js
@@ -1,24 +1,17 @@
-var test = `
-
 class Base {
     constructor() {}
 }
 class Mid extends Base {
     constructor() { super(); }
     f() { return new super.constructor(); }
 }
 class Derived extends Mid {
     constructor() { super(); }
 }
 
 let d = new Derived();
 var df = d.f();
 assertEq(df.constructor, Base);
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropChains.js
+++ b/js/src/tests/ecma_6/Class/superPropChains.js
@@ -1,10 +1,8 @@
-var test = `
-
 // First, let's test the trivial. A chain of three works.
 class base {
     constructor() { }
     testChain() {
         this.baseCalled = true;
     }
 }
 
@@ -51,15 +49,10 @@ for (let i = 0; i < CHAIN_LENGTH; i++)
 
 // Now we poke the chain
 let inst = new chain();
 inst.testChain();
 assertEq(inst.baseCalled, true);
 
 assertEq(inst.x, "yeehaw");
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropDVG.js
+++ b/js/src/tests/ecma_6/Class/superPropDVG.js
@@ -1,25 +1,17 @@
 // Super property accesses should play nice with the pretty printer.
-
-var test = `
-
 class testNonExistent {
     constructor() {
         super["prop"]();
     }
 }
 // Should fold to super.prop
 assertThrownErrorContains(() => new testNonExistent(), 'super.prop');
 
 var ol = { testNonExistent() { super.prop(); } };
 assertThrownErrorContains(() => ol.testNonExistent(), "super.prop");
 
 var olElem = { testNonExistent() { var prop = "prop"; super[prop](); } };
 assertThrownErrorContains(() => olElem.testNonExistent(), "super[prop]");
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropDelete.js
+++ b/js/src/tests/ecma_6/Class/superPropDelete.js
@@ -1,12 +1,10 @@
-var test = `
-
+// Make sure we get the proper side effects.
 // |delete super.prop| and |delete super[expr]| throw universally.
-// Even so, we should make sure we get proper side effects
 
 class base {
     constructor() { }
 }
 
 class derived extends base {
     constructor() { super(); }
     testDeleteProp() { delete super.prop; }
@@ -39,15 +37,10 @@ assertEq(Object.prototype.toString, save
 var thing2 = {
     go() { delete super.prop; }
 };
 Object.setPrototypeOf(thing2, new Proxy({}, {
     deleteProperty(x) { throw "FAIL"; }
 }));
 assertThrowsInstanceOf(() => thing2.go(), ReferenceError);
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropDerivedCalls.js
+++ b/js/src/tests/ecma_6/Class/superPropDerivedCalls.js
@@ -1,10 +1,8 @@
-var test = `
-
 let derivedInstance;
 
 class base {
     constructor() { }
     method(a, b, c) {
         assertEq(this, derivedInstance);
         this.methodCalled = true;
         assertEq(a, 1);
@@ -70,15 +68,10 @@ class derived extends base {
 
 }
 
 derivedInstance = new derived();
 derivedInstance.test();
 derivedInstance.testInEval();
 derivedInstance.testInArrow();
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropDestructuring.js
+++ b/js/src/tests/ecma_6/Class/superPropDestructuring.js
@@ -1,10 +1,8 @@
-var test = `
-
 class base {
     constructor() { }
 }
 
 let seenValues;
 Object.defineProperty(base.prototype, "minutes",
                       {
                         set(x) {
@@ -36,15 +34,10 @@ class derived extends base {
         this.testAsserts();
     }
 }
 
 let d = new derived();
 d.testProps();
 d.testElems();
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropEvalInsideArrow.js
+++ b/js/src/tests/ecma_6/Class/superPropEvalInsideArrow.js
@@ -1,17 +1,11 @@
-var test = `
-
 class foo {
     constructor() { }
 
     method() {
         return (() => eval('super.toString'));
     }
 }
 assertEq(new foo().method()(), Object.prototype.toString);
-`;
-
-if (classesEnabled())
-    eval(test);
 
 if (typeof reportCompare === "function")
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropFor.js
+++ b/js/src/tests/ecma_6/Class/superPropFor.js
@@ -1,12 +1,10 @@
-var test = `
-
 class testForIn {
-    constructor() { 
+    constructor() {
         let hits = 0;
         for (super.prop in { prop1: 1, prop2: 2 })
             hits++;
         assertEq(this.prop, "prop2");
         assertEq(hits, 2);
     }
 }
 
@@ -18,16 +16,10 @@ new testForIn();
         let hits = 0;
         for (super["prop"] of [1, 2])
             hits++;
         assertEq(this.prop, 2);
         assertEq(hits, 2);
     }
 }).testForOf();
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropHeavyweightArrow.js
+++ b/js/src/tests/ecma_6/Class/superPropHeavyweightArrow.js
@@ -1,18 +1,12 @@
-var test = `
-
 class foo {
     constructor() { }
 
     method() {
         return (() => (eval(''), super.toString));
     }
 }
 
 assertEq(new foo().method()(), Object.prototype.toString);
-`;
-
-if (classesEnabled())
-    eval(test);
 
 if (typeof reportCompare === "function")
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropHomeObject.js
+++ b/js/src/tests/ecma_6/Class/superPropHomeObject.js
@@ -1,10 +1,8 @@
-var test = `
-
 // This is super weird. A super property reference in the spec contains two
 // things. The first is the object to do the lookup on, the super base. This
 // should be unchanged, no matter what's going on: I can move the method to
 // another object. I can pull it out as its own function. I can put it on my
 // head and run around the front yard. No changes. The other half, the |this|
 // for invoked calls, is the this at the time of referencing the property, which
 // means it's gonna vary wildly as stuff gets moved around.
 
@@ -22,20 +20,20 @@ class derived extends base {
 
 let derivedInstance = new derived();
 derivedInstance.test(derivedInstance);
 derivedInstance.testCPN(derivedInstance);
 
 let obj = { test: derivedInstance.test };
 obj.test(obj);
 
-let test = derivedInstance.test;
+let testSolo = derivedInstance.test;
 // Hah! The engine is not prepared for non-object receivers, since this couldn't
 // happen before. Hope Waldo fixes this soon as he claims he will :)
-assertThrowsInstanceOf(() =>test(undefined), TypeError);
+assertThrowsInstanceOf(() =>testSolo(undefined), TypeError);
 
 let anotherObject = { };
 derivedInstance.test.call(anotherObject, anotherObject);
 
 let strThis = "this is not an object!";
 derivedInstance.test.call(strThis, strThis);
 
 // You can take the arrow function out of the super, ... or something like that
@@ -55,15 +53,10 @@ class base2 {
 let animals = [];
 for (let exprBase of [base1, base2])
     new class extends exprBase {
         constructor() { super(); }
         test() { animals.push(super["test"]()); }
     }().test();
 assertDeepEq(animals, ["llama", "alpaca"]);
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropIncDecElem.js
+++ b/js/src/tests/ecma_6/Class/superPropIncDecElem.js
@@ -1,10 +1,8 @@
-var test = `
-
 // #1
 function base() { }
 
 base.prototype = {
     test() {
         --super[1];
     }
 }
@@ -17,15 +15,10 @@ class test2 {
     test() {
         super[1]++;
     }
 }
 
 var d = new test2();
 d.test()
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropLazyInnerFunction.js
+++ b/js/src/tests/ecma_6/Class/superPropLazyInnerFunction.js
@@ -1,24 +1,19 @@
-var test = `
 testcase();
 function testcase() {
     var tokenCodes  = {
       get try() {
         try {
           super.actual();
         } catch (e) {}
       }
     };
     var arr = [
         'try',
     ];
     for (var i = 0; i < arr.length; i++) {
         if (tokenCodes[arr[i]] !== i) {};
     }
 }
-`;
-
-if (classesEnabled())
-    eval(test);
 
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropNoOverwriting.js
+++ b/js/src/tests/ecma_6/Class/superPropNoOverwriting.js
@@ -1,10 +1,8 @@
-var test = `
-
 class X {
     constructor() {
         Object.defineProperty(this, "prop1", {
             configurable: true,
             writable: false,
             value: 1
         });
 
@@ -51,15 +49,10 @@ assertThrowsInstanceOf(() => x.f2(), Typ
 assertEq(x.prop2, 15);
 
 assertThrowsInstanceOf(() => x.f3(), TypeError);
 assertEq(x.prop3, undefined);
 
 assertThrowsInstanceOf(() => x.f4(), TypeError);
 assertEq(x.prop4, 20);
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropOrdering.js
+++ b/js/src/tests/ecma_6/Class/superPropOrdering.js
@@ -1,10 +1,8 @@
-var test = `
-
 class base {
     constructor() { }
     method() { this.methodCalled++; }
 }
 
 class derived extends base {
     constructor() { super(); this.methodCalled = 0; }
 
@@ -86,15 +84,10 @@ assertEq(instance.prop, 5);
 reset();
 
 instance.testAssignElemPropValChange();
 
 instance.testAssignProp();
 
 instance.testCompoundAssignProp();
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropProtoChanges.js
+++ b/js/src/tests/ecma_6/Class/superPropProtoChanges.js
@@ -1,10 +1,8 @@
-var test = `
-
 class base {
     constructor() { }
     test() {
         return false;
     }
 }
 
 let standin = { test() { return true; } };
@@ -15,15 +13,10 @@ class derived extends base {
         assertEq(super.test(), false);
         Object.setPrototypeOf(derived.prototype, standin);
         assertEq(super["test"](), true);
     }
 }
 
 new derived().test();
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropProxies.js
+++ b/js/src/tests/ecma_6/Class/superPropProxies.js
@@ -1,10 +1,8 @@
-var test = `
-
 class base {
     constructor() { }
 }
 
 let midStaticHandler = { };
 
 // We shouldn't use the |this.called| strategy here, since we have proxies
 // snooping property accesses.
@@ -76,15 +74,10 @@ var wrappedSuper = g.eval("({ method() {
 assertEq(wrappedSuper.method(), true);
 
 // With a CCW on the proto chain?
 var wrappedBase = g.eval("({ method() { return this.__secretProp__; } })");
 var unwrappedDerived = { __secretProp__: 42, method() { return super.method(); } }
 Object.setPrototypeOf(unwrappedDerived, wrappedBase);
 assertEq(unwrappedDerived.method(), 42);
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropSkips.js
+++ b/js/src/tests/ecma_6/Class/superPropSkips.js
@@ -1,10 +1,8 @@
-var test = `
-
 // Ensure that super lookups and sets skip over properties on the |this| object.
 // That is, super lookups start with the superclass, not the current class.
 
 // The whole point: an empty superclass
 class base {
     constructor() { }
 }
 
@@ -38,15 +36,10 @@ class derived extends base {
 
 Object.defineProperty(derived.prototype, "nonWritableProp", { writable: false, value: "pony" });
 
 let instance = new derived();
 instance.testSkipGet();
 instance.testSkipDerivedOverrides();
 instance.testSkipSet();
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropStatics.js
+++ b/js/src/tests/ecma_6/Class/superPropStatics.js
@@ -1,10 +1,8 @@
-var test = `
-
 class base {
     constructor() { }
     static found() {
         this.foundCalled = true;
     }
     static get accessor() {
         assertEq(this, derived);
         return 45;
@@ -16,26 +14,21 @@ class derived extends base {
     constructor() { }
 
     static found() { throw "NO!"; }
     static get accessor() { throw "NO!"; }
 
     static test() {
         assertEq(super["notFound"], undefined);
         super.found();
-        
+
         // foundCalled is set on |derived| specifically.
         let calledDesc = Object.getOwnPropertyDescriptor(derived, "foundCalled");
         assertEq(calledDesc.value, true);
 
         assertEq(super.accessor, 45);
     }
 }
 
 derived.test();
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Class/superPropStrictAssign.js
+++ b/js/src/tests/ecma_6/Class/superPropStrictAssign.js
@@ -1,30 +1,23 @@
 // While |super| is common in classes, it also works in object litterals,
 // where there is no guarantee of strict mode. Check that we do not somehow
 // get strict mode semantics when they were not called for
 
-var test = `
-
 // |undefined|, writable: false
 Object.defineProperty(Object.prototype, "prop", { writable: false });
 
 class strictAssignmentTest {
-    constructor() { 
+    constructor() {
         // Strict mode. Throws.
         super.prop = 14;
     }
 }
 
 assertThrowsInstanceOf(()=>new strictAssignmentTest(), TypeError);
 
 // Non-strict. Silent failure.
 ({ test() { super.prop = 14; } }).test();
 
 assertEq(Object.prototype.prop, undefined);
 
-`;
-
-if (classesEnabled())
-    eval(test);
-
 if (typeof reportCompare === 'function')
     reportCompare(0,0,"OK");
--- a/js/src/tests/ecma_6/Expressions/ToPropertyKey-symbols.js
+++ b/js/src/tests/ecma_6/Expressions/ToPropertyKey-symbols.js
@@ -34,48 +34,44 @@ for (var sym of symbols) {
     obj[key]++;
     assertEq(obj[sym], 21);
 
     // Getting properties of primitive values.
     Number.prototype[sym] = "success";
     assertEq(Math.PI[key], "success");
     delete Number.prototype[sym];
 
-    if (classesEnabled()) {
-        eval(`
-            // Getting a super property.
-            class X {
-                [sym]() { return "X"; }
-            }
-            class Y extends X {
-                [sym]() { return super[key]() + "Y"; }
-            }
-            var y = new Y();
-            assertEq(y[sym](), "XY");
+    // Getting a super property.
+    class X {
+        [sym]() { return "X"; }
+    }
+    class Y extends X {
+        [sym]() { return super[key]() + "Y"; }
+    }
+    var y = new Y();
+    assertEq(y[sym](), "XY");
 
-            // Setting a super property.
-            class Z {
-                set [sym](v) {
-                    this.self = this;
-                    this.value = v;
-                }
-            }
-            class W extends Z {
-                set [sym](v) {
-                    this.isW = true;
-                    super[key] = v;
-                }
-            }
-            var w = new W();
-            w[key] = "ok";
-            assertEq(w.self, w);
-            assertEq(w.value, "ok");
-            assertEq(w.isW, true);
-        `);
+    // Setting a super property.
+    class Z {
+        set [sym](v) {
+            this.self = this;
+            this.value = v;
+        }
     }
+    class W extends Z {
+        set [sym](v) {
+            this.isW = true;
+            super[key] = v;
+        }
+    }
+    var w = new W();
+    w[key] = "ok";
+    assertEq(w.self, w);
+    assertEq(w.value, "ok");
+    assertEq(w.isW, true);
 
     // Deleting properties.
     obj = {[sym]: 1};
     assertEq(delete obj[key], true);
     assertEq(sym in obj, false);
 
     // LHS of `in` expressions.
     assertEq(key in {iterator: 0}, false);
--- a/js/src/tests/ecma_6/Reflect/apply.js
+++ b/js/src/tests/ecma_6/Reflect/apply.js
@@ -1,20 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/ */
 
 // Reflect.apply calls functions.
 assertEq(Reflect.apply(Math.floor, undefined, [1.75]), 1);
 
 // Reflect.apply requires a target object that's callable.
-var nonCallable = [{}, []];
-if (classesEnabled()) {
-    // classes are not callable
-    nonCallable.push(eval("(class clsX { constructor() {} })"));
-}
+var nonCallable = [{}, [], (class clsX { constructor() {} })];
 for (var value of nonCallable) {
     assertThrowsInstanceOf(() => Reflect.apply(nonCallable), TypeError);
 }
 
 // When target is not callable, Reflect.apply does not try to get argumentList.length before throwing.
 var hits = 0;
 var bogusArgumentList = {get length() { hit++; throw "FAIL";}};
 assertThrowsInstanceOf(() => Reflect.apply({callable: false}, null, bogusArgumentList),
--- a/js/src/tests/ecma_6/Reflect/construct.js
+++ b/js/src/tests/ecma_6/Reflect/construct.js
@@ -25,34 +25,30 @@ assertDeepEq(Reflect.construct(f, [3]), 
 f.prototype = Array.prototype;
 assertDeepEq(Reflect.construct(f, [3]), new f(3));
 
 // Bound functions:
 var bound = f.bind(null, "carrot");
 assertDeepEq(Reflect.construct(bound, []), new bound);
 
 // Classes:
-if (classesEnabled()) {
-    eval(`{
-        class Base {
-            constructor(...args) {
-                this.args = args;
-                this.newTarget = new.target;
-            }
-        }
-        class Derived extends Base {
-            constructor(...args) { super(...args); }
-        }
+class Base {
+    constructor(...args) {
+        this.args = args;
+        this.newTarget = new.target;
+    }
+}
+class Derived extends Base {
+    constructor(...args) { super(...args); }
+}
 
-        assertDeepEq(Reflect.construct(Base, []), new Base);
-        assertDeepEq(Reflect.construct(Derived, [7]), new Derived(7));
-        g = Derived.bind(null, "q");
-        assertDeepEq(Reflect.construct(g, [8, 9]), new g(8, 9));
-    }`);
-}
+assertDeepEq(Reflect.construct(Base, []), new Base);
+assertDeepEq(Reflect.construct(Derived, [7]), new Derived(7));
+g = Derived.bind(null, "q");
+assertDeepEq(Reflect.construct(g, [8, 9]), new g(8, 9));
 
 // Cross-compartment wrappers:
 var g = newGlobal();
 var local = {here: this};
 g.eval("function F(arg) { this.arg = arg }");
 assertDeepEq(Reflect.construct(g.F, [local]), new g.F(local));
 
 // If first argument to Reflect.construct isn't a constructor, it throws a
--- a/js/src/tests/ecma_6/Syntax/keyword-unescaped-requirement.js
+++ b/js/src/tests/ecma_6/Syntax/keyword-unescaped-requirement.js
@@ -10,38 +10,33 @@ var summary =
   "keywords, possibly contextual keywords)";
 
 print(BUGNUMBER + ": " + summary);
 
 /**************
  * BEGIN TEST *
  **************/
 
-function classSyntax(code)
-{
-  return classesEnabled() ? "(class { constructor() {} " + code + " });" : "@";
-}
-
 function memberVariants(code)
 {
-  return [classesEnabled() ? "(class { constructor() {} " + code + " });" : "@",
+  return ["(class { constructor() {} " + code + " });",
           "({ " + code + " })"];
 }
 
 var badScripts =
   [
-   classSyntax("st\\u0061tic m() { return 0; }"),
-   classSyntax("st\\u0061tic get foo() { return 0; }"),
-   classSyntax("st\\u0061tic set foo(v) {}"),
-   classSyntax("st\\u0061tic get ['hi']() { return 0; }"),
-   classSyntax("st\\u0061tic set ['hi'](v) {}"),
-   classSyntax("st\\u0061tic get 'hi'() { return 0; }"),
-   classSyntax("st\\u0061tic set 'hi'(v) {}"),
-   classSyntax("st\\u0061tic get 42() { return 0; }"),
-   classSyntax("st\\u0061tic set 42(v) {}"),
+   "class { st\\u0061tic m() { return 0; } }",
+   "class { st\\u0061tic get foo() { return 0; } }",
+   "class { st\\u0061tic set foo(v) {} }",
+   "class { st\\u0061tic get ['hi']() { return 0; } }",
+   "class { st\\u0061tic set ['hi'](v) {} }",
+   "class { st\\u0061tic get 'hi'() { return 0; } }",
+   "class { st\\u0061tic set 'hi'(v) {} }",
+   "class { st\\u0061tic get 42() { return 0; } }",
+   "class { st\\u0061tic set 42(v) {} }",
    ...memberVariants("\\u0067et foo() { return 0; }"),
    ...memberVariants("\\u0073et foo() {}"),
    ...memberVariants("g\\u0065t foo() { return 0; }"),
    ...memberVariants("s\\u0065t foo() {}"),
    ...memberVariants("g\\u0065t ['hi']() { return 0; }"),
    ...memberVariants("s\\u0065t ['hi']() {}"),
    ...memberVariants("g\\u0065t 'hi'() { return 0; }"),
    ...memberVariants("s\\u0065t 'hi'() {}"),
--- a/js/src/tests/ecma_6/shell.js
+++ b/js/src/tests/ecma_6/shell.js
@@ -210,19 +210,8 @@ if (typeof assertWarning === 'undefined'
         enableLastWarning();
         func();
         var warning = getLastWarning();
         assertEq(warning !== null, true);
         assertEq(warning.name, name);
         disableLastWarning();
     }
 }
-
-function classesEnabled(testCode = "class Foo { constructor() {} }") {
-    try {
-        new Function(testCode);
-        return true;
-    } catch (e) {
-        if (!(e instanceof SyntaxError))
-            throw e;
-        return false;
-    }
-}
--- a/js/src/tests/js1_8_5/reflect-parse/classes.js
+++ b/js/src/tests/js1_8_5/reflect-parse/classes.js
@@ -1,20 +1,10 @@
 // |reftest| skip-if(!xulRuntime.shell)
 // Classes
-function classesEnabled() {
-    try {
-        Reflect.parse("class foo { constructor() { } }");
-        return true;
-    } catch (e) {
-        assertEq(e instanceof SyntaxError, true);
-        return false;
-    }
-}
-
 function testClasses() {
     function methodFun(id, kind, generator, args, body = []) {
         assertEq(generator && kind === "method", generator);
         assertEq(typeof id === 'string' || id === null, true);
         let idN = typeof id === 'string' ? ident(id) : null;
         let methodName = kind !== "method" ? null : idN;
         return generator
                ? genFunExpr("es6", methodName, args.map(ident), blockStmt(body))
@@ -504,12 +494,9 @@ function testClasses() {
     assertClassError("class NAME { constructor() false }", SyntaxError);
     assertClassError("class NAME { constructor() {} a() ({}) }", SyntaxError);
     assertClassError("class NAME { constructor() {} a() void 0 }", SyntaxError);
     assertClassError("class NAME { constructor() {} a() 1 }", SyntaxError);
     assertClassError("class NAME { constructor() {} a() false }", SyntaxError);
 
 }
 
-if (classesEnabled())
-    runtest(testClasses);
-else if (typeof reportCompare === 'function')
-    reportCompare(true, true);
+runtest(testClasses);
--- a/js/xpconnect/src/XPCThrower.cpp
+++ b/js/xpconnect/src/XPCThrower.cpp
@@ -73,16 +73,17 @@ XPCThrower::Throw(nsresult rv, XPCCallCo
 
     if (CheckForPendingException(rv, ccx))
         return;
 
     if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format))
         format = "";
 
     sz = (char*) format;
+    NS_ENSURE_TRUE_VOID(sz);
 
     if (sz && sVerbose)
         Verbosify(ccx, &sz, false);
 
     dom::Throw(ccx, rv, nsDependentCString(sz));
 
     if (sz && sz != format)
         JS_smprintf_free(sz);
@@ -114,16 +115,17 @@ XPCThrower::ThrowBadResult(nsresult rv, 
 
     if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format) || !format)
         format = "";
 
     if (nsXPCException::NameAndFormatForNSResult(result, &name, nullptr) && name)
         sz = JS_smprintf("%s 0x%x (%s)", format, result, name);
     else
         sz = JS_smprintf("%s 0x%x", format, result);
+    NS_ENSURE_TRUE_VOID(sz);
 
     if (sz && sVerbose)
         Verbosify(ccx, &sz, true);
 
     dom::Throw(ccx, result, nsDependentCString(sz));
 
     if (sz)
         JS_smprintf_free(sz);
@@ -135,16 +137,17 @@ XPCThrower::ThrowBadParam(nsresult rv, u
 {
     char* sz;
     const char* format;
 
     if (!nsXPCException::NameAndFormatForNSResult(rv, nullptr, &format))
         format = "";
 
     sz = JS_smprintf("%s arg %d", format, paramNum);
+    NS_ENSURE_TRUE_VOID(sz);
 
     if (sz && sVerbose)
         Verbosify(ccx, &sz, true);
 
     dom::Throw(ccx, rv, nsDependentCString(sz));
 
     if (sz)
         JS_smprintf_free(sz);
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2175,22 +2175,16 @@ nsIFrame::BuildDisplayListForStackingCon
     // Don't clip nsDisplayOpacity items. We clip their descendants instead.
     // The clip we would set on an element with opacity would clip
     // all descendant content, but some should not be clipped.
     DisplayListClipState::AutoSaveRestore opacityClipState(aBuilder);
     opacityClipState.Clear();
     resultList.AppendNewToTop(
         new (aBuilder) nsDisplayOpacity(aBuilder, this, &resultList, opacityItemForEventsOnly));
   }
-  /* If we have sticky positioning, wrap it in a sticky position item.
-   */
-  if (useStickyPosition) {
-    resultList.AppendNewToTop(
-        new (aBuilder) nsDisplayStickyPosition(aBuilder, this, &resultList));
-  }
 
   /* If we're going to apply a transformation and don't have preserve-3d set, wrap
    * everything in an nsDisplayTransform. If there's nothing in the list, don't add
    * anything.
    *
    * For the preserve-3d case we want to individually wrap every child in the list with
    * a separate nsDisplayTransform instead. When the child is already an nsDisplayTransform,
    * we can skip this step, as the computed transform will already include our own.
@@ -2252,16 +2246,23 @@ nsIFrame::BuildDisplayListForStackingCon
       resultList.AppendNewToTop(
         new (aBuilder) nsDisplayPerspective(
           aBuilder, this,
           GetContainingBlock()->GetContent()->GetPrimaryFrame(), &resultList));
     }
 
   }
 
+  /* If we have sticky positioning, wrap it in a sticky position item.
+   */
+  if (useStickyPosition) {
+    resultList.AppendNewToTop(
+        new (aBuilder) nsDisplayStickyPosition(aBuilder, this, &resultList));
+  }
+
   /* If we're doing VR rendering, then we need to wrap everything in a nsDisplayVR
    */
   if (vrHMDInfo && !resultList.IsEmpty()) {
     resultList.AppendNewToTop(
       new (aBuilder) nsDisplayVR(aBuilder, this, &resultList, vrHMDInfo));
   }
 
   /* If adding both a nsDisplayBlendContainer and a nsDisplayMixBlendMode to the
new file mode 100644
--- /dev/null
+++ b/layout/reftests/async-scrolling/position-sticky-transformed-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML>
+<html>
+<body style="height:3000px; margin: 0px; overflow: hidden">
+  <div style="position:sticky; transform: rotateX(30deg) rotateY(10deg); background:blue; top:0; left:0; width:200px; height:100px"></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/async-scrolling/position-sticky-transformed.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html reftest-async-scroll
+      reftest-displayport-x="0" reftest-displayport-y="0"
+      reftest-displayport-w="800" reftest-displayport-h="2000"
+      reftest-async-scroll-x="0" reftest-async-scroll-y="50">
+<body style="height:3000px; margin: 0px; overflow: hidden">
+  <div style="position:sticky; transform: rotateX(30deg) rotateY(10deg); background:blue; top:0; left:0; width:200px; height:100px"></div>
+</body>
+</html>
--- a/layout/reftests/async-scrolling/reftest.list
+++ b/layout/reftests/async-scrolling/reftest.list
@@ -24,16 +24,17 @@ skip-if(!asyncPan) == split-layers-multi
 fails skip-if(!asyncPan) == split-opacity-layers-1.html split-opacity-layers-1-ref.html
 skip-if(!asyncPan) == sticky-pos-scrollable-1.html sticky-pos-scrollable-1-ref.html
 skip-if(!asyncPan) == fixed-pos-scrollable-1.html fixed-pos-scrollable-1-ref.html
 skip-if(!asyncPan) == culling-1.html culling-1-ref.html
 skip-if(!asyncPan) == position-fixed-iframe-1.html position-fixed-iframe-1-ref.html
 skip-if(!asyncPan) == position-fixed-iframe-2.html position-fixed-iframe-2-ref.html
 skip-if(!asyncPan) == position-fixed-in-scroll-container.html position-fixed-in-scroll-container-ref.html
 fuzzy(1,60000) skip-if(!asyncPan) == group-opacity-surface-size-1.html group-opacity-surface-size-1-ref.html
+skip-if(!asyncPan) == position-sticky-transformed.html position-sticky-transformed-ref.html
 
 # for the following tests, we want to disable the low-precision buffer
 # as it will expand the displayport beyond what the test specifies in
 # its reftest-displayport attributes, and interfere with where we expect
 # checkerboarding to occur
 default-preferences pref(layers.low-precision-buffer,false)
 skip-if(!asyncPan) == checkerboard-1.html checkerboard-1-ref.html
 skip-if(!asyncPan) == checkerboard-2.html checkerboard-2-ref.html
--- a/layout/tools/reftest/mach_commands.py
+++ b/layout/tools/reftest/mach_commands.py
@@ -154,17 +154,17 @@ class ReftestRunner(MozbuildObject):
 
         default_manifest = {
             "reftest": (self.topsrcdir, "layout", "reftests", "reftest.list"),
             "crashtest": (self.topsrcdir, "testing", "crashtest", "crashtests.list"),
             "jstestbrowser": (self.topobjdir, "dist", "test-stage", "jsreftest", "tests",
                               "jstests.list")
         }
 
-        kwargs["extraProfileFiles"] = [os.path.join(self.topobjdir, "dist", "plugins")]
+        kwargs["extraProfileFiles"].append(os.path.join(self.topobjdir, "dist", "plugins"))
         kwargs["symbolsPath"] = os.path.join(self.topobjdir, "crashreporter-symbols")
 
         if not kwargs["tests"]:
             kwargs["tests"] = [os.path.join(*default_manifest[kwargs["suite"]])]
 
         if kwargs["suite"] == "jstestbrowser":
             kwargs["extraProfileFiles"].append(os.path.join(self.topobjdir, "dist",
                                                             "test-stage", "jsreftest",
--- a/media/mtransport/test/ice_unittest.cpp
+++ b/media/mtransport/test/ice_unittest.cpp
@@ -90,16 +90,34 @@ enum TrickleMode { TRICKLE_NONE, TRICKLE
 const unsigned int ICE_TEST_PEER_OFFERER = (1 << 0);
 const unsigned int ICE_TEST_PEER_ALLOW_LOOPBACK = (1 << 1);
 const unsigned int ICE_TEST_PEER_ENABLED_TCP = (1 << 2);
 const unsigned int ICE_TEST_PEER_ALLOW_LINK_LOCAL = (1 << 3);
 const unsigned int ICE_TEST_PEER_HIDE_NON_DEFAULT = (1 << 4);
 
 typedef std::string (*CandidateFilter)(const std::string& candidate);
 
+std::vector<std::string> split(const std::string &s, char delim) {
+  std::vector<std::string> elems;
+  std::stringstream ss(s);
+  std::string item;
+  while (std::getline(ss, item, delim)) {
+    elems.push_back(item);
+  }
+  return elems;
+}
+
+static std::string IsSrflxCandidate(const std::string& candidate) {
+  std::vector<std::string> tokens = split(candidate, ' ');
+  if ((tokens.at(6) == "typ") && (tokens.at(7) == "srflx")) {
+    return candidate;
+  }
+  return std::string();
+}
+
 static std::string IsRelayCandidate(const std::string& candidate) {
   if (candidate.find("typ relay") != std::string::npos) {
     return candidate;
   }
   return std::string();
 }
 
 static std::string IsTcpCandidate(const std::string& candidate) {
@@ -118,16 +136,24 @@ static std::string IsTcpSoCandidate(cons
 
 static std::string IsLoopbackCandidate(const std::string& candidate) {
   if (candidate.find("127.0.0.") != std::string::npos) {
     return candidate;
   }
   return std::string();
 }
 
+static std::string IsIpv4Candidate(const std::string& candidate) {
+  std::vector<std::string> tokens = split(candidate, ' ');
+  if (tokens.at(4).find(":") == std::string::npos) {
+    return candidate;
+  }
+  return std::string();
+}
+
 static std::string SabotageHostCandidateAndDropReflexive(
     const std::string& candidate) {
   if (candidate.find("typ srflx") != std::string::npos) {
     return std::string();
   }
 
   if (candidate.find("typ host") != std::string::npos) {
     return kUnreachableHostIceCandidate;
@@ -443,17 +469,17 @@ class IceTestPeer : public sigslot::has_
   std::vector<std::string> GetGlobalAttributes() {
     std::vector<std::string> attrs(ice_ctx_->GetGlobalAttributes());
     if (simulate_ice_lite_) {
       attrs.push_back("ice-lite");
     }
     return attrs;
   }
 
-   std::vector<std::string> GetCandidates(size_t stream) {
+  std::vector<std::string> GetCandidates(size_t stream) {
     std::vector<std::string> v;
 
     RUN_ON_THREAD(
         test_utils->sts_target(),
         WrapRunnableRet(&v, this, &IceTestPeer::GetCandidates_s, stream));
 
     return v;
   }
@@ -495,16 +521,42 @@ class IceTestPeer : public sigslot::has_
     expected_local_transport_ = local_transport;
     expected_remote_type_ = remote;
   }
 
   void SetExpectedRemoteCandidateAddr(const std::string& addr) {
     expected_remote_addr_ = addr;
   }
 
+  int GetCandidatesPrivateIpv4Range(size_t stream) {
+    std::vector<std::string> candidates = GetCandidates(stream);
+
+    int host_net = 0;
+    for (auto c : candidates) {
+      if (c.find("typ host") != std::string::npos) {
+        nr_transport_addr addr;
+        std::vector<std::string> tokens = split(c, ' ');
+        int r = nr_str_port_to_transport_addr(tokens.at(4).c_str(), 0, IPPROTO_UDP, &addr);
+        MOZ_ASSERT(!r);
+        if (!r && (addr.ip_version == NR_IPV4)) {
+          int n = nr_transport_addr_get_private_addr_range(&addr);
+          if (n) {
+            if (host_net) {
+              // TODO: add support for multiple private interfaces
+              std::cerr << "This test doesn't support multiple private interfaces";
+              return -1;
+            }
+            host_net = n;
+          }
+        }
+      }
+    }
+    return host_net;
+  }
+
   bool gathering_complete() { return gathering_complete_; }
   int ready_ct() { return ready_ct_; }
   bool is_ready_s(size_t stream) {
     if (!streams_[stream]) {
       EXPECT_TRUE(false) << "No such stream " << stream;
       return false;
     }
     return streams_[stream]->state() == NrIceMediaStream::ICE_OPEN;
@@ -2461,16 +2513,50 @@ void DelayRelayCandidates(
     if ((*i)->IsRelay()) {
       (*i)->Schedule(ms);
     } else {
       (*i)->Schedule(0);
     }
   }
 }
 
+void AddNonPairableCandidates(
+    std::vector<SchedulableTrickleCandidate*>& candidates,
+    IceTestPeer *peer, size_t stream, int net_type) {
+  for (int i=1; i<5; i++) {
+    if (net_type == i)
+      continue;
+    switch (i) {
+      case 1:
+        candidates.push_back(new SchedulableTrickleCandidate(peer, stream,
+                   "candidate:0 1 UDP 2113601790 10.0.0.1 12345 typ host"));
+        break;
+      case 2:
+        candidates.push_back(new SchedulableTrickleCandidate(peer, stream,
+                   "candidate:0 1 UDP 2113601791 172.16.1.1 12345 typ host"));
+        break;
+      case 3:
+        candidates.push_back(new SchedulableTrickleCandidate(peer, stream,
+                   "candidate:0 1 UDP 2113601792 192.168.0.1 12345 typ host"));
+        break;
+      case 4:
+        candidates.push_back(new SchedulableTrickleCandidate(peer, stream,
+                   "candidate:0 1 UDP 2113601793 100.64.1.1 12345 typ host"));
+        break;
+      default:
+        UNIMPLEMENTED;
+    }
+  }
+
+  for (auto i = candidates.rbegin(); i != candidates.rend(); ++i) {
+    std::cerr << "Scheduling candidate: " << (*i)->Candidate().c_str() << std::endl;
+    (*i)->Schedule(0);
+  }
+}
+
 void DropTrickleCandidates(
     std::vector<SchedulableTrickleCandidate*>& candidates) {
 }
 
 TEST_F(IceConnectTest, TestConnectTrickleAddStreamDuringICE) {
   AddStream("first", 1);
   ASSERT_TRUE(Gather());
   ConnectTrickle();
@@ -2857,16 +2943,86 @@ TEST_F(IceConnectTest, TestPollCandPairs
 
   r = p2_->GetCandidatePairs(0, &pairs);
   ASSERT_EQ(NS_OK, r);
   ASSERT_NE(0U, pairs.size());
   ASSERT_TRUE(p2_->CandidatePairsPriorityDescending(pairs));
   ASSERT_TRUE(ContainsSucceededPair(pairs));
 }
 
+TEST_F(IceConnectTest, TestHostCandPairingFilter) {
+  AddStream("first", 1);
+  ASSERT_TRUE(Gather(kDefaultTimeout, false));
+  SetCandidateFilter(IsIpv4Candidate);
+
+  int host_net = p1_->GetCandidatesPrivateIpv4Range(0);
+  if (host_net <= 0) {
+    // TODO bug 1226838: make this work with multiple private IPs
+    FAIL() << "This test needs exactly one private IPv4 host candidate to work" << std::endl;
+  }
+
+  ConnectTrickle();
+  AddNonPairableCandidates(p1_->ControlTrickle(0), p1_, 0, host_net);
+  AddNonPairableCandidates(p2_->ControlTrickle(0), p2_, 0, host_net);
+
+  std::vector<NrIceCandidatePair> pairs;
+  p1_->GetCandidatePairs(0, &pairs);
+  for (auto p : pairs) {
+    std::cerr << "Verifying pair:" << std::endl;
+    p1_->DumpCandidatePair(p);
+    nr_transport_addr addr;
+    nr_str_port_to_transport_addr(p.local.local_addr.host.c_str(), 0, IPPROTO_UDP, &addr);
+    ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) == host_net);
+    nr_str_port_to_transport_addr(p.remote.cand_addr.host.c_str(), 0, IPPROTO_UDP, &addr);
+    ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) == host_net);
+  }
+}
+
+TEST_F(IceConnectTest, TestSrflxCandPairingFilter) {
+  if (g_stun_server_address.empty()) {
+    return;
+  }
+
+  AddStream("first", 1);
+  ASSERT_TRUE(Gather(kDefaultTimeout));
+  SetCandidateFilter(IsSrflxCandidate);
+
+  if (p1_->GetCandidatesPrivateIpv4Range(0) <= 0) {
+    // TODO bug 1226838: make this work with public IP addresses
+    std::cerr << "Don't run this test at IETF meetings!" << std::endl;
+    FAIL() << "This test needs one private IPv4 host candidate to work" << std::endl;
+  }
+
+  ConnectTrickle();
+  SimulateTrickleP1(0);
+  SimulateTrickleP2(0);
+
+  std::vector<NrIceCandidatePair> pairs;
+  p1_->GetCandidatePairs(0, &pairs);
+  for (auto p : pairs) {
+    std::cerr << "Verifying P1 pair:" << std::endl;
+    p1_->DumpCandidatePair(p);
+    nr_transport_addr addr;
+    nr_str_port_to_transport_addr(p.local.local_addr.host.c_str(), 0, IPPROTO_UDP, &addr);
+    ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) != 0);
+    nr_str_port_to_transport_addr(p.remote.cand_addr.host.c_str(), 0, IPPROTO_UDP, &addr);
+    ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) == 0);
+  }
+  p2_->GetCandidatePairs(0, &pairs);
+  for (auto p : pairs) {
+    std::cerr << "Verifying P2 pair:" << std::endl;
+    p2_->DumpCandidatePair(p);
+    nr_transport_addr addr;
+    nr_str_port_to_transport_addr(p.local.local_addr.host.c_str(), 0, IPPROTO_UDP, &addr);
+    ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) != 0);
+    nr_str_port_to_transport_addr(p.remote.cand_addr.host.c_str(), 0, IPPROTO_UDP, &addr);
+    ASSERT_TRUE(nr_transport_addr_get_private_addr_range(&addr) == 0);
+  }
+}
+
 TEST_F(IceConnectTest, TestPollCandPairsDuringConnect) {
   AddStream("first", 1);
   ASSERT_TRUE(Gather());
 
   p2_->Connect(p1_, TRICKLE_NONE, false);
   p1_->Connect(p2_, TRICKLE_NONE, false);
 
   std::vector<NrIceCandidatePair> pairs1;
--- a/media/mtransport/third_party/nICEr/src/ice/ice_component.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_component.c
@@ -943,16 +943,56 @@ int nr_ice_component_service_pre_answer_
         nr_ice_pre_answer_request_destroy(&r1);
       }
     }
 
     _status=0;
      return(_status);
   }
 
+int nr_ice_component_can_candidate_tcptype_pair(nr_socket_tcp_type left, nr_socket_tcp_type right)
+  {
+    if (left && !right)
+      return(0);
+    if (!left && right)
+      return(0);
+    if (left == TCP_TYPE_ACTIVE && right != TCP_TYPE_PASSIVE)
+      return(0);
+    if (left == TCP_TYPE_SO && right != TCP_TYPE_SO)
+      return(0);
+    if (left == TCP_TYPE_PASSIVE)
+      return(0);
+
+    return(1);
+  }
+
+/* local vs. remote matters here because we allow private -> public pairing,
+ * but discourage public -> private pairing. */
+int nr_ice_component_can_candidate_addr_pair(nr_transport_addr *local, nr_transport_addr *remote)
+  {
+    int remote_range;
+
+    if(local->ip_version != remote->ip_version)
+      return(0);
+    if(nr_transport_addr_is_link_local(local) !=
+       nr_transport_addr_is_link_local(remote))
+      return(0);
+    /* This prevents our ice_unittest (or broken clients) from pairing a
+     * loopback with a host candidate. */
+    if(nr_transport_addr_is_loopback(local) !=
+       nr_transport_addr_is_loopback(remote))
+      return(0);
+    remote_range = nr_transport_addr_get_private_addr_range(remote);
+    if(remote_range && (nr_transport_addr_get_private_addr_range(local) !=
+       remote_range))
+      return(0);
+
+    return(1);
+  }
+
 int nr_ice_component_pair_candidate(nr_ice_peer_ctx *pctx, nr_ice_component *pcomp, nr_ice_candidate *lcand, int pair_all_remote)
   {
     int r, _status;
     nr_ice_candidate *pcand;
     nr_ice_cand_pair *pair=0;
     char codeword[5];
 
     nr_ice_compute_codeword(lcand->label,strlen(lcand->label),codeword);
@@ -970,35 +1010,19 @@ int nr_ice_component_pair_candidate(nr_i
         break;
       default:
         assert(0);
         ABORT(R_INTERNAL);
         break;
     }
 
     TAILQ_FOREACH(pcand, &pcomp->candidates, entry_comp){
-      if (lcand->tcp_type && !pcand->tcp_type)
-        continue;
-      if (!lcand->tcp_type && pcand->tcp_type)
-        continue;
-      if (lcand->tcp_type == TCP_TYPE_ACTIVE && pcand->tcp_type != TCP_TYPE_PASSIVE)
-        continue;
-      if (lcand->tcp_type == TCP_TYPE_SO && pcand->tcp_type != TCP_TYPE_SO)
-        continue;
-      if (lcand->tcp_type == TCP_TYPE_PASSIVE)
+      if(!nr_ice_component_can_candidate_addr_pair(&lcand->addr, &pcand->addr))
         continue;
-      if(pcand->addr.ip_version != lcand->addr.ip_version)
-        continue;
-      /* This prevents our ice_unittest from pairing a loopback with a host
-       * candidate. */
-      if(nr_transport_addr_is_loopback(&lcand->addr) &&
-         !nr_transport_addr_is_loopback(&pcand->addr))
-        continue;
-      if(!nr_transport_addr_is_loopback(&lcand->addr) &&
-         nr_transport_addr_is_loopback(&pcand->addr))
+      if(!nr_ice_component_can_candidate_tcptype_pair(lcand->tcp_type, pcand->tcp_type))
         continue;
 
       /*
         Two modes, depending on |pair_all_remote|
 
         1. Pair remote candidates which have not been paired
            (used in initial pairing or in processing the other side's
            trickle candidates).
--- a/media/mtransport/third_party/nICEr/src/net/transport_addr.c
+++ b/media/mtransport/third_party/nICEr/src/net/transport_addr.c
@@ -390,21 +390,31 @@ int nr_transport_addr_is_loopback(nr_tra
         UNIMPLEMENTED;
     }
 
     return(0);
   }
 
 int nr_transport_addr_is_link_local(nr_transport_addr *addr)
   {
-    if(addr->ip_version == NR_IPV6){
-      UINT4* addrTop = (UINT4*)(addr->u.addr6.sin6_addr.s6_addr);
-      return ((*addrTop & htonl(0xFFC00000)) == htonl(0xFE800000));
-    } else {
-      assert(0);
+    switch(addr->ip_version){
+      case NR_IPV4:
+        /* RFC3927: 169.254/16 */
+        if ((ntohl(addr->u.addr4.sin_addr.s_addr) & 0xFFFF0000) == 0xA9FE0000)
+          return(1);
+        break;
+      case NR_IPV6:
+        {
+          UINT4* addrTop = (UINT4*)(addr->u.addr6.sin6_addr.s6_addr);
+          if ((*addrTop & htonl(0xFFC00000)) == htonl(0xFE800000))
+            return(2);
+        }
+        break;
+      default:
+        UNIMPLEMENTED;
     }
 
     return(0);
   }
 
 int nr_transport_addr_is_wildcard(nr_transport_addr *addr)
   {
     switch(addr->ip_version){
@@ -421,8 +431,42 @@ int nr_transport_addr_is_wildcard(nr_tra
           return(1);
         break;
       default:
         UNIMPLEMENTED;
     }
 
     return(0);
   }
+
+nr_transport_addr_mask nr_private_ipv4_addrs[] = {
+  /* RFC1918: 10/8 */
+  {0x0A000000, 0xFF000000},
+  /* RFC1918: 172.16/12 */
+  {0xAC100000, 0xFFF00000},
+  /* RFC1918: 192.168/16 */
+  {0xC0A80000, 0xFFFF0000},
+  /* RFC6598: 100.64/10 */
+  {0x64400000, 0xFFC00000}
+};
+
+int nr_transport_addr_get_private_addr_range(nr_transport_addr *addr)
+  {
+    switch(addr->ip_version){
+      case NR_IPV4:
+        {
+          UINT4 ip = ntohl(addr->u.addr4.sin_addr.s_addr);
+          for (int i=0; i<(sizeof(nr_private_ipv4_addrs)/sizeof(nr_transport_addr_mask)); i++) {
+            if ((ip & nr_private_ipv4_addrs[i].mask) == nr_private_ipv4_addrs[i].addr)
+              return i + 1;
+          }
+        }
+        break;
+      case NR_IPV6:
+        return(0);
+      default:
+        UNIMPLEMENTED;
+    }
+
+    return(0);
+  }
+
+
--- a/media/mtransport/third_party/nICEr/src/net/transport_addr.h
+++ b/media/mtransport/third_party/nICEr/src/net/transport_addr.h
@@ -66,16 +66,21 @@ typedef struct nr_transport_addr_ {
     struct sockaddr_in6 addr6;
   } u;
   char ifname[MAXIFNAME];
   /* A string version.
      56 = 5 ("IP6:[") + 39 (ipv6 address) + 2 ("]:") + 5 (port) + 4 (/UDP) + 1 (null) */
   char as_string[56];
 } nr_transport_addr;
 
+typedef struct nr_transport_addr_mask_ {
+  UINT4 addr;
+  UINT4 mask;
+} nr_transport_addr_mask;
+
 int nr_sockaddr_to_transport_addr(struct sockaddr *saddr, int protocol, int keep, nr_transport_addr *addr);
 
 // addresses, ports in local byte order
 int nr_ip4_port_to_transport_addr(UINT4 ip4, UINT2 port, int protocol, nr_transport_addr *addr);
 int nr_str_port_to_transport_addr(const char *str, UINT2 port, int protocol, nr_transport_addr *addr);
 int nr_ip6_port_to_transport_addr(struct in6_addr* addr6, UINT2 port, int protocol, nr_transport_addr *addr);
 
 int nr_transport_addr_get_addrstring(const nr_transport_addr *addr, char *str, int maxlen);
@@ -83,16 +88,17 @@ int nr_transport_addr_get_port(nr_transp
 int nr_transport_addr_cmp(nr_transport_addr *addr1,nr_transport_addr *addr2,int mode);
 #define NR_TRANSPORT_ADDR_CMP_MODE_VERSION   1
 #define NR_TRANSPORT_ADDR_CMP_MODE_PROTOCOL  2
 #define NR_TRANSPORT_ADDR_CMP_MODE_ADDR      3
 #define NR_TRANSPORT_ADDR_CMP_MODE_ALL       4
 
 int nr_transport_addr_is_wildcard(nr_transport_addr *addr);
 int nr_transport_addr_is_loopback(nr_transport_addr *addr);
+int nr_transport_addr_get_private_addr_range(nr_transport_addr *addr);
 int nr_transport_addr_is_link_local(nr_transport_addr *addr);
 int nr_transport_addr_copy(nr_transport_addr *to, nr_transport_addr *from);
 int nr_transport_addr_copy_keep_ifname(nr_transport_addr *to, nr_transport_addr *from);
 int nr_transport_addr_fmt_addr_string(nr_transport_addr *addr);
 int nr_transport_addr_fmt_ifname_addr_string(const nr_transport_addr *addr, char *buf, int len);
 int nr_transport_addr_set_port(nr_transport_addr *addr, int port);
 
 #endif
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1690,16 +1690,23 @@ pref("network.dns.get-ttl", true);
 
 // The grace period allows the DNS cache to use expired entries, while kicking off
 // a revalidation in the background.
 pref("network.dnsCacheExpirationGracePeriod", 60);
 
 // This preference can be used to turn off DNS prefetch.
 pref("network.dns.disablePrefetch", false);
 
+// This preference controls whether .onion hostnames are
+// rejected before being given to DNS. RFC 7686
+pref("network.dns.blockDotOnion", true);
+
+// These domains are treated as localhost equivalent
+pref("network.dns.localDomains", "");
+
 // Contols whether or not "localhost" should resolve when offline
 pref("network.dns.offline-localhost", true);
 
 // This preference controls whether or not URLs with UTF-8 characters are
 // escaped.  Set this preference to TRUE for strict RFC2396 conformance.
 pref("network.standard-url.escape-utf8", true);
 
 // This preference controls whether or not URLs are always encoded and sent as
@@ -1822,23 +1829,16 @@ pref("network.proxy.ssl",               
 pref("network.proxy.ssl_port",              0);
 pref("network.proxy.socks",                 "");
 pref("network.proxy.socks_port",            0);
 pref("network.proxy.socks_version",         5);
 pref("network.proxy.socks_remote_dns",      false);
 pref("network.proxy.proxy_over_tls",        true);
 pref("network.proxy.no_proxies_on",         "localhost, 127.0.0.1");
 pref("network.proxy.failover_timeout",      1800); // 30 minutes
-
-// If this value is 'true' and a PAC or all given proxies are inaccessible or
-// failing, Firefox will attempt to connect to the server DIRECTly. Meaning
-// without a proxy. If set 'false', Firefox will not try to access without
-// proxy.
-pref("network.proxy.use_direct_on_fail",    true);
-
 pref("network.online",                      true); //online/offline
 pref("network.cookie.cookieBehavior",       0); // 0-Accept, 1-dontAcceptForeign, 2-dontAcceptAny, 3-limitForeign
 #ifdef ANDROID
 pref("network.cookie.cookieBehavior",       0); // Keep the old default of accepting all cookies
 #endif
 pref("network.cookie.thirdparty.sessionOnly", false);
 pref("network.cookie.lifetimePolicy",       0); // 0-accept, 1-dontUse 2-acceptForSession, 3-acceptForNDays
 pref("network.cookie.alwaysAcceptSessionCookies", false);
--- a/netwerk/base/nsProtocolProxyService.cpp
+++ b/netwerk/base/nsProtocolProxyService.cpp
@@ -642,22 +642,16 @@ nsProtocolProxyService::PrefsChanged(nsI
         proxy_GetBoolPref(prefBranch, PROXY_PREF("socks_remote_dns"),
                           mSOCKSProxyRemoteDNS);
 
     if (!pref || !strcmp(pref, PROXY_PREF("proxy_over_tls"))) {
         proxy_GetBoolPref(prefBranch, PROXY_PREF("proxy_over_tls"),
                           mProxyOverTLS);
     }
 
-    // when proxies fail, try DIRECT
-    if (!pref || !strcmp(pref, PROXY_PREF("use_direct_on_fail"))) {
-        proxy_GetBoolPref(prefBranch, PROXY_PREF("use_direct_on_fail"),
-                          mFailoverToDirect);
-    }
-
     if (!pref || !strcmp(pref, PROXY_PREF("failover_timeout")))
         proxy_GetIntPref(prefBranch, PROXY_PREF("failover_timeout"),
                          mFailedProxyTimeout);
 
     if (!pref || !strcmp(pref, PROXY_PREF("no_proxies_on"))) {
         rv = prefBranch->GetCharPref(PROXY_PREF("no_proxies_on"),
                                      getter_Copies(tempString));
         if (NS_SUCCEEDED(rv))
@@ -1429,29 +1423,21 @@ nsProtocolProxyService::NewProxyInfoWith
 }
 
 NS_IMETHODIMP
 nsProtocolProxyService::GetFailoverForProxy(nsIProxyInfo  *aProxy,
                                             nsIURI        *aURI,
                                             nsresult       aStatus,
                                             nsIProxyInfo **aResult)
 {
-    if (mFailoverToDirect) {
-        if (mProxyConfig == PROXYCONFIG_DIRECT) {
-            return NS_ERROR_NOT_AVAILABLE;
-        }
-    } else {
-        // We only support failover when a PAC file is configured, either
-        // directly or via system settings
-        if (mProxyConfig != PROXYCONFIG_PAC &&
-            mProxyConfig != PROXYCONFIG_WPAD &&
-            mProxyConfig != PROXYCONFIG_SYSTEM) {
-            return NS_ERROR_NOT_AVAILABLE;
-        }
-    }
+    // We only support failover when a PAC file is configured, either
+    // directly or via system settings
+    if (mProxyConfig != PROXYCONFIG_PAC && mProxyConfig != PROXYCONFIG_WPAD &&
+        mProxyConfig != PROXYCONFIG_SYSTEM)
+        return NS_ERROR_NOT_AVAILABLE;
 
     // Verify that |aProxy| is one of our nsProxyInfo objects.
     nsCOMPtr<nsProxyInfo> pi = do_QueryInterface(aProxy);
     NS_ENSURE_ARG(pi);
     // OK, the QI checked out.  We can proceed.
 
     // Remember that this proxy is down.
     DisableProxy(pi);
@@ -2045,65 +2031,62 @@ nsProtocolProxyService::PruneProxyInfo(c
                 last = iter;
                 iter = iter->mNext;
             }
         }
         if (!head)
             return;
     }
 
-    // Now, scan to the next proxy not disabled. If all proxies are disabled,
-    // return blank list to enforce a DIRECT rule.
+    // Now, scan to see if all remaining proxies are disabled.  If so, then
+    // we'll just bail and return them all.  Otherwise, we'll go and prune the
+    // disabled ones.
 
     bool allDisabled = true;
+
     nsProxyInfo *iter;
-
-    if (!mFailoverToDirect) {
-        for (iter = head; iter; iter = iter->mNext) {
-            if (!IsProxyDisabled(iter)) {
-                allDisabled = false;
-                break;
-            }
+    for (iter = head; iter; iter = iter->mNext) {
+        if (!IsProxyDisabled(iter)) {
+            allDisabled = false;
+            break;
         }
     }
 
-    if (!mFailoverToDirect && allDisabled) {
+    if (allDisabled)
         LOG(("All proxies are disabled, so trying all again"));
-    } else {
+    else {
         // remove any disabled proxies.
         nsProxyInfo *last = nullptr;
         for (iter = head; iter; ) {
             if (IsProxyDisabled(iter)) {
                 // reject!
                 nsProxyInfo *reject = iter;
 
                 iter = iter->mNext;
-                if (last) {
+                if (last)
                     last->mNext = iter;
-                }
-            else
-                head = iter;
+                else
+                    head = iter;
 
                 reject->mNext = nullptr;
                 NS_RELEASE(reject);
                 continue;
             }
 
-            allDisabled = false;
+            // since we are about to use this proxy, make sure it is not on
+            // the disabled proxy list.  we'll add it back to that list if
+            // we have to (in GetFailoverForProxy).
+            //
+            // XXX(darin): It might be better to do this as a final pass.
+            //
             EnableProxy(iter);
 
             last = iter;
             iter = iter->mNext;
         }
     }
 
-    if (mFailoverToDirect && allDisabled) {
-        LOG(("All proxies are disabled, try a DIRECT rule!"));
-        *list = nullptr;
-        return;
-    }
-
     // if only DIRECT was specified then return no proxy info, and we're done.
     if (head && !head->mNext && head->mType == kProxyType_DIRECT)
         NS_RELEASE(head);
 
     *list = head;  // Transfer ownership
 }
--- a/netwerk/base/nsProtocolProxyService.h
+++ b/netwerk/base/nsProtocolProxyService.h
@@ -391,19 +391,16 @@ protected:
 
     RefPtr<nsPACMan>           mPACMan;  // non-null if we are using PAC
     nsCOMPtr<nsISystemProxySettings> mSystemProxySettings;
 
     PRTime                       mSessionStart;
     nsFailedProxyTable           mFailedProxies;
     int32_t                      mFailedProxyTimeout;
 
-    // 'true' means try DIRECT when the proxies fail
-    bool                         mFailoverToDirect;
-
 private:
     nsresult AsyncResolveInternal(nsIChannel *channel, uint32_t flags,
                                   nsIProtocolProxyCallback *callback,
                                   nsICancelable **result,
                                   bool isSyncOK);
 
 };
 
--- a/netwerk/dns/nsDNSService2.cpp
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -45,16 +45,17 @@ using namespace mozilla;
 using namespace mozilla::net;
 
 static const char kPrefDnsCacheEntries[]     = "network.dnsCacheEntries";
 static const char kPrefDnsCacheExpiration[]  = "network.dnsCacheExpiration";
 static const char kPrefDnsCacheGrace[]       = "network.dnsCacheExpirationGracePeriod";
 static const char kPrefIPv4OnlyDomains[]     = "network.dns.ipv4OnlyDomains";
 static const char kPrefDisableIPv6[]         = "network.dns.disableIPv6";
 static const char kPrefDisablePrefetch[]     = "network.dns.disablePrefetch";
+static const char kPrefBlockDotOnion[]       = "network.dns.blockDotOnion";
 static const char kPrefDnsLocalDomains[]     = "network.dns.localDomains";
 static const char kPrefDnsOfflineLocalhost[] = "network.dns.offline-localhost";
 static const char kPrefDnsNotifyResolution[] = "network.dns.notifyResolution";
 
 //-----------------------------------------------------------------------------
 
 class nsDNSRecord : public nsIDNSRecord
 {
@@ -538,16 +539,17 @@ nsDNSService::Init()
     NS_ENSURE_TRUE(!mResolver, NS_ERROR_ALREADY_INITIALIZED);
     // prefs
     uint32_t maxCacheEntries  = 400;
     uint32_t defaultCacheLifetime = 120; // seconds
     uint32_t defaultGracePeriod = 60; // seconds
     bool     disableIPv6      = false;
     bool     offlineLocalhost = true;
     bool     disablePrefetch  = false;
+    bool     blockDotOnion    = true;
     int      proxyType        = nsIProtocolProxyService::PROXYCONFIG_DIRECT;
     bool     notifyResolution = false;
 
     nsAdoptingCString ipv4OnlyDomains;
     nsAdoptingCString localDomains;
 
     // read prefs
     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
@@ -561,16 +563,17 @@ nsDNSService::Init()
             defaultGracePeriod = val;
 
         // ASSUMPTION: pref branch does not modify out params on failure
         prefs->GetBoolPref(kPrefDisableIPv6, &disableIPv6);
         prefs->GetCharPref(kPrefIPv4OnlyDomains, getter_Copies(ipv4OnlyDomains));
         prefs->GetCharPref(kPrefDnsLocalDomains, getter_Copies(localDomains));
         prefs->GetBoolPref(kPrefDnsOfflineLocalhost, &offlineLocalhost);
         prefs->GetBoolPref(kPrefDisablePrefetch, &disablePrefetch);
+        prefs->GetBoolPref(kPrefBlockDotOnion, &blockDotOnion);
 
         // If a manual proxy is in use, disable prefetch implicitly
         prefs->GetIntPref("network.proxy.type", &proxyType);
         prefs->GetBoolPref(kPrefDnsNotifyResolution, &notifyResolution);
     }
 
     if (mFirstTime) {
         mFirstTime = false;
@@ -580,16 +583,17 @@ nsDNSService::Init()
             prefs->AddObserver(kPrefDnsCacheEntries, this, false);
             prefs->AddObserver(kPrefDnsCacheExpiration, this, false);
             prefs->AddObserver(kPrefDnsCacheGrace, this, false);
             prefs->AddObserver(kPrefIPv4OnlyDomains, this, false);
             prefs->AddObserver(kPrefDnsLocalDomains, this, false);
             prefs->AddObserver(kPrefDisableIPv6, this, false);
             prefs->AddObserver(kPrefDnsOfflineLocalhost, this, false);
             prefs->AddObserver(kPrefDisablePrefetch, this, false);
+            prefs->AddObserver(kPrefBlockDotOnion, this, false);
             prefs->AddObserver(kPrefDnsNotifyResolution, this, false);
 
             // Monitor these to see if there is a change in proxy configuration
             // If a manual proxy is in use, disable prefetch implicitly
             prefs->AddObserver("network.proxy.type", this, false);
         }
 
         nsCOMPtr<nsIObserverService> observerService =
@@ -613,16 +617,17 @@ nsDNSService::Init()
     if (NS_SUCCEEDED(rv)) {
         // now, set all of our member variables while holding the lock
         MutexAutoLock lock(mLock);
         mResolver = res;
         mIDN = idn;
         mIPv4OnlyDomains = ipv4OnlyDomains; // exchanges buffer ownership
         mOfflineLocalhost = offlineLocalhost;
         mDisableIPv6 = disableIPv6;
+        mBlockDotOnion = blockDotOnion;
 
         // Disable prefetching either by explicit preference or if a manual proxy is configured 
         mDisablePrefetch = disablePrefetch || (proxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL);
 
         mLocalDomains.Clear();
         if (localDomains) {
             nsCCharSeparatedTokenizer tokenizer(localDomains, ',',
                                                 nsCCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
@@ -693,32 +698,42 @@ nsDNSService::GetPrefetchEnabled(bool *o
 NS_IMETHODIMP
 nsDNSService::SetPrefetchEnabled(bool inVal)
 {
     MutexAutoLock lock(mLock);
     mDisablePrefetch = !inVal;
     return NS_OK;
 }
 
-static inline bool PreprocessHostname(bool              aLocalDomain,
-                                      const nsACString &aInput,
-                                      nsIIDNService    *aIDN,
-                                      nsACString       &aACE)
+nsresult
+nsDNSService::PreprocessHostname(bool              aLocalDomain,
+                                 const nsACString &aInput,
+                                 nsIIDNService    *aIDN,
+                                 nsACString       &aACE)
 {
+    // Enforce RFC 7686
+    if (mBlockDotOnion &&
+        StringEndsWith(aInput, NS_LITERAL_CSTRING(".onion"))) {
+        return NS_ERROR_UNKNOWN_HOST;
+    }
+
     if (aLocalDomain) {
         aACE.AssignLiteral("localhost");
-        return true;
+        return NS_OK;
     }
 
     if (!aIDN || IsASCII(aInput)) {
         aACE = aInput;
-        return true;
+        return NS_OK;
     }
 
-    return IsUTF8(aInput) && NS_SUCCEEDED(aIDN->ConvertUTF8toACE(aInput, aACE));
+    if (!(IsUTF8(aInput) && NS_SUCCEEDED(aIDN->ConvertUTF8toACE(aInput, aACE)))) {
+        return NS_ERROR_FAILURE;
+    }
+    return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDNSService::AsyncResolve(const nsACString  &aHostname,
                            uint32_t           flags,
                            nsIDNSListener    *listener,
                            nsIEventTarget    *target_,
                            nsICancelable    **result)
@@ -755,18 +770,20 @@ nsDNSService::AsyncResolveExtended(const
     if (mNotifyResolution) {
         NS_DispatchToMainThread(new NotifyDNSResolution(aHostname));
     }
 
     if (!res)
         return NS_ERROR_OFFLINE;
 
     nsCString hostname;
-    if (!PreprocessHostname(localDomain, aHostname, idn, hostname))
-        return NS_ERROR_FAILURE;
+    nsresult rv = PreprocessHostname(localDomain, aHostname, idn, hostname);
+    if (NS_FAILED(rv)) {
+        return rv;
+    }
 
     if (mOffline &&
         (!mOfflineLocalhost || !hostname.LowerCaseEqualsASCII("localhost"))) {
         flags |= RESOLVE_OFFLINE;
     }
 
     // make sure JS callers get notification on the main thread
     nsCOMPtr<nsIXPConnectWrappedJS> wrappedListener = do_QueryInterface(listener);
@@ -786,19 +803,18 @@ nsDNSService::AsyncResolveExtended(const
         new nsDNSAsyncRequest(res, hostname, listener, flags, af,
                               aNetworkInterface);
     if (!req)
         return NS_ERROR_OUT_OF_MEMORY;
     NS_ADDREF(*result = req);
 
     // addref for resolver; will be released when OnLookupComplete is called.
     NS_ADDREF(req);
-    nsresult rv = res->ResolveHost(req->mHost.get(), flags, af,
-                                   req->mNetworkInterface.get(),
-                                   req);
+    rv = res->ResolveHost(req->mHost.get(), flags, af,
+                          req->mNetworkInterface.get(), req);
     if (NS_FAILED(rv)) {
         NS_RELEASE(req);
         NS_RELEASE(*result);
     }
     return rv;
 }
 
 NS_IMETHODIMP
@@ -832,18 +848,20 @@ nsDNSService::CancelAsyncResolveExtended
         res = mResolver;
         idn = mIDN;
         localDomain = mLocalDomains.GetEntry(aHostname);
     }
     if (!res)
         return NS_ERROR_OFFLINE;
 
     nsCString hostname;
-    if (!PreprocessHostname(localDomain, aHostname, idn, hostname))
-        return NS_ERROR_FAILURE;
+    nsresult rv = PreprocessHostname(localDomain, aHostname, idn, hostname);
+    if (NS_FAILED(rv)) {
+        return rv;
+    }
 
     uint16_t af = GetAFForLookup(hostname, aFlags);
 
     res->CancelAsyncRequest(hostname.get(), aFlags, af,
                             nsPromiseFlatCString(aNetworkInterface).get(), aListener,
                             aReason);
     return NS_OK;
 }
@@ -867,18 +885,20 @@ nsDNSService::Resolve(const nsACString &
 
     if (mNotifyResolution) {
         NS_DispatchToMainThread(new NotifyDNSResolution(aHostname));
     }
 
     NS_ENSURE_TRUE(res, NS_ERROR_OFFLINE);
 
     nsCString hostname;
-    if (!PreprocessHostname(localDomain, aHostname, idn, hostname))
-        return NS_ERROR_FAILURE;
+    nsresult rv = PreprocessHostname(localDomain, aHostname, idn, hostname);
+    if (NS_FAILED(rv)) {
+        return rv;
+    }
 
     if (mOffline &&
         (!mOfflineLocalhost || !hostname.LowerCaseEqualsASCII("localhost"))) {
         flags |= RESOLVE_OFFLINE;
     }
 
     //
     // sync resolve: since the host resolver only works asynchronously, we need
@@ -892,17 +912,17 @@ nsDNSService::Resolve(const nsACString &
     if (!mon)
         return NS_ERROR_OUT_OF_MEMORY;
 
     PR_EnterMonitor(mon);
     nsDNSSyncRequest syncReq(mon);
 
     uint16_t af = GetAFForLookup(hostname, flags);
 
-    nsresult rv = res->ResolveHost(hostname.get(), flags, af, "", &syncReq);
+    rv = res->ResolveHost(hostname.get(), flags, af, "", &syncReq);
     if (NS_SUCCEEDED(rv)) {
         // wait for result
         while (!syncReq.mDone)
             PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
 
         if (NS_FAILED(syncReq.mStatus))
             rv = syncReq.mStatus;
         else {
--- a/netwerk/dns/nsDNSService2.h
+++ b/netwerk/dns/nsDNSService2.h
@@ -38,28 +38,34 @@ public:
 
 private:
     ~nsDNSService();
 
     static nsDNSService* GetSingleton();
 
     uint16_t GetAFForLookup(const nsACString &host, uint32_t flags);
 
+    nsresult PreprocessHostname(bool              aLocalDomain,
+                                const nsACString &aInput,
+                                nsIIDNService    *aIDN,
+                                nsACString       &aACE);
+
     RefPtr<nsHostResolver>  mResolver;
     nsCOMPtr<nsIIDNService>   mIDN;
 
     // mLock protects access to mResolver and mIPv4OnlyDomains
     mozilla::Mutex            mLock;
 
     // mIPv4OnlyDomains is a comma-separated list of domains for which only
     // IPv4 DNS lookups are performed. This allows the user to disable IPv6 on
     // a per-domain basis and work around broken DNS servers. See bug 68796.
     nsAdoptingCString                         mIPv4OnlyDomains;
     bool                                      mDisableIPv6;
     bool                                      mDisablePrefetch;
+    bool                                      mBlockDotOnion;
     bool                                      mFirstTime;
     bool                                      mOffline;
     bool                                      mNotifyResolution;
     bool                                      mOfflineLocalhost;
     nsTHashtable<nsCStringHashKey>            mLocalDomains;
 };
 
 #endif //nsDNSService2_h__
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_dns_onion.js
@@ -0,0 +1,70 @@
+var dns = Cc["@mozilla.org/network/dns-service;1"].getService(Ci.nsIDNSService);
+var threadManager = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager);
+var mainThread = threadManager.currentThread;
+
+var onionPref;
+var localdomainPref;
+var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
+
+// check that we don't lookup .onion
+var listenerBlock = {
+  onLookupComplete: function(inRequest, inRecord, inStatus) {
+    do_check_false(Components.isSuccessCode(inStatus));
+    do_test_dontBlock();
+  },
+  QueryInterface: function(aIID) {
+    if (aIID.equals(Ci.nsIDNSListener) ||
+        aIID.equals(Ci.nsISupports)) {
+      return this;
+    }
+    throw Cr.NS_ERROR_NO_INTERFACE;
+  }
+};
+
+// check that we do lookup .onion (via pref)
+var listenerDontBlock = {
+  onLookupComplete: function(inRequest, inRecord, inStatus) {
+    var answer = inRecord.getNextAddrAsString();
+    do_check_true(answer == "127.0.0.1" || answer == "::1");
+    all_done();
+  },
+  QueryInterface: function(aIID) {
+    if (aIID.equals(Ci.nsIDNSListener) ||
+        aIID.equals(Ci.nsISupports)) {
+      return this;
+    }
+    throw Cr.NS_ERROR_NO_INTERFACE;
+  }
+};
+
+function do_test_dontBlock() {
+  prefs.setBoolPref("network.dns.blockDotOnion", false);
+  dns.asyncResolve("private.onion", 0, listenerDontBlock, mainThread);
+}
+
+function do_test_block() {
+  prefs.setBoolPref("network.dns.blockDotOnion", true);
+  try {
+    dns.asyncResolve("private.onion", 0, listenerBlock, mainThread);
+  } catch (e) {
+    // it is ok for this negative test to fail fast
+    do_check_true(true);
+    do_test_dontBlock();
+  }
+}
+
+function all_done() {
+  // reset locally modified prefs
+  prefs.setCharPref("network.dns.localDomains", localdomainPref);
+  prefs.setBoolPref("network.dns.blockDotOnion", onionPref);
+  do_test_finished();
+}
+
+function run_test() {
+  onionPref = prefs.getBoolPref("network.dns.blockDotOnion");
+  localdomainPref = prefs.getCharPref("network.dns.localDomains");
+  prefs.setCharPref("network.dns.localDomains", "private.onion");
+  do_test_block();
+  do_test_pending();
+}
+
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -182,16 +182,17 @@ skip-if = bits != 32
 [test_cookie_header.js]
 [test_cookiejars.js]
 [test_cookiejars_safebrowsing.js]
 [test_dns_cancel.js]
 [test_dns_per_interface.js]
 [test_data_protocol.js]
 [test_dns_service.js]
 [test_dns_offline.js]
+[test_dns_onion.js]
 [test_dns_localredirect.js]
 [test_dns_proxy_bypass.js]
 [test_duplicate_headers.js]
 [test_chunked_responses.js]
 [test_content_length_underrun.js]
 [test_event_sink.js]
 [test_extract_charset_from_content_type.js]
 [test_fallback_no-cache-entry_canceled.js]
--- a/python/mozboot/mozboot/osx.py
+++ b/python/mozboot/mozboot/osx.py
@@ -302,16 +302,17 @@ class OSXBootstrapper(BaseBootstrapper):
             # development headers which are missing from OS X (at least on
             # 10.8) and because the build system wants a version newer than
             # what Apple ships.
             ('python', 'python'),
             ('mercurial', 'mercurial'),
             ('git', 'git'),
             ('autoconf213', HOMEBREW_AUTOCONF213),
             ('gnu-tar', 'gnu-tar'),
+            ('watchman', 'watchman',)
         ]
         self._ensure_homebrew_packages(packages)
 
     def ensure_homebrew_browser_packages(self):
         packages = [
             ('yasm', 'yasm'),
         ]
         self._ensure_homebrew_packages(packages)
@@ -380,20 +381,23 @@ class OSXBootstrapper(BaseBootstrapper):
         installed = set(self.check_output([self.port, 'installed']).split())
 
         missing = [package for package in packages if package not in installed]
         if missing:
             print(PACKAGE_MANAGER_PACKAGES % ('MacPorts',))
             self.run_as_root([self.port, '-v', 'install'] + missing)
 
     def ensure_macports_system_packages(self):
-        packages = ['python27',
-                    'mercurial',
-                    'autoconf213',
-                    'gnutar']
+        packages = [
+            'python27',
+            'mercurial',
+            'autoconf213',
+            'gnutar',
+            'watchman',
+        ]
 
         self._ensure_macports_packages(packages)
         self.run_as_root([self.port, 'select', '--set', 'python', 'python27'])
 
     def ensure_macports_browser_packages(self):
         packages = ['yasm']
 
         self._ensure_macports_packages(packages)
--- a/testing/docker/tester-device/VERSION
+++ b/testing/docker/tester-device/VERSION
@@ -1,1 +1,1 @@
-0.0.7
+0.0.8
--- a/testing/docker/tester-device/bin/entrypoint
+++ b/testing/docker/tester-device/bin/entrypoint
@@ -3,17 +3,17 @@
 echo "Validating Task"
 python /home/worker/bin/validate_task.py
 
 echo "Retrieving device"
 res=`curl --request POST -H "Content-Type: application/json" -d "$DEVICE_CAPABILITIES" http://$CLOUD_HOST/device`
 error=`echo $res | jq .error`
 
 if [ "$error" != "null" ]; then
-    echo $error
+    echo "[taskcluster:error] $error"
     exit -1
 fi
 
 status=`echo $res | jq .session`
 
 if [ "$status" == "null" ]; then
     echo "Session could not be created with a device."
     exit -1
--- a/testing/taskcluster/tasks/branches/base_job_flags.yml
+++ b/testing/taskcluster/tasks/branches/base_job_flags.yml
@@ -91,16 +91,17 @@ flags:
     - dolphin
     - dolphin-eng
     - dolphin-512
     - dolphin-512-eng
     - aries
     - aries-ota
     - aries-eng
     - aries-dogfood
+    - aries-noril
     - android-api-11
     - android-partner-sample1
     - android-b2gdroid
     - linux
     - linux64
     - linux64-st-an
     - macosx64
     - macosx64-st-an
--- a/testing/taskcluster/tasks/branches/base_jobs.yml
+++ b/testing/taskcluster/tasks/branches/base_jobs.yml
@@ -69,16 +69,22 @@ builds:
       debug:
         task: tasks/builds/b2g_aries_spark_debug.yml
   aries-eng:
     platforms:
       - b2g
     types:
       opt:
         task: tasks/builds/b2g_aries_eng.yml
+  aries-noril:
+    platforms:
+      - b2g
+    types:
+      opt:
+        task: tasks/builds/b2g_aries_spark_noril_opt.yml
   flame-kk:
     platforms:
       - b2g
     types:
       opt:
         task: tasks/builds/b2g_flame_kk_opt.yml
       debug:
         task: tasks/builds/b2g_flame_kk_debug.yml
new file mode 100644
--- /dev/null
+++ b/testing/taskcluster/tasks/builds/b2g_aries_spark_noril_opt.yml
@@ -0,0 +1,43 @@
+$inherits:
+  from: 'tasks/builds/b2g_phone_base.yml'
+  variables:
+    build_name: 'aries-noril'
+    build_type: 'opt'
+task:
+  workerType: flame-kk
+  scopes:
+    - 'docker-worker:cache:build-aries-opt'
+    - 'docker-worker:cache:build-aries-opt-objdir-gecko-{{project}}'
+  metadata:
+    name: '[TC] B2G Aries No RIL Opt'
+
+  payload:
+    cache:
+      build-aries-opt: /home/worker/workspace
+      build-aries-opt-objdir-gecko-{{project}}: /home/worker/objdir-gecko
+    env:
+      TARGET: 'aries'
+      DEBUG: 0
+      VARIANT: user
+      GAIA_OPTIMIZE: '1'
+      B2G_SYSTEM_APPS: '1'
+      MOZHARNESS_CONFIG: b2g/taskcluster-spark.py
+      GECKO_CONFIGURE_ARGS: "--disable-b2g-ril"
+    command:
+      - >
+        checkout-gecko workspace &&
+        cd ./workspace/gecko/testing/taskcluster/scripts/phone-builder &&
+        buildbot_step 'Build' ./build-phone.sh $HOME/workspace
+  extra:
+    treeherderEnv:
+      - production
+      - staging
+    treeherder:
+      symbol: Bnr
+      groupSymbol: Aries
+      groupName: Aries Device Image
+      tier: 2
+      machine:
+        platform: b2g-device-image
+    locations:
+      img: 'private/build/aries.zip'
--- a/testing/web-platform/mozilla/meta/MANIFEST.json
+++ b/testing/web-platform/mozilla/meta/MANIFEST.json
@@ -177,16 +177,17 @@
           {
             "path": "service-workers/service-worker/fetch-event.https.html",
             "url": "/_mozilla/service-workers/service-worker/fetch-event.https.html"
           }
         ],
         "service-workers/service-worker/fetch-frame-resource.https.html": [
           {
             "path": "service-workers/service-worker/fetch-frame-resource.https.html",
+            "timeout": "long",
             "url": "/_mozilla/service-workers/service-worker/fetch-frame-resource.https.html"
           }
         ],
         "service-workers/service-worker/fetch-header-visibility.https.html": [
           {
             "path": "service-workers/service-worker/fetch-header-visibility.https.html",
             "url": "/_mozilla/service-workers/service-worker/fetch-header-visibility.https.html"
           }
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html.ini
@@ -0,0 +1,3 @@
+[clients-matchall-include-uncontrolled.https.html]
+  type: testharness
+  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1230042
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/unregister-then-register-new-script.https.html.ini
@@ -0,0 +1,4 @@
+[unregister-then-register-new-script.https.html]
+  type: testharness
+  [Registering a new script URL while an unregistered registration is in use]
+    disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1231974
deleted file mode 100644
--- a/testing/web-platform/mozilla/meta/service-workers/service-worker/update-after-navigation-fetch-event.https.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[update-after-navigation-fetch-event.https.html]
-  type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1226443
--- a/testing/web-platform/mozilla/meta/service-workers/service-worker/update-after-oneday.https.html.ini
+++ b/testing/web-platform/mozilla/meta/service-workers/service-worker/update-after-oneday.https.html.ini
@@ -1,4 +1,3 @@
 [update-after-oneday.https.html]
   type: testharness
   prefs: [dom.serviceWorkers.testUpdateOverOneDay: true]
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1226443
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-frame-resource.https.html
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/fetch-frame-resource.https.html
@@ -1,10 +1,11 @@
 <!DOCTYPE html>
 <title>Service Worker: Fetch for the frame loading.</title>
+<meta name=timeout content=long>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="resources/get-host-info.sub.js"></script>
 <script src="resources/test-helpers.sub.js"></script>
 <body>
 <script>
 var worker = 'resources/fetch-rewrite-worker.js';
 var path = base_path() + 'resources/fetch-access-control.py';
@@ -40,17 +41,17 @@ function getLoadedObject(win, contentFun
           // expected json result.
           var content = '';
           try {
             content = contentFunc(win);
           } catch(e) {
             // use default empty string for cross-domain window
           }
           done(content);
-        }, 5000);
+        }, 10000);
 
       win.onload = function() {
           clearTimeout(timeout);
           var content = contentFunc(win);
           done(content);
         };
     });
 }
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -44,16 +44,17 @@
 #include <sys/sysctl.h>
 #endif
 
 #include "prmem.h"
 #include "prnetdb.h"
 #include "prprf.h"
 #include "prproces.h"
 #include "prenv.h"
+#include "prtime.h"
 
 #include "nsIAppShellService.h"
 #include "nsIAppStartup.h"
 #include "nsIAppStartupNotifier.h"
 #include "nsIMutableArray.h"
 #include "nsICategoryManager.h"
 #include "nsIChromeRegistry.h"
 #include "nsICommandLineRunner.h"
@@ -1019,34 +1020,16 @@ nsXULAppInfo::GetAccessibilityEnabled(bo
   *aResult = GetAccService() != nullptr;
 #else
   *aResult = false;
 #endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsXULAppInfo::GetAccessibilityIsBlacklistedForE10S(bool* aResult)
-{
-  *aResult = false;
-#if defined(ACCESSIBILITY)
-#if defined(XP_WIN)
-  if (GetAccService() && mozilla::a11y::Compatibility::IsBlacklistedForE10S()) {
-    *aResult = true;
-  }
-#elif defined(XP_MACOSX)
-  if (GetAccService()) {
-    *aResult = true;
-  }
-#endif
-#endif // defined(ACCESSIBILITY)
-  return NS_OK;
-}
-