Merge m-c to b-i
authorPhil Ringnalda <philringnalda@gmail.com>
Sat, 12 Dec 2015 17:02:02 -0800
changeset 310424 f8168f43b47fb3e1c18b25f91b014006aa092e1b
parent 310423 37d019bba0b09fb717193da5a0206d8864f8572c (current diff)
parent 310400 f07e71078bc8991f74c2101944c8f869c77f442a (diff)
child 310425 a9868de2174a369099d737858c0bd110181878b9
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)
milestone45.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to b-i
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/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -7,18 +7,16 @@ XPCOMUtils.defineLazyServiceGetter(this,
                                    "nsIAboutNewTabService");
 
 XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern",
                                   "resource://gre/modules/MatchPattern.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Services",
                                   "resource://gre/modules/Services.jsm");
 
-/* globals aboutNewTabService */
-
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 
 var {
   EventManager,
   ignoreEvent,
   runSafe,
 } = ExtensionUtils;
 
--- a/browser/components/extensions/ext-windows.js
+++ b/browser/components/extensions/ext-windows.js
@@ -1,18 +1,16 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
 
-/* globals aboutNewTabService */
-
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 var {
   EventManager,
   runSafe,
 } = ExtensionUtils;
 
 extensions.registerSchemaAPI("windows", null, (extension, context) => {
   return {
--- 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/components/sessionstore/content/aboutSessionRestore.js
+++ b/browser/components/sessionstore/content/aboutSessionRestore.js
@@ -72,17 +72,17 @@ function initTreeView() {
     var winState = {
       label: aWinData.tabGroupsMigrationTitle || winLabel.replace("%S", (aIx + 1)),
       open: true,
       checked: true,
       ix: aIx
     };
     winState.tabs = aWinData.tabs.map(function(aTabData) {
       var entry = aTabData.entries[aTabData.index - 1] || { url: "about:blank" };
-      var iconURL = aTabData.attributes && aTabData.attributes.image || null;
+      var iconURL = aTabData.image || null;
       // don't initiate a connection just to fetch a favicon (see bug 462863)
       if (/^https?:/.test(iconURL))
         iconURL = "moz-anno:favicon:" + iconURL;
       return {
         label: entry.title || entry.url,
         checked: true,
         src: iconURL,
         parent: winState
--- a/browser/components/sessionstore/test/browser_aboutSessionRestore.js
+++ b/browser/components/sessionstore/test/browser_aboutSessionRestore.js
@@ -1,15 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-const CRASH_SHENTRY = {url: "about:mozilla"};
-const CRASH_TAB = {entries: [CRASH_SHENTRY]};
+const CRASH_URL = "about:mozilla";
+const CRASH_FAVICON = "chrome://branding/content/icon32.png";
+const CRASH_SHENTRY = {url: CRASH_URL};
+const CRASH_TAB = {entries: [CRASH_SHENTRY], image: CRASH_FAVICON};
 const CRASH_STATE = {windows: [{tabs: [CRASH_TAB]}]};
 
 const TAB_URL = "about:sessionrestore";
 const TAB_FORMDATA = {url: TAB_URL, id: {sessionData: CRASH_STATE}};
 const TAB_SHENTRY = {url: TAB_URL};
 const TAB_STATE = {entries: [TAB_SHENTRY], formdata: TAB_FORMDATA};
 
 const FRAME_SCRIPT = "data:," +
@@ -21,24 +23,30 @@ add_task(function* () {
   let browser = tab.linkedBrowser;
   yield promiseBrowserLoaded(browser);
 
   // Fake a post-crash tab.
   ss.setTabState(tab, JSON.stringify(TAB_STATE));
   yield promiseTabRestored(tab);
 
   ok(gBrowser.tabs.length > 1, "we have more than one tab");
+
+  let view = browser.contentDocument.getElementById("tabList").view;
+  ok(view.isContainer(0), "first entry is the window");
+  is(view.getCellProperties(1, { id: "title" }), "icon",
+    "second entry is the tab and has a favicon");
+
   browser.messageManager.loadFrameScript(FRAME_SCRIPT, true);
 
   // Wait until the new window was restored.
   let win = yield waitForNewWindow();
   yield BrowserTestUtils.closeWindow(win);
 
   let [{tabs: [{entries: [{url}]}]}] = JSON.parse(ss.getClosedWindowData());
-  is(url, "about:mozilla", "session was restored correctly");
+  is(url, CRASH_URL, "session was restored correctly");
   ss.forgetClosedWindow(0);
 });
 
 function waitForNewWindow() {
   return new Promise(resolve => {
     Services.obs.addObserver(function observe(win, topic) {
       Services.obs.removeObserver(observe, topic);
       resolve(win);
--- a/browser/extensions/loop/bootstrap.js
+++ b/browser/extensions/loop/bootstrap.js
@@ -456,16 +456,20 @@ var WindowListener = {
        *
        * Push message parameters:
        * - {Integer} windowId  The new windowId for the browser.
        */
       startBrowserSharing: function() {
         if (!this._listeningToTabSelect) {
           gBrowser.tabContainer.addEventListener("TabSelect", this);
           this._listeningToTabSelect = true;
+
+          // Watch for title changes as opposed to location changes as more
+          // metadata about the page is available when this event fires.
+          gBrowser.addEventListener("DOMTitleChanged", this);
         }
 
         this._maybeShowBrowserSharingInfoBar();
 
         // Get the first window Id for the listener.
         this.LoopAPI.broadcastPushMessage("BrowserSwitch",
           gBrowser.selectedBrowser.outerWindowID);
       },
@@ -475,16 +479,17 @@ var WindowListener = {
        */
       stopBrowserSharing: function() {
         if (!this._listeningToTabSelect) {
           return;
         }
 
         this._hideBrowserSharingInfoBar();
         gBrowser.tabContainer.removeEventListener("TabSelect", this);
+        gBrowser.removeEventListener("DOMTitleChanged", this);
         this._listeningToTabSelect = false;
       },
 
       /**
        * Helper function to fetch a localized string via the MozLoopService API.
        * It's currently inconveniently wrapped inside a string of stringified JSON.
        *
        * @param  {String} key The element id to get strings for.
@@ -570,40 +575,50 @@ var WindowListener = {
         if (permanently) {
           this.MozLoopService.setLoopPref(kPrefBrowserSharingInfoBar, false);
         }
 
         return removed;
       },
 
       /**
+       * Broadcast 'BrowserSwitch' event.
+      */
+      _notifyBrowserSwitch() {
+         // Get the first window Id for the listener.
+        this.LoopAPI.broadcastPushMessage("BrowserSwitch",
+          gBrowser.selectedBrowser.outerWindowID);
+      },
+
+      /**
        * Handles events from gBrowser.
        */
       handleEvent: function(event) {
-        // We only should get "select" events.
-        if (event.type != "TabSelect") {
-          return;
-        }
+        switch(event.type) {
+          case "DOMTitleChanged":
+            // Get the new title of the shared tab
+            this._notifyBrowserSwitch();
+            break;
+          case "TabSelect":
+            let wasVisible = false;
+            // Hide the infobar from the previous tab.
+            if (event.detail.previousTab) {
+              wasVisible = this._hideBrowserSharingInfoBar(false, event.detail.previousTab.linkedBrowser);
+            }
 
-        let wasVisible = false;
-        // Hide the infobar from the previous tab.
-        if (event.detail.previousTab) {
-          wasVisible = this._hideBrowserSharingInfoBar(false,
-            event.detail.previousTab.linkedBrowser);
-        }
+            // We've changed the tab, so get the new window id.
+            this._notifyBrowserSwitch();
 
-        // We've changed the tab, so get the new window id.
-        this.LoopAPI.broadcastPushMessage("BrowserSwitch",
-          gBrowser.selectedBrowser.outerWindowID);
-
-        if (wasVisible) {
-          // If the infobar was visible before, we should show it again after the
-          // switch.
-          this._maybeShowBrowserSharingInfoBar();
-        }
+            if (wasVisible) {
+              // If the infobar was visible before, we should show it again after the
+              // switch.
+              this._maybeShowBrowserSharingInfoBar();
+            }
+            break;
+          }
       },
 
       /**
        * Fetch the favicon of the currently selected tab in the format of a data-uri.
        *
        * @param  {Function} callback Function to be invoked with an error object as
        *                             its first argument when an error occurred or
        *                             a string as second argument when the favicon
--- a/browser/extensions/loop/content/panels/js/roomStore.js
+++ b/browser/extensions/loop/content/panels/js/roomStore.js
@@ -482,29 +482,28 @@ loop.store = loop.store || {};
             error: result
           }));
           return;
         }
 
         var roomData = {};
         var context = result.decryptedContext;
         var oldRoomName = context.roomName;
-        var newRoomName = actionData.newRoomName.trim();
+        var newRoomName = (actionData.newRoomName || "").trim();
         if (newRoomName && oldRoomName !== newRoomName) {
           roomData.roomName = newRoomName;
         }
         var oldRoomURLs = context.urls;
         var oldRoomURL = oldRoomURLs && oldRoomURLs[0];
         // Since we want to prevent storing falsy (i.e. empty) values for context
         // data, there's no need to send that to the server as an update.
         var newRoomURL = loop.shared.utils.stripFalsyValues({
-          location: actionData.newRoomURL ? actionData.newRoomURL.trim() : "",
-          thumbnail: actionData.newRoomURL ? actionData.newRoomThumbnail.trim() : "",
-          description: actionData.newRoomDescription ?
-            actionData.newRoomDescription.trim() : ""
+          location: (actionData.newRoomURL || "").trim(),
+          thumbnail: (actionData.newRoomThumbnail || "").trim(),
+          description: (actionData.newRoomDescription || "").trim()
         });
         // Only attach a context to the room when
         // 1) there was already a URL set,
         // 2) a new URL is provided as of now,
         // 3) the URL data has changed.
         var diff = loop.shared.utils.objectDiff(oldRoomURL, newRoomURL);
         if (diff.added.length || diff.updated.length) {
           newRoomURL = _.extend(oldRoomURL || {}, newRoomURL);
--- a/browser/extensions/loop/content/shared/js/actions.js
+++ b/browser/extensions/loop/content/shared/js/actions.js
@@ -316,18 +316,18 @@ loop.shared.actions = (function() {
       roomToken: String
     }),
 
     /**
      * Updates the context data attached to a room.
      * XXX: should move to some roomActions module - refs bug 1079284
      */
     UpdateRoomContext: Action.define("updateRoomContext", {
-      roomToken: String,
-      newRoomName: String
+      roomToken: String
+      // newRoomName: String, Optional.
       // newRoomDescription: String, Optional.
       // newRoomThumbnail: String, Optional.
       // newRoomURL: String Optional.
     }),
 
     /**
      * Updating the context data attached to a room error.
      */
--- a/browser/extensions/loop/content/shared/js/activeRoomStore.js
+++ b/browser/extensions/loop/content/shared/js/activeRoomStore.js
@@ -54,16 +54,18 @@ loop.store.ActiveRoomStore = (function()
     roomContextUrls: "roomContextUrls",
     roomDescription: "roomDescription",
     roomInfoFailure: "roomInfoFailure",
     roomName: "roomName",
     roomState: "roomState",
     socialShareProviders: "socialShareProviders"
   };
 
+  var updateContextTimer = null;
+
   /**
    * Active room store.
    *
    * @param {loop.Dispatcher} dispatcher  The dispatcher for dispatching actions
    *                                      and registering to consume actions.
    * @param {Object} options Options object:
    * - {OTSdkDriver} sdkDriver  The SDK driver instance.
    */
@@ -916,16 +918,37 @@ loop.store.ActiveRoomStore = (function()
         };
         this._sdkDriver.startScreenShare(options);
       } else if (screenSharingState === SCREEN_SHARE_STATES.ACTIVE) {
         // Just update the current share.
         this._sdkDriver.switchAcquiredWindow(windowId);
       } else {
         console.error("Unexpectedly received windowId for browser sharing when pending");
       }
+
+      // The browser being shared changed, so update to the new context
+      loop.request("GetSelectedTabMetadata").then(function(meta) {
+        if (!meta) {
+          return;
+        }
+
+        if (updateContextTimer) {
+          clearTimeout(updateContextTimer);
+        }
+
+        updateContextTimer = setTimeout(function() {
+          this.dispatchAction(new sharedActions.UpdateRoomContext({
+            newRoomDescription: meta.title || meta.description || meta.url,
+            newRoomThumbnail: meta.favicon,
+            newRoomURL: meta.url,
+            roomToken: this.getStoreState().roomToken
+          }));
+          updateContextTimer = null;
+        }.bind(this), 500);
+      }.bind(this));
     },
 
     /**
      * Initiates a browser tab sharing publisher.
      *
      * @param {sharedActions.StartBrowserShare} actionData
      */
     startBrowserShare: function(actionData) {
--- a/browser/extensions/loop/test/shared/activeRoomStore_test.js
+++ b/browser/extensions/loop/test/shared/activeRoomStore_test.js
@@ -10,23 +10,25 @@ describe("loop.store.ActiveRoomStore", f
   var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;
   var ROOM_STATES = loop.store.ROOM_STATES;
   var CHAT_CONTENT_TYPES = loop.shared.utils.CHAT_CONTENT_TYPES;
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
   var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
   var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
   var sandbox, dispatcher, store, requestStubs, fakeSdkDriver, fakeMultiplexGum;
   var standaloneMediaRestore;
+  var clock;
 
   beforeEach(function() {
     sandbox = LoopMochaUtils.createSandbox();
-    sandbox.useFakeTimers();
+    clock = sandbox.useFakeTimers();
 
     LoopMochaUtils.stubLoopRequest(requestStubs = {
       GetLoopPref: sinon.stub(),
+      GetSelectedTabMetadata: sinon.stub(),
       SetLoopPref: sinon.stub(),
       AddConversationContext: sinon.stub(),
       AddBrowserSharingListener: sinon.stub().returns(42),
       RemoveBrowserSharingListener: sinon.stub(),
       "Rooms:Get": sinon.stub().returns({
         roomUrl: "http://invalid"
       }),
       "Rooms:Join": sinon.stub().returns({}),
@@ -1543,48 +1545,93 @@ describe("loop.store.ActiveRoomStore", f
 
       expect(store.getStoreState().remoteVideoDimensions).eql({
         camera: { fake: 20 }
       });
     });
   });
 
   describe("#startBrowserShare", function() {
+    var getSelectedTabMetadataStub;
+
+    beforeEach(function() {
+      getSelectedTabMetadataStub = sinon.stub();
+      LoopMochaUtils.stubLoopRequest({
+        GetSelectedTabMetadata: getSelectedTabMetadataStub.returns({
+          title: "fakeTitle",
+          favicon: "fakeFavicon",
+          url: "http://www.fakeurl.com"
+        })
+      });
+
+      store.setStoreState({
+        roomState: ROOM_STATES.JOINED,
+        roomToken: "fakeToken",
+        sessionToken: "1627384950"
+      });
+
+      store.startBrowserShare(new sharedActions.StartBrowserShare());
+    });
+
     afterEach(function() {
       store.endScreenShare();
     });
 
     it("should set the state to 'pending'", function() {
-      store.startBrowserShare(new sharedActions.StartBrowserShare());
-
       sinon.assert.calledOnce(dispatcher.dispatch);
       sinon.assert.calledWith(dispatcher.dispatch,
         new sharedActions.ScreenSharingState({
           state: SCREEN_SHARE_STATES.PENDING
         }));
     });
 
     it("should add a browser sharing listener for tab sharing", function() {
-      store.startBrowserShare(new sharedActions.StartBrowserShare());
-
       sinon.assert.calledOnce(requestStubs.AddBrowserSharingListener);
     });
 
     it("should invoke the SDK driver with the correct options for tab sharing", function() {
-      store.startBrowserShare(new sharedActions.StartBrowserShare());
-
       sinon.assert.calledOnce(fakeSdkDriver.startScreenShare);
       sinon.assert.calledWith(fakeSdkDriver.startScreenShare, {
         videoSource: "browser",
         constraints: {
           browserWindow: 42,
           scrollWithPage: true
         }
       });
     });
+
+    it("should request the new metadata when the browser being shared change", function() {
+      clock.tick(500);
+      sinon.assert.calledOnce(getSelectedTabMetadataStub);
+      sinon.assert.calledTwice(dispatcher.dispatch);
+      sinon.assert.calledWith(dispatcher.dispatch.getCall(1),
+        new sharedActions.UpdateRoomContext({
+          newRoomDescription: "fakeTitle",
+          newRoomThumbnail: "fakeFavicon",
+          newRoomURL: "http://www.fakeurl.com",
+          roomToken: store.getStoreState().roomToken
+      }));
+    });
+
+    it("should process only one request", function() {
+      // Simulates multiple requests.
+      LoopMochaUtils.publish("BrowserSwitch", 72);
+      LoopMochaUtils.publish("BrowserSwitch", 72);
+
+      clock.tick(500);
+      sinon.assert.calledThrice(getSelectedTabMetadataStub);
+      sinon.assert.calledTwice(dispatcher.dispatch);
+      sinon.assert.calledWith(dispatcher.dispatch.getCall(1),
+        new sharedActions.UpdateRoomContext({
+          newRoomDescription: "fakeTitle",
+          newRoomThumbnail: "fakeFavicon",
+          newRoomURL: "http://www.fakeurl.com",
+          roomToken: store.getStoreState().roomToken
+      }));
+    });
   });
 
   describe("Screen share Events", function() {
     beforeEach(function() {
       store.startBrowserShare(new sharedActions.StartBrowserShare());
 
       store.setStoreState({
         screenSharingState: SCREEN_SHARE_STATES.ACTIVE
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,3 +1,3 @@
 This is the pdf.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 1.3.56
+Current extension version is: 1.3.72
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -15,18 +15,18 @@
 /*jshint globalstrict: false */
 /* globals PDFJS */
 
 // Initializing PDFJS global object (if still undefined)
 if (typeof PDFJS === 'undefined') {
   (typeof window !== 'undefined' ? window : this).PDFJS = {};
 }
 
-PDFJS.version = '1.3.56';
-PDFJS.build = 'e2aca38';
+PDFJS.version = '1.3.72';
+PDFJS.build = '4d6f3c8';
 
 (function pdfjsWrapper() {
   // Use strict in our context only - users might not want it
   'use strict';
 
 
 
 var globalScope = (typeof window === 'undefined') ? this : window;
@@ -4025,37 +4025,39 @@ var CanvasGraphics = (function CanvasGra
         var alpha_ = 255 - alpha;
         bytes[i - 3] = (bytes[i - 3] * alpha + r0 * alpha_) >> 8;
         bytes[i - 2] = (bytes[i - 2] * alpha + g0 * alpha_) >> 8;
         bytes[i - 1] = (bytes[i - 1] * alpha + b0 * alpha_) >> 8;
       }
     }
   }
 
-  function composeSMaskAlpha(maskData, layerData) {
+  function composeSMaskAlpha(maskData, layerData, transferMap) {
     var length = maskData.length;
     var scale = 1 / 255;
     for (var i = 3; i < length; i += 4) {
-      var alpha = maskData[i];
+      var alpha = transferMap ? transferMap[maskData[i]] : maskData[i];
       layerData[i] = (layerData[i] * alpha * scale) | 0;
     }
   }
 
-  function composeSMaskLuminosity(maskData, layerData) {
+  function composeSMaskLuminosity(maskData, layerData, transferMap) {
     var length = maskData.length;
     for (var i = 3; i < length; i += 4) {
       var y = (maskData[i - 3] * 77) +  // * 0.3 / 255 * 0x10000
               (maskData[i - 2] * 152) + // * 0.59 ....
               (maskData[i - 1] * 28);   // * 0.11 ....
-      layerData[i] = (layerData[i] * y) >> 16;
+      layerData[i] = transferMap ?
+        (layerData[i] * transferMap[y >> 8]) >> 8 :
+        (layerData[i] * y) >> 16;
     }
   }
 
   function genericComposeSMask(maskCtx, layerCtx, width, height,
-                               subtype, backdrop) {
+                               subtype, backdrop, transferMap) {
     var hasBackdrop = !!backdrop;
     var r0 = hasBackdrop ? backdrop[0] : 0;
     var g0 = hasBackdrop ? backdrop[1] : 0;
     var b0 = hasBackdrop ? backdrop[2] : 0;
 
     var composeFn;
     if (subtype === 'Luminosity') {
       composeFn = composeSMaskLuminosity;
@@ -4069,39 +4071,39 @@ var CanvasGraphics = (function CanvasGra
     for (var row = 0; row < height; row += chunkSize) {
       var chunkHeight = Math.min(chunkSize, height - row);
       var maskData = maskCtx.getImageData(0, row, width, chunkHeight);
       var layerData = layerCtx.getImageData(0, row, width, chunkHeight);
 
       if (hasBackdrop) {
         composeSMaskBackdrop(maskData.data, r0, g0, b0);
       }
-      composeFn(maskData.data, layerData.data);
+      composeFn(maskData.data, layerData.data, transferMap);
 
       maskCtx.putImageData(layerData, 0, row);
     }
   }
 
   function composeSMask(ctx, smask, layerCtx) {
     var mask = smask.canvas;
     var maskCtx = smask.context;
 
     ctx.setTransform(smask.scaleX, 0, 0, smask.scaleY,
                      smask.offsetX, smask.offsetY);
 
     var backdrop = smask.backdrop || null;
-    if (WebGLUtils.isEnabled) {
+    if (!smask.transferMap && WebGLUtils.isEnabled) {
       var composed = WebGLUtils.composeSMask(layerCtx.canvas, mask,
         {subtype: smask.subtype, backdrop: backdrop});
       ctx.setTransform(1, 0, 0, 1, 0, 0);
       ctx.drawImage(composed, smask.offsetX, smask.offsetY);
       return;
     }
     genericComposeSMask(maskCtx, layerCtx, mask.width, mask.height,
-                        smask.subtype, backdrop);
+                        smask.subtype, backdrop, smask.transferMap);
     ctx.drawImage(mask, 0, 0);
   }
 
   var LINE_CAP_STYLES = ['butt', 'round', 'square'];
   var LINE_JOIN_STYLES = ['miter', 'round', 'bevel'];
   var NORMAL_CLIP = {};
   var EO_CLIP = {};
 
@@ -4837,26 +4839,32 @@ var CanvasGraphics = (function CanvasGra
           width = vmetric ? -vmetric[0] : width;
           scaledX = vx / fontSizeScale;
           scaledY = (x + vy) / fontSizeScale;
         } else {
           scaledX = x / fontSizeScale;
           scaledY = 0;
         }
 
-        if (font.remeasure && width > 0 && this.isFontSubpixelAAEnabled) {
-          // some standard fonts may not have the exact width, trying to
-          // rescale per character
+        if (font.remeasure && width > 0) {
+          // Some standard fonts may not have the exact width: rescale per
+          // character if measured width is greater than expected glyph width
+          // and subpixel-aa is enabled, otherwise just center the glyph.
           var measuredWidth = ctx.measureText(character).width * 1000 /
             fontSize * fontSizeScale;
-          var characterScaleX = width / measuredWidth;
-          restoreNeeded = true;
-          ctx.save();
-          ctx.scale(characterScaleX, 1);
-          scaledX /= characterScaleX;
+          if (width < measuredWidth && this.isFontSubpixelAAEnabled) {
+            var characterScaleX = width / measuredWidth;
+            restoreNeeded = true;
+            ctx.save();
+            ctx.scale(characterScaleX, 1);
+            scaledX /= characterScaleX;
+          } else if (width !== measuredWidth) {
+            scaledX += (width - measuredWidth) / 2000 *
+              fontSize / fontSizeScale;
+          }
         }
 
         if (simpleFillText && !accent) {
           // common case
           ctx.fillText(character, scaledX, scaledY);
         } else {
           this.paintChar(character, scaledX, scaledY);
           if (accent) {
@@ -5142,17 +5150,18 @@ var CanvasGraphics = (function CanvasGra
         this.smaskStack.push({
           canvas: scratchCanvas.canvas,
           context: groupCtx,
           offsetX: offsetX,
           offsetY: offsetY,
           scaleX: scaleX,
           scaleY: scaleY,
           subtype: group.smask.subtype,
-          backdrop: group.smask.backdrop
+          backdrop: group.smask.backdrop,
+          transferMap: group.smask.transferMap || null
         });
       } else {
         // Setup the current ctx so when the group is popped we draw it at the
         // right location.
         currentCtx.setTransform(1, 0, 0, 1, 0, 0);
         currentCtx.translate(offsetX, offsetY);
         currentCtx.scale(scaleX, scaleY);
       }
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -15,18 +15,18 @@
 /*jshint globalstrict: false */
 /* globals PDFJS */
 
 // Initializing PDFJS global object (if still undefined)
 if (typeof PDFJS === 'undefined') {
   (typeof window !== 'undefined' ? window : this).PDFJS = {};
 }
 
-PDFJS.version = '1.3.56';
-PDFJS.build = 'e2aca38';
+PDFJS.version = '1.3.72';
+PDFJS.build = '4d6f3c8';
 
 (function pdfjsWrapper() {
   // Use strict in our context only - users might not want it
   'use strict';
 
 
 
 var globalScope = (typeof window === 'undefined') ? this : window;
@@ -10531,16 +10531,32 @@ var PartialEvaluator = (function Partial
     handleSMask: function PartialEvaluator_handleSmask(smask, resources,
                                                        operatorList, task,
                                                        stateManager) {
       var smaskContent = smask.get('G');
       var smaskOptions = {
         subtype: smask.get('S').name,
         backdrop: smask.get('BC')
       };
+
+      // The SMask might have a alpha/luminosity value transfer function --
+      // we will build a map of integer values in range 0..255 to be fast.
+      var transferObj = smask.get('TR');
+      if (isPDFFunction(transferObj)) {
+        var transferFn = PDFFunction.parse(this.xref, transferObj);
+        var transferMap = new Uint8Array(256);
+        var tmp = new Float32Array(1);
+        for (var i = 0; i < 255; i++) {
+          tmp[0] = i / 255;
+          transferFn(tmp, 0, tmp, 0);
+          transferMap[i] = (tmp[0] * 255) | 0;
+        }
+        smaskOptions.transferMap = transferMap;
+      }
+
       return this.buildFormXObject(resources, smaskContent, smaskOptions,
                             operatorList, task, stateManager.state.clone());
     },
 
     handleTilingType:
         function PartialEvaluator_handleTilingType(fn, args, resources,
                                                    pattern, patternDict,
                                                    operatorList, task) {
@@ -16221,16 +16237,19 @@ function reverseIfRtl(chars) {
   var s = '';
   for (var ii = charsLength - 1; ii >= 0; ii--) {
     s += chars[ii];
   }
   return s;
 }
 
 function adjustWidths(properties) {
+  if (!properties.fontMatrix) {
+    return;
+  }
   if (properties.fontMatrix[0] === FONT_IDENTITY_MATRIX[0]) {
     return;
   }
   // adjusting width to fontMatrix scale
   var scale = 0.001 / properties.fontMatrix[0];
   var glyphsWidths = properties.widths;
   for (var glyph in glyphsWidths) {
     glyphsWidths[glyph] *= scale;
@@ -16344,17 +16363,17 @@ var IdentityToUnicodeMap = (function Ide
     get: function (i) {
       if (this.firstChar <= i && i <= this.lastChar) {
         return String.fromCharCode(i);
       }
       return undefined;
     },
 
     charCodeOf: function (v) {
-      error('should not call .charCodeOf');
+      return (isInt(v) && v >= this.firstChar && v <= this.lastChar) ? v : -1;
     }
   };
 
   return IdentityToUnicodeMap;
 })();
 
 var OpenTypeFileBuilder = (function OpenTypeFileBuilderClosure() {
   function writeInt16(dest, offset, num) {
@@ -16735,16 +16754,18 @@ var Font = (function FontClosure() {
       case 'TrueType':
       case 'CIDFontType2':
         this.mimetype = 'font/opentype';
 
         // Repair the TrueType file. It is can be damaged in the point of
         // view of the sanitizer
         data = this.checkAndRepair(name, file, properties);
         if (this.isOpenType) {
+          adjustWidths(properties);
+
           type = 'OpenType';
         }
         break;
 
       default:
         error('Font ' + type + ' is not supported');
         break;
     }
@@ -18167,16 +18188,18 @@ var Font = (function FontClosure() {
       if (!isTrueType) {
         // OpenType font
         if ((header.version === 'OTTO' && properties.type !== 'CIDFontType2') ||
             !tables.head || !tables.hhea || !tables.maxp || !tables.post) {
           // no major tables: throwing everything at CFFFont
           cffFile = new Stream(tables['CFF '].data);
           cff = new CFFFont(cffFile, properties);
 
+          adjustWidths(properties);
+
           return this.convert(name, cff, properties);
         }
 
         delete tables.glyf;
         delete tables.loca;
         delete tables.fpgm;
         delete tables.prep;
         delete tables['cvt '];
@@ -18788,17 +18811,17 @@ var Font = (function FontClosure() {
         // finding the charcode via unicodeToCID map
         var charcode = 0;
         if (this.composite) {
           if (this.cMap.contains(glyphUnicode)) {
             charcode = this.cMap.lookup(glyphUnicode);
           }
         }
         // ... via toUnicode map
-        if (!charcode && 'toUnicode' in this) {
+        if (!charcode && this.toUnicode) {
           charcode = this.toUnicode.charCodeOf(glyphUnicode);
         }
         // setting it to unicode if negative or undefined
         if (charcode <= 0) {
           charcode = glyphUnicode;
         }
         // trying to get width via charcode
         width = this.widths[charcode];
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -1525,17 +1525,17 @@ var PDFLinkService = (function () {
             pdfOpenParams += '&zoom=' + scale;
             if (dest[2] || dest[3]) {
               pdfOpenParams += ',' + (dest[2] || 0) + ',' + (dest[3] || 0);
             }
           }
           return pdfOpenParams;
         }
       }
-      return '';
+      return this.getAnchorUrl('');
     },
 
     /**
      * Prefix the full url on anchor links to make sure that links are resolved
      * relative to the current URL instead of the one defined in <base href>.
      * @param {String} anchor The anchor hash, including the #.
      * @returns {string} The hyperlink to the PDF object.
      */
--- 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/browser/themes/windows/tabbrowser/newtab-inverted-XPVista7.svg
+++ b/browser/themes/windows/tabbrowser/newtab-inverted-XPVista7.svg
@@ -1,13 +1,13 @@
 <!-- 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/. -->
 <svg width="16" height="18" xmlns="http://www.w3.org/2000/svg">
-  <g stroke="#7f7f7f" stroke-width="2" fill="none">
+  <g stroke="#666" stroke-width="2" fill="none">
     <rect x="7" y="4" width="2" height="10" rx="0.25" ry="0.25"/>
     <rect x="3" y="8" width="10" height="2" rx="0.25" ry="0.25"/>
   </g>
   <g fill="#fff">
     <rect width="2" height="10" x="7" y="4"/>
     <rect width="10" height="2" x="3" y="8"/>
   </g>
 </svg>
--- a/browser/themes/windows/tabbrowser/newtab-inverted.svg
+++ b/browser/themes/windows/tabbrowser/newtab-inverted.svg
@@ -1,13 +1,13 @@
 <!-- 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/. -->
 <svg width="16" height="18" xmlns="http://www.w3.org/2000/svg">
-  <g stroke="#7f7f7f" stroke-width="2" fill="none">
+  <g stroke="#666" stroke-width="2" fill="none">
     <rect x="7" y="3" width="2" height="12" rx="0.25" ry="0.25"/>
     <rect x="2" y="8" width="12" height="2" rx="0.25" ry="0.25"/>
   </g>
   <g fill="#fff">
     <rect x="7" y="3" width="2" height="12"/>
     <rect x="2" y="8" width="12" height="2"/>
   </g>
 </svg>
--- a/browser/themes/windows/tabbrowser/tab-arrow-left-XPVista7.svg
+++ b/browser/themes/windows/tabbrowser/tab-arrow-left-XPVista7.svg
@@ -1,12 +1,13 @@
 <!-- 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/. -->
 <svg width="15" height="17" xmlns="http://www.w3.org/2000/svg">
   <defs>
-    <linearGradient id="fill" x1="50%" y1="0%" x2="50%" y2="100%">
-      <stop stop-color="#3f4f5a" offset="0"/>
-      <stop stop-color="#7e8c97" offset="1"/>
+    <linearGradient id="gradient" x1="0%" x2="0%" y1="0%" y2="100%">
+      <stop offset="0%" stop-color="#1c2835"/>
+      <stop offset="60%" stop-color="#606e7b"/>
+      <stop offset="100%" stop-color="#465765"/>
     </linearGradient>
   </defs>
-  <path d="M11 4L9.5 2.5l-5.875 6 5.875 6L11 13 6.625 8.5z" fill="url(#fill)"/>
+  <path d="M11 4L9.5 2.5l-5.875 6 5.875 6L11 13 6.625 8.5z" fill="url(#gradient)"/>
 </svg>
--- a/devtools/client/jsonview/converter-sniffer.js
+++ b/devtools/client/jsonview/converter-sniffer.js
@@ -17,16 +17,17 @@ loader.lazyRequireGetter(this, "NetworkH
                                "devtools/shared/webconsole/network-helper");
 
 // Constants
 const JSON_TYPE = "application/json";
 const CONTRACT_ID = "@mozilla.org/devtools/jsonview-sniffer;1";
 const CLASS_ID = "{4148c488-dca1-49fc-a621-2a0097a62422}";
 const JSON_VIEW_MIME_TYPE = "application/vnd.mozilla.json.view";
 const JSON_VIEW_TYPE = "JSON View";
+const JSON_EXTENSION = "json";
 const CONTENT_SNIFFER_CATEGORY = "net-content-sniffers";
 
 /**
  * This component represents a sniffer (implements nsIContentSniffer
  * interface) responsible for changing top level 'application/json'
  * document types to: 'application/vnd.mozilla.json.view'.
  *
  * This internal type is consequently rendered by JSON View component
@@ -58,16 +59,20 @@ var Sniffer = Class({
         // Channel doesn't support content dispositions
       }
 
       // Check the response content type and if it's application/json
       // change it to new internal type consumed by JSON View.
       if (aRequest.contentType == JSON_TYPE) {
         return JSON_VIEW_MIME_TYPE;
       }
+
+      if (NetworkHelper.getFileExtension(aRequest.name) == JSON_EXTENSION) {
+        return JSON_VIEW_MIME_TYPE;
+      }
     }
 
     return "";
   }
 });
 
 var service = xpcom.Service({
   id: components.ID(CLASS_ID),
--- a/devtools/client/themes/memory.css
+++ b/devtools/client/themes/memory.css
@@ -136,16 +136,17 @@ html, body, #app, #memory-tool {
 }
 
 /**
  * Sidebar
  */
 
 .list {
   width: var(--sidebar-width);
+  min-width: var(--sidebar-width);
   overflow-y: auto;
   margin: 0;
   padding: 0;
   background-color: var(--theme-sidebar-background);
   border-inline-end: 1px solid var(--theme-splitter-color);
 }
 
 .snapshot-list-item {
--- a/devtools/shared/webconsole/network-helper.js
+++ b/devtools/shared/webconsole/network-helper.js
@@ -798,14 +798,44 @@ var NetworkHelper = {
   nsIURL: function(aUrl, aStore = gNSURLStore) {
     if (aStore.has(aUrl)) {
       return aStore.get(aUrl);
     }
 
     let uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL);
     aStore.set(aUrl, uri);
     return uri;
+  },
+
+  /**
+   * Returns extension for file URLs (e.g. 'json').
+   * Not everyURL has an extension and this method works as follows:
+   * 1) Remove query string
+   * 2) Get part after the last slash (a file name)
+   * 3) Look for the last dot (an extension)
+   */
+  getFileExtension: function(aUrl) {
+    if (!aUrl) {
+      return;
+    }
+
+    // Remove query string from the URL if any.
+    let queryString = aUrl.indexOf("?");
+    if (queryString != -1) {
+      aUrl = aUrl.substr(0, queryString);
+    }
+
+    // Look for the part after last slash
+    var lastSlash = aUrl.lastIndexOf("/");
+    var fileName = aUrl.substr(lastSlash + 1);
+    if (!fileName) {
+      return;
+    }
+
+    // Now get the file extension.
+    var lastDot = fileName.lastIndexOf(".");
+    return fileName.substr(lastDot + 1);
   }
 };
 
 for (let prop of Object.getOwnPropertyNames(NetworkHelper)) {
   exports[prop] = NetworkHelper[prop];
 }
--- 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/tests/crashtests/crashtests.list
+++ b/gfx/tests/crashtests/crashtests.list
@@ -7,17 +7,17 @@ load 248518-1.html
 load 306649-1.xml
 load 306902-1.xml
 load 333861-1.html
 load 334735-1.html
 load 345576-1.html
 load 345629-1.html
 load 348462-1.html
 load 348462-2.html
-load 358732-1.xhtml
+skip load 358732-1.xhtml # Bug 1226751
 load 358732-2.svg
 load 358732-3.html
 load 366643.html
 load 369688-1.html
 load 369947-1.html
 load 372094-1.xhtml
 load 376627-1.html
 load 377231-1.html
@@ -49,17 +49,17 @@ load 398042-1.xhtml
 load 398042-2.xhtml
 load 402307-1.html
 load 403464-1.html
 load 404112-1.html
 load 404112-2.html
 load 405268-1.xhtml
 load 407761-1.html
 load 407842.html
-asserts-if(Android,1) load 408754-1.html
+load 408754-1.html
 load 410728-1.xml
 load 416637-1.html
 load 419095-1.html
 load 419255-1.html
 load 420945-1.html
 load 420962-1.html
 load 421393-1.html
 load 421813-1.html
--- 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/reftests/writing-mode/reftest.list
+++ b/layout/reftests/writing-mode/reftest.list
@@ -162,12 +162,13 @@ fuzzy-if(gtkWidget||B2G,255,6) fuzzy-if(
 
 == 1196887-1-computed-display-inline-block.html 1196887-1-computed-display-inline-block-ref.html
 == 1205787-legacy-svg-values-1.html 1205787-legacy-svg-values-1-ref.html
 
 == 1216747-1.html 1216747-1-ref.html
 != 1216747-1.html 1216747-1-notref.html
 
 # Suite of tests from GĂ©rard Talbot in bug 1079151
-include abspos/reftest.list
+# Frequent Windows 7 load failed: timed out waiting for test to complete (waiting for onload scripts to complete), bug 1167155 and friends
+skip-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) include abspos/reftest.list
 
 # Tests for tables with vertical writing modes
 include tables/reftest.list
--- 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