merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 25 May 2016 15:04:00 +0200
changeset 337903 d6d4e8417d2fd71fdf47c319b7a217f6ace9d5a5
parent 337701 242c458c4c61e94c8d12628612ef5e4e83992eaa (current diff)
parent 337902 97b22d33cd789520910ef8dfc81374ab53c62134 (diff)
child 337970 0bc5c825c58ef9474a28e82f20be5b1415de863f
child 338004 ff6044ca81893c73528253564e0f3ccbcacff046
push id6249
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 13:59:36 +0000
treeherdermozilla-beta@bad9d4f5bf7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone49.0a1
first release with
nightly linux32
d6d4e8417d2f / 49.0a1 / 20160525063710 / files
nightly linux64
d6d4e8417d2f / 49.0a1 / 20160525063710 / files
nightly mac
d6d4e8417d2f / 49.0a1 / 20160525063710 / files
nightly win32
d6d4e8417d2f / 49.0a1 / 20160525063710 / files
nightly win64
d6d4e8417d2f / 49.0a1 / 20160525063710 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
browser/base/content/browser.js
dom/events/EventListenerManager.cpp
modules/libpref/init/all.js
security/manager/ssl/nsIBufEntropyCollector.idl
testing/marionette/client/marionette_driver/marionette.py
testing/marionette/harness/marionette/tests/unit/test_elementState.py
testing/marionette/harness/marionette/tests/unit/test_elementState_chrome.py
testing/marionette/harness/marionette/tests/unit/test_getattr.py
testing/marionette/harness/marionette/tests/unit/test_getattr_chrome.py
testing/taskcluster/tasks/branches/cedar/job_flags.yml
testing/web-platform/meta/DOM-parsing/innerhtml-01.xhtml.ini
testing/web-platform/meta/DOM-parsing/innerhtml-05.xhtml.ini
testing/web-platform/meta/DOM-parsing/xml-serialization.xhtml.ini
testing/web-platform/meta/html/semantics/embedded-content/the-iframe-element/iframe-allowfullscreen.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/attr-referrer/cross-origin/http-http/iframe-tag/cross-origin.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/attr-referrer/cross-origin/http-http/iframe-tag/cross-origin.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/attr-referrer/cross-origin/http-http/iframe-tag/cross-origin.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/attr-referrer/cross-origin/http-https/iframe-tag/cross-origin.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/attr-referrer/cross-origin/http-https/iframe-tag/cross-origin.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/attr-referrer/cross-origin/http-https/iframe-tag/cross-origin.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/attr-referrer/same-origin/http-http/iframe-tag/same-origin-insecure.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/attr-referrer/same-origin/http-https/iframe-tag/same-origin-downgrade.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/attr-referrer/same-origin/http-https/iframe-tag/same-origin-downgrade.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/attr-referrer/same-origin/http-https/iframe-tag/same-origin-downgrade.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/attr-referrer/same-origin/http-https/iframe-tag/same-origin-insecure.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/attr-referrer/same-origin/http-https/iframe-tag/same-origin-upgrade.keep-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/attr-referrer/same-origin/http-https/iframe-tag/same-origin-upgrade.no-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/attr-referrer/same-origin/http-https/iframe-tag/same-origin-upgrade.swap-origin-redirect.http.html.ini
testing/web-platform/meta/uievents/constructors/constructors.html.ini
testing/web-platform/meta/uievents/order-of-events/init-event-while-dispatching.html.ini
testing/web-platform/meta/uievents/throwing-in-listener-and-window-error-event.html.ini
testing/web-platform/tests/DOM-parsing/createContextualFragment.html
testing/web-platform/tests/DOM-parsing/innerhtml-01.xhtml
testing/web-platform/tests/DOM-parsing/innerhtml-03.xhtml
testing/web-platform/tests/DOM-parsing/innerhtml-04.html
testing/web-platform/tests/DOM-parsing/innerhtml-05.xhtml
testing/web-platform/tests/DOM-parsing/innerhtml-06.html
testing/web-platform/tests/DOM-parsing/innerhtml-07.html
testing/web-platform/tests/DOM-parsing/insert_adjacent_html.html
testing/web-platform/tests/DOM-parsing/insert_adjacent_html.js
testing/web-platform/tests/DOM-parsing/insert_adjacent_html.xhtml
testing/web-platform/tests/DOM-parsing/outerhtml-01.html
testing/web-platform/tests/DOM-parsing/outerhtml-02.html
testing/web-platform/tests/DOM-parsing/xml-serialization.xhtml
testing/web-platform/tests/uievents/ClickFakeEvent.nondocument.html
testing/web-platform/tests/uievents/constructors/constructors.html
testing/web-platform/tests/uievents/legacy-domevents-tests/approved/DOM.event.flow.html
testing/web-platform/tests/uievents/legacy-domevents-tests/approved/EventListener.eventHandler.html
testing/web-platform/tests/uievents/legacy-domevents-tests/approved/EventObject.multiple.dispatchEvent.html
testing/web-platform/tests/uievents/order-of-events/event-phases-order.html
testing/web-platform/tests/uievents/order-of-events/init-event-while-dispatching.html
testing/web-platform/tests/uievents/throwing-in-listener-and-window-error-event.html
testing/web-platform/tests/uievents/throwing-in-listener-when-all-have-not-run-yet.html
toolkit/components/telemetry/Histograms.json
toolkit/mozapps/update/updater/macbuild/Contents/Info.plist
--- a/b2g/locales/en-US/chrome/overrides/appstrings.properties
+++ b/b2g/locales/en-US/chrome/overrides/appstrings.properties
@@ -29,12 +29,12 @@ externalProtocolPrompt=An external appli
 externalProtocolUnknown=<Unknown>
 externalProtocolChkMsg=Remember my choice for all links of this type.
 externalProtocolLaunchBtn=Launch application
 malwareBlocked=The site at %S has been reported as an attack site and has been blocked based on your security preferences.
 unwantedBlocked=The site at %S has been reported as serving unwanted software and has been blocked based on your security preferences.
 deceptiveBlocked=This web page at %S has been reported as a deceptive site and has been blocked based on your security preferences.
 forbiddenBlocked=The site at %S has been blocked by your browser configuration.
 cspBlocked=This page has a content security policy that prevents it from being loaded in this way.
-corruptedContentError=The site at %S has experienced a network protocol violation that cannot be repaired.
+corruptedContentErrorv2=The site at %S has experienced a network protocol violation that cannot be repaired.
 remoteXUL=This page uses an unsupported technology that is no longer available by default in Firefox.
 sslv3Used=Firefox cannot guarantee the safety of your data on %S because it uses SSLv3, a broken security protocol.
 weakCryptoUsed=The owner of %S has configured their website improperly. To protect your information from being stolen, Firefox has not connected to this website.
--- a/browser/app/Makefile.in
+++ b/browser/app/Makefile.in
@@ -82,10 +82,13 @@ tools repackage:: $(DIST)/bin/$(MOZ_APP_
 	sed -e 's/%APP_VERSION%/$(MOZ_APP_VERSION)/' -e 's/%MAC_APP_NAME%/$(MAC_APP_NAME)/' -e 's/%MOZ_MACBUNDLE_ID%/$(MOZ_MACBUNDLE_ID)/' -e 's/%MAC_BUNDLE_VERSION%/$(MAC_BUNDLE_VERSION)/' $(srcdir)/macbuild/Contents/Info.plist.in > $(dist_dest)/Contents/Info.plist
 	sed -e 's/%MAC_APP_NAME%/$(MAC_APP_NAME)/' $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in | iconv -f UTF-8 -t UTF-16 > $(dist_dest)/$(LPROJ)/InfoPlist.strings
 	rsync -a --exclude-from='$(srcdir)/macbuild/Contents/MacOS-files.in' $(DIST)/bin/ $(dist_dest)/Contents/Resources
 	rsync -a --include-from='$(srcdir)/macbuild/Contents/MacOS-files.in' --exclude '*' $(DIST)/bin/ $(dist_dest)/Contents/MacOS
 	$(RM) $(dist_dest)/Contents/MacOS/$(MOZ_APP_NAME)
 	rsync -aL $(DIST)/bin/$(MOZ_APP_NAME) $(dist_dest)/Contents/MacOS
 	cp -RL $(DIST)/branding/firefox.icns $(dist_dest)/Contents/Resources/firefox.icns
 	cp -RL $(DIST)/branding/document.icns $(dist_dest)/Contents/Resources/document.icns
+	$(MKDIR) -p $(dist_dest)/Contents/Library/LaunchServices
+	mv -f $(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater $(dist_dest)/Contents/Library/LaunchServices
+	ln -s ../../../../Library/LaunchServices/org.mozilla.updater $(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater
 	printf APPLMOZB > $(dist_dest)/Contents/PkgInfo
 endif
--- a/browser/app/macbuild/Contents/Info.plist.in
+++ b/browser/app/macbuild/Contents/Info.plist.in
@@ -206,12 +206,17 @@
 	<key>LSApplicationCategoryType</key>
 	<string>public.app-category.productivity</string>
 	<key>LSMinimumSystemVersion</key>
 	<string>10.9.0</string>
   <key>NSSupportsAutomaticGraphicsSwitching</key>
   <true/>
   <key>NSPrincipalClass</key>
   <string>GeckoNSApplication</string>
+	<key>SMPrivilegedExecutables</key>
+	<dict>
+		<key>org.mozilla.updater</key>
+		<string>identifier "org.mozilla.updater" and ((anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9]) or (anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] and certificate leaf[field.1.2.840.113635.100.6.1.13] and certificate leaf[subject.OU] = "43AQ936H96"))</string>
+	</dict>
   <key>NSDisablePersistence</key>
   <true/>
 </dict>
 </plist>
--- a/browser/base/content/aboutDialog-appUpdater.js
+++ b/browser/base/content/aboutDialog-appUpdater.js
@@ -5,16 +5,19 @@
 // Note: this file is included in aboutDialog.xul if MOZ_UPDATER is defined.
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
                                   "resource://gre/modules/UpdateUtils.jsm");
 
+const PREF_APP_UPDATE_CANCELATIONS_OSX = "app.update.cancelations.osx";
+const PREF_APP_UPDATE_ELEVATE_NEVER    = "app.update.elevate.never";
+
 var gAppUpdater;
 
 function onUnload(aEvent) {
   if (gAppUpdater.isChecking)
     gAppUpdater.checker.stopChecking(Components.interfaces.nsIUpdateChecker.CURRENT_CHECK);
   // Safe to call even when there isn't a download in progress.
   gAppUpdater.removeDownloadListener();
   gAppUpdater = null;
@@ -71,17 +74,18 @@ function appUpdater()
     // selectPanel("downloading") is called from setupDownloadingUI().
     return;
   }
 
   // Honor the "Never check for updates" option by not only disabling background
   // update checks, but also in the About dialog, by presenting a
   // "Check for updates" button.
   // If updates are found, the user is then asked if he wants to "Update to <version>".
-  if (!this.updateEnabled) {
+  if (!this.updateEnabled ||
+      Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ELEVATE_NEVER)) {
     this.selectPanel("checkForUpdates");
     return;
   }
 
   // That leaves the options
   // "Check for updates, but let me choose whether to install them", and
   // "Automatically install updates".
   // In both cases, we check for updates without asking.
@@ -93,21 +97,23 @@ appUpdater.prototype =
 {
   // true when there is an update check in progress.
   isChecking: false,
 
   // true when there is an update already staged / ready to be applied.
   get isPending() {
     if (this.update) {
       return this.update.state == "pending" ||
-             this.update.state == "pending-service";
+             this.update.state == "pending-service" ||
+             this.update.state == "pending-elevate";
     }
     return this.um.activeUpdate &&
            (this.um.activeUpdate.state == "pending" ||
-            this.um.activeUpdate.state == "pending-service");
+            this.um.activeUpdate.state == "pending-service" ||
+            this.um.activeUpdate.state == "pending-elevate");
   },
 
   // true when there is an update already installed in the background.
   get isApplied() {
     if (this.update)
       return this.update.state == "applied" ||
              this.update.state == "applied-service";
     return this.um.activeUpdate &&
@@ -178,51 +184,60 @@ appUpdater.prototype =
       this.updateDeck.selectedPanel = panel;
     }
   },
 
   /**
    * Check for updates
    */
   checkForUpdates: function() {
+    // Clear prefs that could prevent a user from discovering available updates.
+    if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CANCELATIONS_OSX)) {
+      Services.prefs.clearUserPref(PREF_APP_UPDATE_CANCELATIONS_OSX);
+    }
+    if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_ELEVATE_NEVER)) {
+      Services.prefs.clearUserPref(PREF_APP_UPDATE_ELEVATE_NEVER);
+    }
     this.selectPanel("checkingForUpdates");
     this.isChecking = true;
     this.checker.checkForUpdates(this.updateCheckListener, true);
     // after checking, onCheckComplete() is called
   },
 
   /**
    * Handles oncommand for the "Restart to Update" button
    * which is presented after the download has been downloaded.
    */
   buttonRestartAfterDownload: function() {
-    if (!this.isPending && !this.isApplied)
+    if (!this.isPending && !this.isApplied) {
       return;
+    }
 
-      // Notify all windows that an application quit has been requested.
-      let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"].
-                       createInstance(Components.interfaces.nsISupportsPRBool);
-      Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
-
-      // Something aborted the quit process.
-      if (cancelQuit.data)
-        return;
+    // Notify all windows that an application quit has been requested.
+    let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"].
+                     createInstance(Components.interfaces.nsISupportsPRBool);
+    Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
 
-      let appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"].
-                       getService(Components.interfaces.nsIAppStartup);
+    // Something aborted the quit process.
+    if (cancelQuit.data) {
+      return;
+    }
+
+    let appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"].
+                     getService(Components.interfaces.nsIAppStartup);
 
-      // If already in safe mode restart in safe mode (bug 327119)
-      if (Services.appinfo.inSafeMode) {
-        appStartup.restartInSafeMode(Components.interfaces.nsIAppStartup.eAttemptQuit);
-        return;
-      }
+    // If already in safe mode restart in safe mode (bug 327119)
+    if (Services.appinfo.inSafeMode) {
+      appStartup.restartInSafeMode(Components.interfaces.nsIAppStartup.eAttemptQuit);
+      return;
+    }
 
-      appStartup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit |
-                      Components.interfaces.nsIAppStartup.eRestart);
-    },
+    appStartup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit |
+                    Components.interfaces.nsIAppStartup.eRestart);
+  },
 
   /**
    * Handles oncommand for the "Apply Update…" button
    * which is presented if we need to show the billboard.
    */
   buttonApplyBillboard: function() {
     const URI_UPDATE_PROMPT_DIALOG = "chrome://mozapps/content/update/updates.xul";
     var ary = null;
@@ -366,17 +381,18 @@ appUpdater.prototype =
       if (this.backgroundUpdateEnabled) {
         this.selectPanel("applying");
         let update = this.um.activeUpdate;
         let self = this;
         Services.obs.addObserver(function (aSubject, aTopic, aData) {
           // Update the UI when the background updater is finished
           let status = aData;
           if (status == "applied" || status == "applied-service" ||
-              status == "pending" || status == "pending-service") {
+              status == "pending" || status == "pending-service" ||
+              status == "pending-elevate") {
             // If the update is successfully applied, or if the updater has
             // fallen back to non-staged updates, show the "Restart to Update"
             // button.
             self.selectPanel("apply");
           } else if (status == "failed") {
             // Background update has failed, let's show the UI responsible for
             // prompting the user to update manually.
             self.selectPanel("downloadFailed");
--- a/browser/base/content/aboutNetError.xhtml
+++ b/browser/base/content/aboutNetError.xhtml
@@ -516,17 +516,17 @@
         <h1 id="et_proxyResolveFailure">&proxyResolveFailure.title;</h1>
         <h1 id="et_proxyConnectFailure">&proxyConnectFailure.title;</h1>
         <h1 id="et_contentEncodingError">&contentEncodingError.title;</h1>
         <h1 id="et_unsafeContentType">&unsafeContentType.title;</h1>
         <h1 id="et_nssFailure2">&nssFailure2.title;</h1>
         <h1 id="et_nssBadCert">&certerror.longpagetitle1;</h1>
         <h1 id="et_cspBlocked">&cspBlocked.title;</h1>
         <h1 id="et_remoteXUL">&remoteXUL.title;</h1>
-        <h1 id="et_corruptedContentError">&corruptedContentError.title;</h1>
+        <h1 id="et_corruptedContentErrorv2">&corruptedContentErrorv2.title;</h1>
         <h1 id="et_sslv3Used">&sslv3Used.title;</h1>
         <h1 id="et_weakCryptoUsed">&weakCryptoUsed.title;</h1>
         <h1 id="et_inadequateSecurityError">&inadequateSecurityError.title;</h1>
       </div>
       <div id="errorDescriptionsContainer">
         <div id="ed_generic">&generic.longDesc;</div>
         <div id="ed_dnsNotFound">&dnsNotFound.longDesc;</div>
         <div id="ed_fileNotFound">&fileNotFound.longDesc;</div>
@@ -545,17 +545,17 @@
         <div id="ed_proxyResolveFailure">&proxyResolveFailure.longDesc;</div>
         <div id="ed_proxyConnectFailure">&proxyConnectFailure.longDesc;</div>
         <div id="ed_contentEncodingError">&contentEncodingError.longDesc;</div>
         <div id="ed_unsafeContentType">&unsafeContentType.longDesc;</div>
         <div id="ed_nssFailure2">&nssFailure2.longDesc2;</div>
         <div id="ed_nssBadCert">&certerror.introPara;</div>
         <div id="ed_cspBlocked">&cspBlocked.longDesc;</div>
         <div id="ed_remoteXUL">&remoteXUL.longDesc;</div>
-        <div id="ed_corruptedContentError">&corruptedContentError.longDesc;</div>
+        <div id="ed_corruptedContentErrorv2">&corruptedContentErrorv2.longDesc;</div>
         <div id="ed_sslv3Used">&sslv3Used.longDesc2;</div>
         <div id="ed_weakCryptoUsed">&weakCryptoUsed.longDesc2;</div>
         <div id="ed_inadequateSecurityError">&inadequateSecurityError.longDesc;</div>
       </div>
     </div>
 
     <!-- PAGE CONTAINER (for styling purposes only) -->
     <div id="errorPageContainer" class="container">
old mode 100644
new mode 100755
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2563,16 +2563,17 @@ var gMenuButtonBadgeManager = {
   }
 };
 
 // Setup the hamburger button badges for updates, if enabled.
 var gMenuButtonUpdateBadge = {
   enabled: false,
   badgeWaitTime: 0,
   timer: null,
+  cancelObserverRegistered: false,
 
   init: function () {
     try {
       this.enabled = Services.prefs.getBoolPref("app.update.badge");
     } catch (e) {}
     if (this.enabled) {
       try {
         this.badgeWaitTime = Services.prefs.getIntPref("app.update.badgeWaitTime");
@@ -2587,16 +2588,20 @@ var gMenuButtonUpdateBadge = {
   uninit: function () {
     if (this.timer)
       this.timer.cancel();
     if (this.enabled) {
       Services.obs.removeObserver(this, "update-staged");
       Services.obs.removeObserver(this, "update-downloaded");
       this.enabled = false;
     }
+    if (this.cancelObserverRegistered) {
+      Services.obs.removeObserver(this, "update-canceled");
+      this.cancelObserverRegistered = false;
+    }
   },
 
   onMenuPanelCommand: function(event) {
     if (event.originalTarget.getAttribute("update-status") === "succeeded") {
       // restart the app
       let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
                        .createInstance(Ci.nsISupportsPRBool);
       Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
@@ -2607,61 +2612,76 @@ var gMenuButtonUpdateBadge = {
     } else {
       // open the page for manual update
       let url = Services.urlFormatter.formatURLPref("app.update.url.manual");
       openUILinkIn(url, "tab");
     }
   },
 
   observe: function (subject, topic, status) {
+    if (topic == "update-canceled") {
+      this.reset();
+      return;
+    }
     if (status == "failed") {
       // Background update has failed, let's show the UI responsible for
       // prompting the user to update manually.
+      this.uninit();
       this.displayBadge(false);
-      this.uninit();
       return;
     }
 
     // Give the user badgeWaitTime seconds to react before prompting.
     this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
     this.timer.initWithCallback(this, this.badgeWaitTime * 1000,
                                 this.timer.TYPE_ONE_SHOT);
     // The timer callback will call uninit() when it completes.
   },
 
   notify: function () {
     // If the update is successfully applied, or if the updater has fallen back
     // to non-staged updates, add a badge to the hamburger menu to indicate an
     // update will be applied once the browser restarts.
+    this.uninit();
     this.displayBadge(true);
-    this.uninit();
   },
 
   displayBadge: function (succeeded) {
     let status = succeeded ? "succeeded" : "failed";
     let badgeStatus = "update-" + status;
     gMenuButtonBadgeManager.addBadge(gMenuButtonBadgeManager.BADGEID_APPUPDATE, badgeStatus);
 
     let stringId;
     let updateButtonText;
     if (succeeded) {
       let brandBundle = document.getElementById("bundle_brand");
       let brandShortName = brandBundle.getString("brandShortName");
       stringId = "appmenu.restartNeeded.description";
       updateButtonText = gNavigatorBundle.getFormattedString(stringId,
                                                              [brandShortName]);
+      Services.obs.addObserver(this, "update-canceled", false);
+      this.cancelObserverRegistered = true;
     } else {
       stringId = "appmenu.updateFailed.description";
       updateButtonText = gNavigatorBundle.getString(stringId);
     }
 
     let updateButton = document.getElementById("PanelUI-update-status");
     updateButton.setAttribute("label", updateButtonText);
     updateButton.setAttribute("update-status", status);
     updateButton.hidden = false;
+  },
+
+  reset: function () {
+    gMenuButtonBadgeManager.removeBadge(
+      gMenuButtonBadgeManager.BADGEID_APPUPDATE);
+    let updateButton = document.getElementById("PanelUI-update-status");
+    updateButton.hidden = true;
+    this.uninit();
+    this.init();
   }
 };
 
 // Values for telemtery bins: see TLS_ERROR_REPORT_UI in Histograms.json
 const TLS_ERROR_REPORT_TELEMETRY_AUTO_CHECKED   = 2;
 const TLS_ERROR_REPORT_TELEMETRY_AUTO_UNCHECKED = 3;
 const TLS_ERROR_REPORT_TELEMETRY_MANUAL_SEND    = 4;
 const TLS_ERROR_REPORT_TELEMETRY_AUTO_SEND      = 5;
--- a/browser/components/sessionstore/TabStateCache.jsm
+++ b/browser/components/sessionstore/TabStateCache.jsm
@@ -56,28 +56,105 @@ var TabStateCacheInternal = {
    *         The cached data stored for the given |tab|
    *         or associated |browser|.
    */
   get: function (browserOrTab) {
     return this._data.get(browserOrTab.permanentKey);
   },
 
   /**
+   * Helper function used by update (see below). For message size
+   * optimization sometimes we don't update the whole session storage
+   * only the values those have been changed.
+   *
+   * @param data (object)
+   *        The cached data where we want to update the changes.
+   * @param change (object)
+   *        The actual changed values per domain.
+   */
+  updatePartialStorageChange: function (data, change) {
+    if (!data.storage) {
+      data.storage = {};
+    }
+
+    let storage = data.storage;
+    for (let domain of Object.keys(change)) {
+      for (let key of Object.keys(change[domain])) {
+        let value = change[domain][key];
+        if (value === null) {
+          if (storage[domain] && storage[domain][key]) {
+            delete storage[domain][key];
+          }
+        } else {
+          if (!storage[domain]) {
+            storage[domain] = {};
+          }
+          storage[domain][key] = value;
+        }
+      }
+    }
+  },
+
+  /**
+   * Helper function used by update (see below). For message size
+   * optimization sometimes we don't update the whole browser history
+   * only the current index and the tail of the history from a certain
+   * index (specified by change.fromIdx)
+   *
+   * @param data (object)
+   *        The cached data where we want to update the changes.
+   * @param change (object)
+   *        Object containing the tail of the history array, and
+   *        some additional metadata.
+   */
+  updatePartialHistoryChange: function (data, change) {
+    const kLastIndex = Number.MAX_SAFE_INTEGER - 1;
+
+    if (!data.history) {
+      data.history = { entries: [] };
+    }
+
+    let history = data.history;
+    for (let key of Object.keys(change)) {
+      if (key == "entries") {
+        if (change.fromIdx != kLastIndex) {
+          history.entries.splice(change.fromIdx + 1);
+          while (change.entries.length) {
+            history.entries.push(change.entries.shift());
+          }
+        }
+      } else if (key != "fromIndex") {
+        history[key] = change[key];
+      }
+    }
+  },
+
+  /**
    * Updates cached data for a given |tab| or associated |browser|.
    *
    * @param browserOrTab (xul:tab or xul:browser)
    *        The tab or browser belonging to the given tab data.
    * @param newData (object)
    *        The new data to be stored for the given |tab|
    *        or associated |browser|.
    */
   update: function (browserOrTab, newData) {
     let data = this._data.get(browserOrTab.permanentKey) || {};
 
     for (let key of Object.keys(newData)) {
+      if (key == "storagechange") {
+        this.updatePartialStorageChange(data, newData.storagechange);
+        continue;
+      }
+
+      if (key == "historychange") {
+        this.updatePartialHistoryChange(data, newData.historychange);
+        continue;
+      }
+
       let value = newData[key];
       if (value === null) {
         delete data[key];
       } else {
         data[key] = value;
       }
     }
 
--- a/browser/components/sessionstore/content/content-sessionStore.js
+++ b/browser/components/sessionstore/content/content-sessionStore.js
@@ -44,16 +44,19 @@ var gCurrentEpoch = 0;
 
 // A bound to the size of data to store for DOM Storage.
 const DOM_STORAGE_MAX_CHARS = 10000000; // 10M characters
 
 // This pref controls whether or not we send updates to the parent on a timeout
 // or not, and should only be used for tests or debugging.
 const TIMEOUT_DISABLED_PREF = "browser.sessionstore.debug.no_auto_updates";
 
+const kNoIndex = Number.MAX_SAFE_INTEGER;
+const kLastIndex = Number.MAX_SAFE_INTEGER - 1;
+
 /**
  * Returns a lazy function that will evaluate the given
  * function |fn| only once and cache its return value.
  */
 function createLazy(fn) {
   let cached = false;
   let cachedValue = null;
 
@@ -247,49 +250,89 @@ var SessionHistoryListener = {
   uninit: function () {
     let sessionHistory = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory;
     if (sessionHistory) {
       sessionHistory.removeSHistoryListener(this);
     }
   },
 
   collect: function () {
+    this._fromIdx = kNoIndex;
     if (docShell) {
       MessageQueue.push("history", () => SessionHistory.collect(docShell));
     }
   },
 
+  _fromIdx: kNoIndex,
+
+  // History can grow relatively big with the nested elements, so if we don't have to, we
+  // don't want to send the entire history all the time. For a simple optimization
+  // we keep track of the smallest index from after any change has occured and we just send
+  // the elements from that index. If something more complicated happens we just clear it
+  // and send the entire history. We always send the additional info like the current selected
+  // index (so for going back and forth between history entries we set the index to kLastIndex
+  // if nothing else changed send an empty array and the additonal info like the selected index)
+  collectFrom: function (idx) {
+    if (this._fromIdx <= idx) {
+      // If we already know that we need to update history fromn index N we can ignore any changes
+      // tha happened with an element with index larger than N.
+      // Note: initially we use kNoIndex which is MAX_SAFE_INTEGER which means we don't ignore anything
+      // here, and in case of navigation in the history back and forth we use kLastIndex which ignores
+      // only the subsequent navigations, but not any new elements added.
+      return;
+    }
+
+    this._fromIdx = idx;
+    MessageQueue.push("historychange", () => {
+      if (this._fromIdx === kNoIndex) {
+        return null;
+      }
+
+      let history = SessionHistory.collect(docShell);
+      if (kLastIndex == idx) {
+        history.entries = [];
+      } else {
+        history.entries.splice(0, this._fromIdx + 1);
+      }
+
+      history.fromIdx = this._fromIdx;
+
+      this._fromIdx = kNoIndex;
+      return history;
+    });
+  },
+
   handleEvent(event) {
     this.collect();
   },
 
   onFrameTreeCollected: function () {
     this.collect();
   },
 
   onFrameTreeReset: function () {
     this.collect();
   },
 
-  OnHistoryNewEntry: function (newURI) {
-    this.collect();
+  OnHistoryNewEntry: function (newURI, oldIndex) {
+    this.collectFrom(oldIndex);
   },
 
   OnHistoryGoBack: function (backURI) {
-    this.collect();
+    this.collectFrom(kLastIndex);
     return true;
   },
 
   OnHistoryGoForward: function (forwardURI) {
-    this.collect();
+    this.collectFrom(kLastIndex);
     return true;
   },
 
   OnHistoryGotoIndex: function (index, gotoURI) {
-    this.collect();
+    this.collectFrom(kLastIndex);
     return true;
   },
 
   OnHistoryPurge: function (numEntries) {
     this.collect();
     return true;
   },
 
@@ -491,17 +534,17 @@ var SessionStorageListener = {
   },
 
   uninit: function () {
     Services.obs.removeObserver(this, "browser:purge-domain-data");
   },
 
   handleEvent: function (event) {
     if (gFrameTree.contains(event.target)) {
-      this.collect();
+      this.collectFromEvent(event);
     }
   },
 
   observe: function () {
     // Collect data on the next tick so that any other observer
     // that needs to purge data can do its work first.
     setTimeout(() => this.collect(), 0);
   },
@@ -532,18 +575,57 @@ var SessionStorageListener = {
         let perKey = perHost[key];
         size += perKey.length;
       }
     }
 
     return size;
   },
 
+  // We don't want to send all the session storage data for all the frames
+  // for every change. So if only a few value changed we send them over as
+  // a "storagechange" event. If however for some reason before we send these
+  // changes we have to send over the entire sessions storage data, we just
+  // reset these changes.
+  _changes: undefined,
+
+  resetChanges: function () {
+    this._changes = undefined;
+  },
+
+  collectFromEvent: function (event) {
+    // TODO: we should take browser.sessionstore.dom_storage_limit into an account here.
+    if (docShell) {
+      let {url, key, newValue} = event;
+      let uri = Services.io.newURI(url, null, null);
+      let domain = uri.prePath;
+      if (!this._changes) {
+        this._changes = {};
+      }
+      if (!this._changes[domain]) {
+        this._changes[domain] = {};
+      }
+      this._changes[domain][key] = newValue;
+
+      MessageQueue.push("storagechange", () => {
+        let tmp = this._changes;
+        // If there were multiple changes we send them merged.
+        // First one will collect all the changes the rest of
+        // these messages will be ignored.
+        this.resetChanges();
+        return tmp;
+      });
+    }
+  },
+
   collect: function () {
     if (docShell) {
+      // We need the entire session storage, let's reset the pending individual change
+      // messages.
+      this.resetChanges();
       MessageQueue.push("storage", () => {
         let collected = SessionStorage.collect(docShell, gFrameTree);
 
         if (collected == null) {
           return collected;
         }
 
         let size = this.estimateStorageSize(collected);
@@ -722,17 +804,17 @@ var MessageQueue = {
     let data = {};
     let telemetry = {};
     for (let [key, func] of this._data) {
       let value = func();
       if (key == "telemetry") {
         for (let histogramId of Object.keys(value)) {
           telemetry[histogramId] = value[histogramId];
         }
-      } else {
+      } else if (value || (key != "storagechange" && key != "historychange")) {
         data[key] = value;
       }
     }
 
     this._data.clear();
 
     durationMs = Date.now() - durationMs;
     telemetry.FX_SESSION_RESTORE_CONTENT_COLLECT_DATA_LONGEST_OP_MS = durationMs;
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -30,16 +30,17 @@
 ; Equals Contents/Resources/ on Mac OS X and is equivalent to @BINPATH@ on other
 ; platforms.
 
 #filter substitution
 
 #ifdef XP_MACOSX
 ; Mac bundle stuff
 @APPNAME@/Contents/Info.plist
+@APPNAME@/Contents/Library/LaunchServices
 @APPNAME@/Contents/PkgInfo
 @RESPATH@/firefox.icns
 @RESPATH@/document.icns
 @RESPATH@/@LPROJ_ROOT@.lproj/*
 #endif
 
 [@AB_CD@]
 @RESPATH@/browser/chrome/@AB_CD@@JAREXT@
--- a/browser/locales/en-US/chrome/overrides/appstrings.properties
+++ b/browser/locales/en-US/chrome/overrides/appstrings.properties
@@ -30,15 +30,15 @@ externalProtocolPrompt=An external appli
 externalProtocolUnknown=<Unknown>
 externalProtocolChkMsg=Remember my choice for all links of this type.
 externalProtocolLaunchBtn=Launch application
 malwareBlocked=The site at %S has been reported as an attack site and has been blocked based on your security preferences.
 unwantedBlocked=The site at %S has been reported as serving unwanted software and has been blocked based on your security preferences.
 deceptiveBlocked=This web page at %S has been reported as a deceptive site and has been blocked based on your security preferences.
 forbiddenBlocked=The site at %S has been blocked by your browser configuration.
 cspBlocked=This page has a content security policy that prevents it from being loaded in this way.
-corruptedContentError=The site at %S has experienced a network protocol violation that cannot be repaired.
+corruptedContentErrorv2=The site at %S has experienced a network protocol violation that cannot be repaired.
 remoteXUL=This page uses an unsupported technology that is no longer available by default in Firefox.
 ## LOCALIZATION NOTE (sslv3Used) - Do not translate "%S".
 sslv3Used=Firefox cannot guarantee the safety of your data on %S because it uses SSLv3, a broken security protocol.
 ## LOCALIZATION NOTE (weakCryptoUsed) - Do not translate "%S".
 weakCryptoUsed=The owner of %S has configured their website improperly. To protect your information from being stolen, Firefox has not connected to this website.
 inadequateSecurityError=The website tried to negotiate an inadequate level of security.
--- a/browser/locales/en-US/chrome/overrides/netError.dtd
+++ b/browser/locales/en-US/chrome/overrides/netError.dtd
@@ -160,18 +160,18 @@ was trying to connect. -->
   <li>If your computer or network is protected by a firewall or proxy, make sure
     that &brandShortName; is permitted to access the Web.</li>
 </ul>
 ">
 
 <!ENTITY cspBlocked.title "Blocked by Content Security Policy">
 <!ENTITY cspBlocked.longDesc "<p>&brandShortName; prevented this page from loading in this way because the page has a content security policy that disallows it.</p>">
 
-<!ENTITY corruptedContentError.title "Corrupted Content Error">
-<!ENTITY corruptedContentError.longDesc "<p>The page you are trying to view cannot be shown because an error in the data transmission was detected.</p><ul><li>Please contact the website owners to inform them of this problem.</li></ul>">
+<!ENTITY corruptedContentErrorv2.title "Corrupted Content Error">
+<!ENTITY corruptedContentErrorv2.longDesc "<p>The page you are trying to view cannot be shown because an error in the data transmission was detected.</p><ul><li>Please contact the website owners to inform them of this problem.</li></ul>">
 
 
 <!ENTITY securityOverride.exceptionButtonLabel "Add Exception…">
 
 <!ENTITY errorReporting.automatic2 "Report errors like this to help Mozilla identify and block malicious sites">
 <!ENTITY errorReporting.learnMore "Learn more…">
 
 <!ENTITY remoteXUL.title "Remote XUL">
--- a/build/moz.configure/old.configure
+++ b/build/moz.configure/old.configure
@@ -246,17 +246,16 @@ def old_configure_options(*options):
     '--enable-synth-pico',
     '--enable-synth-speechd',
     '--enable-system-cairo',
     '--enable-system-extension-dirs',
     '--enable-system-ffi',
     '--enable-system-pixman',
     '--enable-system-sqlite',
     '--enable-tasktracer',
-    '--enable-tests',
     '--enable-thread-sanitizer',
     '--enable-trace-logging',
     '--enable-tree-freetype',
     '--enable-ui-locale',
     '--enable-universalchardet',
     '--enable-updater',
     '--enable-url-classifier',
     '--enable-valgrind',
--- a/build/variables.py
+++ b/build/variables.py
@@ -31,37 +31,41 @@ def get_program_output(*command):
 def get_hg_info(workdir):
     repo = get_program_output('hg', '-R', workdir, 'path', 'default')
     if repo:
         repo = repo.strip()
         if repo.startswith('ssh://'):
             repo = 'https://' + repo[6:]
         repo = repo.rstrip('/')
 
-    changeset = get_program_output(
-        'hg', '-R', workdir, 'parent', '--template={node}')
+    changeset = get_hg_changeset(workdir)
 
     return repo, changeset
 
 
+def get_hg_changeset(path):
+    return get_program_output('hg', '-R', path, 'parent', '--template={node}')
+
+
 def source_repo_header(output):
     # We allow the source repo and changeset to be specified via the
     # environment (see configure)
     import buildconfig
     repo = buildconfig.substs.get('MOZ_SOURCE_REPO')
     changeset = buildconfig.substs.get('MOZ_SOURCE_CHANGESET')
     source = ''
 
-    if bool(repo) != bool(changeset):
-        raise Exception('MOZ_SOURCE_REPO and MOZ_SOURCE_CHANGESET both must '
-                        'be set (or not set).')
-
     if not repo:
         if os.path.exists(os.path.join(buildconfig.topsrcdir, '.hg')):
             repo, changeset = get_hg_info(buildconfig.topsrcdir)
+    elif not changeset:
+        changeset = get_hg_changeset(buildconfig.topsrcdir)
+        if not changeset:
+            raise Exception('could not resolve changeset; '
+                            'try setting MOZ_SOURCE_CHANGESET')
 
     if changeset:
         output.write('#define MOZ_SOURCE_STAMP %s\n' % changeset)
 
     if repo and buildconfig.substs.get('MOZ_INCLUDE_SOURCE_INFO'):
         source = '%s/rev/%s' % (repo, changeset)
         output.write('#define MOZ_SOURCE_REPO %s\n' % repo)
         output.write('#define MOZ_SOURCE_URL %s\n' % source)
--- a/config/external/nss/nss.symbols
+++ b/config/external/nss/nss.symbols
@@ -685,16 +685,17 @@ SSL_ResetHandshake
 SSL_SetCanFalseStartCallback
 SSL_SetDowngradeCheckVersion
 SSL_SetNextProtoNego
 SSL_SetPKCS11PinArg
 SSL_SetSockPeerID
 SSL_SetSRTPCiphers
 SSL_SetStapledOCSPResponses
 SSL_SetURL
+SSL_ShutdownServerSessionIDCache
 SSL_SNISocketConfigHook
 SSL_VersionRangeGet
 SSL_VersionRangeGetDefault
 SSL_VersionRangeGetSupported
 SSL_VersionRangeSet
 SSL_VersionRangeSetDefault
 UTIL_SetForkState
 VFY_Begin
--- a/config/mozunit.py
+++ b/config/mozunit.py
@@ -68,24 +68,24 @@ class _MozTestResult(_TestResult):
     def addFailure(self, test, err):
         _TestResult.addFailure(self, test, err)
         self.printFail(test,err)
         self.stream.writeln("FAIL: {0}".format(self.getDescription(test)))
         self.stream.writeln(self.failures[-1][1])
 
     def printFail(self, test, err):
         exctype, value, tb = err
+        message = value.message.splitlines()[0]
         # Skip test runner traceback levels
         while tb and self._is_relevant_tb_level(tb):
             tb = tb.tb_next
-        if not tb:
-            self.stream.writeln("TEST-UNEXPECTED-FAIL | NO TRACEBACK |")
-        _f, _ln, _t = inspect.getframeinfo(tb)[:3]
-        self.printStatus('TEST-UNEXPECTED-FAIL', test,
-                         'line {0}: {1}'.format(_ln, value.message))
+        if tb:
+            _, ln, _ = inspect.getframeinfo(tb)[:3]
+            message = 'line {0}: {1}'.format(ln, message)
+        self.printStatus("TEST-UNEXPECTED-FAIL", test, message)
 
 
 class MozTestRunner(_TestRunner):
     def _makeResult(self):
         return _MozTestResult(self.stream, self.descriptions)
     def run(self, test):
         result = self._makeResult()
         test(result)
--- a/devtools/client/inspector/rules/test/browser_rules_completion-new-property_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_completion-new-property_02.js
@@ -18,17 +18,17 @@
 //  ]
 var testData = [
   ["d", {}, "display", 1, 3, false],
   ["VK_TAB", {}, "", -1, 43, true],
   ["VK_DOWN", {}, "block", 0, 43, true],
   ["n", {}, "none", -1, 0, true],
   ["VK_TAB", {shiftKey: true}, "display", -1, 0, true],
   ["VK_BACK_SPACE", {}, "", -1, 0, false],
-  ["o", {}, "overflow", 13, 16, false],
+  ["o", {}, "overflow", 13, 17, false],
   ["u", {}, "outline", 0, 5, false],
   ["VK_DOWN", {}, "outline-color", 1, 5, false],
   ["VK_TAB", {}, "none", -1, 0, true],
   ["r", {}, "rebeccapurple", 0, 6, true],
   ["VK_DOWN", {}, "red", 1, 6, true],
   ["VK_DOWN", {}, "rgb", 2, 6, true],
   ["VK_DOWN", {}, "rgba", 3, 6, true],
   ["VK_DOWN", {}, "rosybrown", 4, 6, true],
--- a/devtools/client/responsivedesign/responsivedesign.jsm
+++ b/devtools/client/responsivedesign/responsivedesign.jsm
@@ -597,29 +597,29 @@ ResponsiveUI.prototype = {
     let volumeButtons = this.chromeDoc.createElement("vbox");
     volumeButtons.className = "devtools-responsiveui-volume-buttons";
     volumeButtons.setAttribute("top", 0);
     volumeButtons.setAttribute("left", 0);
 
     let volumeUp = this.chromeDoc.createElement("button");
     volumeUp.className = "devtools-responsiveui-volume-up-button";
     volumeUp.addEventListener("mousedown", () => {
-      SystemAppProxy.dispatchKeyboardEvent("keydown", {key: "VolumeUp"});
+      SystemAppProxy.dispatchKeyboardEvent("keydown", {key: "AudioVolumeUp"});
     });
     volumeUp.addEventListener("mouseup", () => {
-      SystemAppProxy.dispatchKeyboardEvent("keyup", {key: "VolumeUp"});
+      SystemAppProxy.dispatchKeyboardEvent("keyup", {key: "AudioVolumeUp"});
     });
 
     let volumeDown = this.chromeDoc.createElement("button");
     volumeDown.className = "devtools-responsiveui-volume-down-button";
     volumeDown.addEventListener("mousedown", () => {
-      SystemAppProxy.dispatchKeyboardEvent("keydown", {key: "VolumeDown"});
+      SystemAppProxy.dispatchKeyboardEvent("keydown", {key: "AudioVolumeDown"});
     });
     volumeDown.addEventListener("mouseup", () => {
-      SystemAppProxy.dispatchKeyboardEvent("keyup", {key: "VolumeDown"});
+      SystemAppProxy.dispatchKeyboardEvent("keyup", {key: "AudioVolumeDown"});
     });
 
     volumeButtons.appendChild(volumeUp);
     volumeButtons.appendChild(volumeDown);
     this.stack.appendChild(volumeButtons);
 
     let bottomToolbar = this.chromeDoc.createElement("toolbar");
     bottomToolbar.className = "devtools-responsiveui-hardware-buttons";
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -5061,21 +5061,21 @@ nsDocShell::DisplayLoadError(nsresult aE
         error.AssignLiteral("remoteXUL");
         break;
       case NS_ERROR_UNSAFE_CONTENT_TYPE:
         // Channel refused to load from an unrecognized content type.
         error.AssignLiteral("unsafeContentType");
         break;
       case NS_ERROR_CORRUPTED_CONTENT:
         // Broken Content Detected. e.g. Content-MD5 check failure.
-        error.AssignLiteral("corruptedContentError");
+        error.AssignLiteral("corruptedContentErrorv2");
         break;
       case NS_ERROR_INTERCEPTION_FAILED:
         // ServiceWorker intercepted request, but something went wrong.
-        error.AssignLiteral("corruptedContentError");
+        error.AssignLiteral("corruptedContentErrorv2");
         break;
       case NS_ERROR_NET_INADEQUATE_SECURITY:
         // Server negotiated bad TLS for HTTP/2.
         error.AssignLiteral("inadequateSecurityError");
         addHostPort = true;
         break;
       default:
         break;
--- a/docshell/resources/content/netError.xhtml
+++ b/docshell/resources/content/netError.xhtml
@@ -302,17 +302,17 @@
         <h1 id="et_proxyResolveFailure">&proxyResolveFailure.title;</h1>
         <h1 id="et_proxyConnectFailure">&proxyConnectFailure.title;</h1>
         <h1 id="et_contentEncodingError">&contentEncodingError.title;</h1>
         <h1 id="et_unsafeContentType">&unsafeContentType.title;</h1>
         <h1 id="et_nssFailure2">&nssFailure2.title;</h1>
         <h1 id="et_nssBadCert">&nssBadCert.title;</h1>
         <h1 id="et_cspBlocked">&cspBlocked.title;</h1>
         <h1 id="et_remoteXUL">&remoteXUL.title;</h1>
-        <h1 id="et_corruptedContentError">&corruptedContentError.title;</h1>
+        <h1 id="et_corruptedContentErrorv2">&corruptedContentErrorv2.title;</h1>
         <h1 id="et_inadequateSecurityError">&inadequateSecurityError.title;</h1>
       </div>
       <div id="errorDescriptionsContainer">
         <div id="ed_generic">&generic.longDesc;</div>
         <div id="ed_dnsNotFound">&dnsNotFound.longDesc;</div>
         <div id="ed_fileNotFound">&fileNotFound.longDesc;</div>
         <div id="ed_fileAccessDenied">&fileAccessDenied.longDesc;</div>
         <div id="ed_malformedURI">&malformedURI.longDesc;</div>
@@ -329,17 +329,17 @@
         <div id="ed_proxyResolveFailure">&proxyResolveFailure.longDesc;</div>
         <div id="ed_proxyConnectFailure">&proxyConnectFailure.longDesc;</div>
         <div id="ed_contentEncodingError">&contentEncodingError.longDesc;</div>
         <div id="ed_unsafeContentType">&unsafeContentType.longDesc;</div>
         <div id="ed_nssFailure2">&nssFailure2.longDesc2;</div>
         <div id="ed_nssBadCert">&nssBadCert.longDesc2;</div>
         <div id="ed_cspBlocked">&cspBlocked.longDesc;</div>
         <div id="ed_remoteXUL">&remoteXUL.longDesc;</div>
-        <div id="ed_corruptedContentError">&corruptedContentError.longDesc;</div>
+        <div id="ed_corruptedContentErrorv2">&corruptedContentErrorv2.longDesc;</div>
         <div id="ed_inadequateSecurityError">&inadequateSecurityError.longDesc;</div>
       </div>
     </div>
 
     <!-- PAGE CONTAINER (for styling purposes only) -->
     <div id="errorPageContainer">
 
       <!-- Error Title -->
--- a/docshell/shistory/nsISHistoryListener.idl
+++ b/docshell/shistory/nsISHistoryListener.idl
@@ -25,18 +25,19 @@ interface nsIURI;
 interface nsISHistoryListener : nsISupports 
 {
   /**
    * Called when a new document is added to session history. New documents are
    * added to session history by docshell when new pages are loaded in a frame
    * or content area, for example via nsIWebNavigation::loadURI()
    *
    * @param aNewURI     The URI of the document to be added to session history.
+   * @param aOldIndex   The index of the current history item before the operation.
    */
-   void OnHistoryNewEntry(in nsIURI aNewURI);
+   void OnHistoryNewEntry(in nsIURI aNewURI, in long aOldIndex);
 
   /**
    * Called when navigating to a previous session history entry, for example
    * due to a nsIWebNavigation::goBack() call.
    *
    * @param aBackURI    The URI of the session history entry being navigated to.
    * @return            Whether the operation can proceed.
    */
--- a/docshell/shistory/nsSHistory.cpp
+++ b/docshell/shistory/nsSHistory.cpp
@@ -397,17 +397,17 @@ nsSHistory::AddEntry(nsISHEntry* aSHEntr
   }
 
   nsCOMPtr<nsISHTransaction> txn(
     do_CreateInstance(NS_SHTRANSACTION_CONTRACTID));
   NS_ENSURE_TRUE(txn, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIURI> uri;
   aSHEntry->GetURI(getter_AddRefs(uri));
-  NOTIFY_LISTENERS(OnHistoryNewEntry, (uri));
+  NOTIFY_LISTENERS(OnHistoryNewEntry, (uri, currentIndex));
 
   // If a listener has changed mIndex, we need to get currentTxn again,
   // otherwise we'll be left at an inconsistent state (see bug 320742)
   if (currentIndex != mIndex) {
     GetTransactionAtIndex(mIndex, getter_AddRefs(currentTxn));
   }
 
   // Set the ShEntry and parent for the transaction. setting the
--- a/dom/base/ScreenOrientation.cpp
+++ b/dom/base/ScreenOrientation.cpp
@@ -548,17 +548,18 @@ ScreenOrientation::Notify(const hal::Scr
 }
 
 void
 ScreenOrientation::UpdateActiveOrientationLock(ScreenOrientationInternal aOrientation)
 {
   if (aOrientation == eScreenOrientation_None) {
     hal::UnlockScreenOrientation();
   } else {
-    hal::LockScreenOrientation(aOrientation);
+    bool rv = hal::LockScreenOrientation(aOrientation);
+    NS_WARN_IF(!rv);
   }
 }
 
 void
 ScreenOrientation::DispatchChangeEvent()
 {
   nsresult rv = DispatchTrustedEvent(NS_LITERAL_STRING("change"));
   NS_WARN_IF(NS_FAILED(rv));
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -439,16 +439,17 @@ LOCAL_INCLUDES += [
     '/dom/xml',
     '/dom/xslt/xpath',
     '/dom/xul',
     '/gfx/2d',
     '/image',
     '/js/xpconnect/src',
     '/js/xpconnect/wrappers',
     '/layout/base',
+    '/layout/forms',
     '/layout/generic',
     '/layout/style',
     '/layout/svg',
     '/layout/xul',
     '/netwerk/base',
     '/security/manager/ssl',
     '/widget',
     '/xpcom/ds',
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -3051,34 +3051,38 @@ nsContentUtils::DocumentInactiveForImage
 }
 
 imgLoader*
 nsContentUtils::GetImgLoaderForDocument(nsIDocument* aDoc)
 {
   NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aDoc), nullptr);
 
   if (!aDoc) {
-    return imgLoader::Singleton();
+    return imgLoader::NormalLoader();
   }
   bool isPrivate = IsInPrivateBrowsing(aDoc);
-  return isPrivate ? imgLoader::PBSingleton() : imgLoader::Singleton();
+  return isPrivate ? imgLoader::PrivateBrowsingLoader()
+                   : imgLoader::NormalLoader();
 }
 
 // static
 imgLoader*
 nsContentUtils::GetImgLoaderForChannel(nsIChannel* aChannel,
                                        nsIDocument* aContext)
 {
   NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aContext), nullptr);
 
-  if (!aChannel)
-    return imgLoader::Singleton();
+  if (!aChannel) {
+    return imgLoader::NormalLoader();
+  }
   nsCOMPtr<nsILoadContext> context;
   NS_QueryNotificationCallbacks(aChannel, context);
-  return context && context->UsePrivateBrowsing() ? imgLoader::PBSingleton() : imgLoader::Singleton();
+  return context && context->UsePrivateBrowsing() ?
+                      imgLoader::PrivateBrowsingLoader() :
+                      imgLoader::NormalLoader();
 }
 
 // static
 bool
 nsContentUtils::IsImageInCache(nsIURI* aURI, nsIDocument* aDocument)
 {
     imgILoader* loader = GetImgLoaderForDocument(aDocument);
     nsCOMPtr<imgICache> cache = do_QueryInterface(loader);
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2629,25 +2629,16 @@ private:
 
   static nsIParserService *sParserService;
 
   static nsNameSpaceManager *sNameSpaceManager;
 
   static nsIIOService *sIOService;
   static nsIUUIDGenerator *sUUIDGenerator;
 
-  static bool sImgLoaderInitialized;
-  static void InitImgLoader();
-
-  // The following four members are initialized lazily
-  static imgLoader* sImgLoader;
-  static imgLoader* sPrivateImgLoader;
-  static imgICache* sImgCache;
-  static imgICache* sPrivateImgCache;
-
   static nsIConsoleService* sConsoleService;
 
   static nsDataHashtable<nsISupportsHashKey, EventNameMapping>* sAtomEventTable;
   static nsDataHashtable<nsStringHashKey, EventNameMapping>* sStringEventTable;
   static nsCOMArray<nsIAtom>* sUserDefinedEvents;
 
   static nsIStringBundleService* sStringBundleService;
   static nsIStringBundle* sStringBundles[PropertiesFile_COUNT];
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -37,16 +37,17 @@
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIPrincipal.h"
 #include "nsIObserverService.h"
 #include "nsIObjectFrame.h"
 #include "nsBindingManager.h"
 #include "nsStyleCoord.h"
 #include "TabChild.h"
 #include "nsFrameLoader.h"
+#include "nsNumberControlFrame.h"
 
 #include "mozilla/ContentEvents.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/LookAndFeel.h"
@@ -305,16 +306,34 @@ nsFocusManager::GetFocusedDescendant(nsP
 
   return currentContent;
 }
 
 // static
 nsIContent*
 nsFocusManager::GetRedirectedFocus(nsIContent* aContent)
 {
+  // For input number, redirect focus to our anonymous text control.
+  if (aContent->IsHTMLElement(nsGkAtoms::input)) {
+    bool typeIsNumber =
+      static_cast<dom::HTMLInputElement*>(aContent)->GetType() ==
+        NS_FORM_INPUT_NUMBER;
+
+    if (typeIsNumber) {
+      nsNumberControlFrame* numberControlFrame =
+        do_QueryFrame(aContent->GetPrimaryFrame());
+
+      if (numberControlFrame) {
+        HTMLInputElement* textControl =
+          numberControlFrame->GetAnonTextControl();
+        return static_cast<nsIContent*>(textControl);
+      }
+    }
+  }
+
 #ifdef MOZ_XUL
   if (aContent->IsXULElement()) {
     nsCOMPtr<nsIDOMNode> inputField;
 
     nsCOMPtr<nsIDOMXULTextBoxElement> textbox = do_QueryInterface(aContent);
     if (textbox) {
       textbox->GetInputField(getter_AddRefs(inputField));
     }
@@ -1500,18 +1519,18 @@ nsFocusManager::IsNonFocusableRoot(nsICo
 }
 
 nsIContent*
 nsFocusManager::CheckIfFocusable(nsIContent* aContent, uint32_t aFlags)
 {
   if (!aContent)
     return nullptr;
 
-  // this is a special case for some XUL elements where an anonymous child is
-  // actually focusable and not the element itself.
+  // this is a special case for some XUL elements or input number, where an
+  // anonymous child is actually focusable and not the element itself.
   nsIContent* redirectedFocus = GetRedirectedFocus(aContent);
   if (redirectedFocus)
     return CheckIfFocusable(redirectedFocus, aFlags);
 
   nsCOMPtr<nsIDocument> doc = aContent->GetComposedDoc();
   // can't focus elements that are not in documents
   if (!doc) {
     LOGCONTENT("Cannot focus %s because content not in document", aContent)
--- a/dom/base/nsFocusManager.h
+++ b/dom/base/nsFocusManager.h
@@ -109,17 +109,17 @@ public:
    * aWindow and aFocusedWindow must both be non-null.
    */
   static nsIContent* GetFocusedDescendant(nsPIDOMWindowOuter* aWindow, bool aDeep,
                                           nsPIDOMWindowOuter** aFocusedWindow);
 
   /**
    * Returns the content node that focus will be redirected to if aContent was
    * focused. This is used for the special case of certain XUL elements such
-   * as textboxes which redirect focus to an anonymous child.
+   * as textboxes or input number which redirect focus to an anonymous child.
    *
    * aContent must be non-null.
    *
    * XXXndeakin this should be removed eventually but I want to do that as
    * followup work.
    */
   static nsIContent* GetRedirectedFocus(nsIContent* aContent);
 
--- a/dom/base/test/chrome/window_nsITextInputProcessor.xul
+++ b/dom/base/test/chrome/window_nsITextInputProcessor.xul
@@ -2765,19 +2765,19 @@ function runKeyTests()
     { key: "F19",                       keyCode: KeyboardEvent.DOM_VK_F19 },
     { key: "F20",                       keyCode: KeyboardEvent.DOM_VK_F20 },
     { key: "F21",                       keyCode: KeyboardEvent.DOM_VK_F21 },
     { key: "F22",                       keyCode: KeyboardEvent.DOM_VK_F22 },
     { key: "F23",                       keyCode: KeyboardEvent.DOM_VK_F23 },
     { key: "F24",                       keyCode: KeyboardEvent.DOM_VK_F24 },
     { key: "NumLock",                   keyCode: KeyboardEvent.DOM_VK_NUM_LOCK,           isModifier: true, isLockableModifier: true },
     { key: "ScrollLock",                keyCode: KeyboardEvent.DOM_VK_SCROLL_LOCK,        isModifier: true, isLockableModifier: true },
-    { key: "VolumeMute",                keyCode: KeyboardEvent.DOM_VK_VOLUME_MUTE },
-    { key: "VolumeDown",                keyCode: KeyboardEvent.DOM_VK_VOLUME_DOWN },
-    { key: "VolumeUp",                  keyCode: KeyboardEvent.DOM_VK_VOLUME_UP },
+    { key: "AudioVolumeMute",           keyCode: KeyboardEvent.DOM_VK_VOLUME_MUTE },
+    { key: "AudioVolumeDown",           keyCode: KeyboardEvent.DOM_VK_VOLUME_DOWN },
+    { key: "AudioVolumeUp",             keyCode: KeyboardEvent.DOM_VK_VOLUME_UP },
     { key: "Meta",                      keyCode: KeyboardEvent.DOM_VK_META,               isModifier: true },
     { key: "AltGraph",                  keyCode: KeyboardEvent.DOM_VK_ALTGR,              isModifier: true },
     { key: "Attn",                      keyCode: KeyboardEvent.DOM_VK_ATTN },
     { key: "CrSel",                     keyCode: KeyboardEvent.DOM_VK_CRSEL },
     { key: "ExSel",                     keyCode: KeyboardEvent.DOM_VK_EXSEL },
     { key: "EraseEof",                  keyCode: KeyboardEvent.DOM_VK_EREOF },
     { key: "Play",                      keyCode: KeyboardEvent.DOM_VK_PLAY },
     { key: "ZoomToggle",                keyCode: KeyboardEvent.DOM_VK_ZOOM },
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -1668,17 +1668,17 @@ BrowserElementChild.prototype = {
             return;
           case Cr.NS_ERROR_REMOTE_XUL :
             sendAsyncMsg('error', { type: 'remoteXUL' });
             return;
           case Cr.NS_ERROR_UNSAFE_CONTENT_TYPE :
             sendAsyncMsg('error', { type: 'unsafeContentType' });
             return;
           case Cr.NS_ERROR_CORRUPTED_CONTENT :
-            sendAsyncMsg('error', { type: 'corruptedContentError' });
+            sendAsyncMsg('error', { type: 'corruptedContentErrorv2' });
             return;
 
           default:
             // getErrorClass() will throw if the error code passed in is not a NSS
             // error code.
             try {
               let nssErrorsService = Cc['@mozilla.org/nss_errors_service;1']
                                        .getService(Ci.nsINSSErrorsService);
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -21,16 +21,17 @@
 #include "mozilla/JSEventHandler.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/EventTargetBinding.h"
+#include "mozilla/dom/TouchEvent.h"
 #include "mozilla/TimelineConsumers.h"
 #include "mozilla/EventTimelineMarker.h"
 
 #include "EventListenerService.h"
 #include "nsCOMArray.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 #include "nsDOMCID.h"
@@ -1709,22 +1710,35 @@ bool
 EventListenerManager::IsApzAwareListener(Listener* aListener)
 {
   return !aListener->mFlags.mPassive && IsApzAwareEvent(aListener->mTypeAtom);
 }
 
 bool
 EventListenerManager::IsApzAwareEvent(nsIAtom* aEvent)
 {
-  return aEvent == nsGkAtoms::ontouchstart ||
-         aEvent == nsGkAtoms::ontouchmove ||
-         aEvent == nsGkAtoms::onwheel ||
-         aEvent == nsGkAtoms::onDOMMouseScroll ||
-         aEvent == nsHtml5Atoms::onmousewheel ||
-         aEvent == nsGkAtoms::onMozMousePixelScroll;
+  if (aEvent == nsGkAtoms::onwheel ||
+      aEvent == nsGkAtoms::onDOMMouseScroll ||
+      aEvent == nsHtml5Atoms::onmousewheel ||
+      aEvent == nsGkAtoms::onMozMousePixelScroll) {
+    return true;
+  }
+  // In theory we should schedule a repaint if the touch event pref changes,
+  // because the event regions might be out of date. In practice that seems like
+  // overkill because users generally shouldn't be flipping this pref, much
+  // less expecting touch listeners on the page to immediately start preventing
+  // scrolling without so much as a repaint. Tests that we write can work
+  // around this constraint easily enough.
+  if (TouchEvent::PrefEnabled()) {
+    if (aEvent == nsGkAtoms::ontouchstart ||
+        aEvent == nsGkAtoms::ontouchmove) {
+      return true;
+    }
+  }
+  return false;
 }
 
 already_AddRefed<nsIScriptGlobalObject>
 EventListenerManager::GetScriptGlobalAndDocument(nsIDocument** aDoc)
 {
   nsCOMPtr<nsINode> node(do_QueryInterface(mTarget));
   nsCOMPtr<nsIDocument> doc;
   nsCOMPtr<nsIScriptGlobalObject> global;
--- a/dom/events/KeyNameList.h
+++ b/dom/events/KeyNameList.h
@@ -24,21 +24,23 @@
  * Special Key Values
  *****************************************************************************/
 DEFINE_KEYNAME_WITH_SAME_NAME(Unidentified)
 
 /******************************************************************************
  * Our Internal Key Values (must have "Moz" prefix)
  *****************************************************************************/
 DEFINE_KEYNAME_INTERNAL(PrintableKey, "MozPrintableKey")
+DEFINE_KEYNAME_INTERNAL(SoftLeft, "MozSoftLeft")
+DEFINE_KEYNAME_INTERNAL(SoftRight, "MozSoftRight")
+
+#ifdef MOZ_B2G
 DEFINE_KEYNAME_INTERNAL(HomeScreen, "MozHomeScreen")
 DEFINE_KEYNAME_INTERNAL(CameraFocusAdjust, "MozCameraFocusAdjust")
-DEFINE_KEYNAME_INTERNAL(PhoneCall, "MozPhoneCall")
-DEFINE_KEYNAME_INTERNAL(SoftLeft, "MozSoftLeft")
-DEFINE_KEYNAME_INTERNAL(SoftRight, "MozSoftRight")
+#endif // #ifdef MOZ_B2G
 
 /******************************************************************************
  * Modifier Keys
  *****************************************************************************/
 DEFINE_KEYNAME_WITH_SAME_NAME(Alt)
 DEFINE_KEYNAME_WITH_SAME_NAME(AltGraph)
 DEFINE_KEYNAME_WITH_SAME_NAME(CapsLock)
 DEFINE_KEYNAME_WITH_SAME_NAME(Control)
@@ -243,19 +245,26 @@ DEFINE_KEYNAME_WITH_SAME_NAME(AudioBalan
 DEFINE_KEYNAME_WITH_SAME_NAME(AudioBassBoostDown)
 DEFINE_KEYNAME_WITH_SAME_NAME(AudioBassBoostToggle)
 DEFINE_KEYNAME_WITH_SAME_NAME(AudioBassBoostUp)
 DEFINE_KEYNAME_WITH_SAME_NAME(AudioFaderFront)
 DEFINE_KEYNAME_WITH_SAME_NAME(AudioFaderRear)
 DEFINE_KEYNAME_WITH_SAME_NAME(AudioSurroundModeNext)
 DEFINE_KEYNAME_WITH_SAME_NAME(AudioTrebleDown)
 DEFINE_KEYNAME_WITH_SAME_NAME(AudioTrebleUp)
+#ifndef MOZ_B2G
+DEFINE_KEYNAME_WITH_SAME_NAME(AudioVolumeDown)
+DEFINE_KEYNAME_WITH_SAME_NAME(AudioVolumeUp)
+DEFINE_KEYNAME_WITH_SAME_NAME(AudioVolumeMute)
+#else
+// Temporarily, remaining for B2G
 DEFINE_KEYNAME_WITH_SAME_NAME(VolumeDown)
 DEFINE_KEYNAME_WITH_SAME_NAME(VolumeUp)
 DEFINE_KEYNAME_WITH_SAME_NAME(VolumeMute)
+#endif
 DEFINE_KEYNAME_WITH_SAME_NAME(MicrophoneToggle)
 DEFINE_KEYNAME_WITH_SAME_NAME(MicrophoneVolumeDown)
 DEFINE_KEYNAME_WITH_SAME_NAME(MicrophoneVolumeUp)
 DEFINE_KEYNAME_WITH_SAME_NAME(MicrophoneVolumeMute)
 
 /******************************************************************************
  * Speech Keys
  *****************************************************************************/
--- a/dom/events/TouchEvent.cpp
+++ b/dom/events/TouchEvent.cpp
@@ -160,38 +160,43 @@ TouchEvent::ChangedTouches()
   }
   return mChangedTouches;
 }
 
 // static
 bool
 TouchEvent::PrefEnabled(JSContext* aCx, JSObject* aGlobal)
 {
+  static bool sPrefCached = false;
+  static int32_t sPrefCacheValue = 0;
+
+  if (!sPrefCached) {
+    sPrefCached = true;
+    Preferences::AddIntVarCache(&sPrefCacheValue, "dom.w3c_touch_events.enabled");
+  }
+
   bool prefValue = false;
-  int32_t flag = 0;
-  if (NS_SUCCEEDED(Preferences::GetInt("dom.w3c_touch_events.enabled", &flag))) {
-    if (flag == 2) {
+  if (sPrefCacheValue == 2) {
 #if defined(MOZ_B2G) || defined(MOZ_WIDGET_ANDROID)
-      // Touch support is always enabled on B2G and android.
-      prefValue = true;
+    // Touch support is always enabled on B2G and android.
+    prefValue = true;
 #elif defined(XP_WIN) || MOZ_WIDGET_GTK == 3
-      static bool sDidCheckTouchDeviceSupport = false;
-      static bool sIsTouchDeviceSupportPresent = false;
-      // On Windows and GTK3 we auto-detect based on device support.
-      if (!sDidCheckTouchDeviceSupport) {
-        sDidCheckTouchDeviceSupport = true;
-        sIsTouchDeviceSupportPresent = WidgetUtils::IsTouchDeviceSupportPresent();
-      }
-      prefValue = sIsTouchDeviceSupportPresent;
+    static bool sDidCheckTouchDeviceSupport = false;
+    static bool sIsTouchDeviceSupportPresent = false;
+    // On Windows and GTK3 we auto-detect based on device support.
+    if (!sDidCheckTouchDeviceSupport) {
+      sDidCheckTouchDeviceSupport = true;
+      sIsTouchDeviceSupportPresent = WidgetUtils::IsTouchDeviceSupportPresent();
+    }
+    prefValue = sIsTouchDeviceSupportPresent;
 #else
-      prefValue = false;
+    prefValue = false;
 #endif
-    } else {
-      prefValue = !!flag;
-    }
+  } else {
+    prefValue = !!sPrefCacheValue;
   }
   if (prefValue) {
     nsContentUtils::InitializeTouchEventTable();
   }
   return prefValue;
 }
 
 // static
--- a/dom/html/test/forms/mochitest.ini
+++ b/dom/html/test/forms/mochitest.ini
@@ -46,16 +46,17 @@ skip-if = os == "android" || appname == 
 [test_input_number_mouse_events.html]
 # Not run on Firefox OS and Firefox for Android where the spin buttons are hidden:
 skip-if = os == "android" || appname == "b2g" || buildapp == "mulet"
 [test_input_number_rounding.html]
 skip-if = os == "android" || buildapp == "mulet"
 [test_input_number_validation.html]
 # We don't build ICU for Firefox for Android or Firefox OS:
 skip-if = os == "android" || appname == "b2g"
+[test_input_number_focus.html]
 [test_input_range_attr_order.html]
 [test_input_range_key_events.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_input_range_mouse_and_touch_events.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure; bug 926546
 [test_input_range_rounding.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_input_sanitization.html]
new file mode 100644
--- /dev/null
+++ b/dom/html/test/forms/test_input_number_focus.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1268556
+-->
+<head>
+  <title>Test focus behaviour for &lt;input type='number'&gt;</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1268556">Mozilla Bug 1268556</a>
+<p id="display"></p>
+<div id="content">
+  <input id="input" type="number">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/**
+ * Test for Bug 1268556.
+ * This test checks that when focusing on an input type=number, the focus is
+ * redirected to the anonymous text control, but the document.activeElement
+ * still returns the <input type=number>.
+ **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+  test();
+  SimpleTest.finish();
+});
+
+function test() {
+  var number = document.getElementById("input");
+  number.focus();
+
+  // The active element returns the input type=number.
+  var activeElement = document.activeElement;
+  is (activeElement, number, "activeElement should be the number element");
+  is (activeElement.localName, "input", "activeElement should be an input element");
+  is (activeElement.getAttribute("type"), "number", "activeElement should of type number");
+
+  // Use FocusManager to check that the actual focus is on the anonymous
+  // text control.
+  var fm = SpecialPowers.Cc["@mozilla.org/focus-manager;1"]
+                        .getService(SpecialPowers.Ci.nsIFocusManager);
+  var focusedElement = fm.focusedElement;
+  is (focusedElement.localName, "input", "focusedElement should be an input element");
+  is (focusedElement.getAttribute("type"), "text", "focusedElement should of type text");
+}
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/html/test/test_bug369370.html
+++ b/dom/html/test/test_bug369370.html
@@ -1,17 +1,17 @@
 <!DOCTYPE html>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=369370
 -->
 <head>
   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
   <title>Test for Bug 369370</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
   <script type="text/javascript">
     /*
      * Test strategy:
      */
     function makeClickFor(x, y) {
@@ -136,16 +136,16 @@ https://bugzilla.mozilla.org/show_bug.cg
 
         test6();
     }
     var kidWin;
     var kidDoc;
 
     SimpleTest.waitForExplicitFinish();
     SpecialPowers.pushPrefEnv({"set":[["browser.enable_automatic_image_resizing", true]]}, function() {
-       kidWin = window.open("bug369370-popup.png", "bug369370", "width=400,height=300");
+       kidWin = window.open("bug369370-popup.png", "bug369370", "width=400,height=300,scrollbars=no");
        // will init onload
        ok(kidWin, "opened child window");
        kidWin.onload = childLoaded;
     });
   </script>
 </body>
 </html>
--- a/dom/inputmethod/forms.js
+++ b/dom/inputmethod/forms.js
@@ -197,22 +197,31 @@ function guessKeyNameFromKeyCode(Keyboar
     case KeyboardEvent.DOM_VK_F23:
       return "F23";
     case KeyboardEvent.DOM_VK_F24:
       return "F24";
     case KeyboardEvent.DOM_VK_NUM_LOCK:
       return "NumLock";
     case KeyboardEvent.DOM_VK_SCROLL_LOCK:
       return "ScrollLock";
+#ifndef MOZ_B2G
+    case KeyboardEvent.DOM_VK_VOLUME_MUTE:
+      return "AudioVolumeMute";
+    case KeyboardEvent.DOM_VK_VOLUME_DOWN:
+      return "AudioVolumeDown";
+    case KeyboardEvent.DOM_VK_VOLUME_UP:
+      return "AudioVolumeUp";
+#else
     case KeyboardEvent.DOM_VK_VOLUME_MUTE:
       return "VolumeMute";
     case KeyboardEvent.DOM_VK_VOLUME_DOWN:
       return "VolumeDown";
     case KeyboardEvent.DOM_VK_VOLUME_UP:
       return "VolumeUp";
+#endif
     case KeyboardEvent.DOM_VK_META:
       return "Meta";
     case KeyboardEvent.DOM_VK_ALTGR:
       return "AltGraph";
     case KeyboardEvent.DOM_VK_ATTN:
       return "Attn";
     case KeyboardEvent.DOM_VK_CRSEL:
       return "CrSel";
--- a/dom/inputmethod/jar.mn
+++ b/dom/inputmethod/jar.mn
@@ -1,6 +1,6 @@
 # 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/.
 
 toolkit.jar:
-  content/global/forms.js                      (forms.js)
+* content/global/forms.js                      (forms.js)
--- a/dom/locales/en-US/chrome/appstrings.properties
+++ b/dom/locales/en-US/chrome/appstrings.properties
@@ -29,13 +29,13 @@ externalProtocolPrompt=An external appli
 externalProtocolUnknown=<Unknown>
 externalProtocolChkMsg=Remember my choice for all links of this type.
 externalProtocolLaunchBtn=Launch application
 malwareBlocked=The site at %S has been reported as an attack site and has been blocked based on your security preferences.
 unwantedBlocked=The site at %S has been reported as serving unwanted software and has been blocked based on your security preferences.
 deceptiveBlocked=This web page at %S has been reported as a deceptive site and has been blocked based on your security preferences.
 forbiddenBlocked=The site at %S has been blocked by your browser configuration.
 cspBlocked=This page has a content security policy that prevents it from being loaded in this way.
-corruptedContentError=The site at %S has experienced a network protocol violation that cannot be repaired.
+corruptedContentErrorv2=The site at %S has experienced a network protocol violation that cannot be repaired.
 remoteXUL=This page uses an unsupported technology that is no longer available by default.
 sslv3Used=The safety of your data on %S could not be guaranteed because it uses SSLv3, a broken security protocol.
 weakCryptoUsed=The owner of %S has configured their website improperly. To protect your information from being stolen, the connection to this website has not been established.
 inadequateSecurityError=The website tried to negotiate an inadequate level of security.
--- a/dom/locales/en-US/chrome/netError.dtd
+++ b/dom/locales/en-US/chrome/netError.dtd
@@ -82,18 +82,18 @@
 <!ENTITY securityOverride.warningContent "
 <p>You should not add an exception if you are using an internet connection that you do not trust completely or if you are not used to seeing a warning for this server.</p>
 <p>If you still wish to add an exception for this site, you can do so in your advanced encryption settings.</p>
 ">
 
 <!ENTITY cspBlocked.title "Blocked by Content Security Policy">
 <!ENTITY cspBlocked.longDesc "<p>The browser prevented this page from loading in this way because the page has a content security policy that disallows it.</p>">
 
-<!ENTITY corruptedContentError.title "Corrupted Content Error">
-<!ENTITY corruptedContentError.longDesc "<p>The page you are trying to view cannot be shown because an error in the data transmission was detected.</p><ul><li>Please contact the website owners to inform them of this problem.</li></ul>">
+<!ENTITY corruptedContentErrorv2.title "Corrupted Content Error">
+<!ENTITY corruptedContentErrorv2.longDesc "<p>The page you are trying to view cannot be shown because an error in the data transmission was detected.</p><ul><li>Please contact the website owners to inform them of this problem.</li></ul>">
 
 <!ENTITY remoteXUL.title "Remote XUL">
 <!ENTITY remoteXUL.longDesc "<p><ul><li>Please contact the website owners to inform them of this problem.</li></ul></p>">
 
 <!ENTITY inadequateSecurityError.title "Your connection is not secure">
 <!-- LOCALIZATION NOTE (inadequateSecurityError.longDesc) - Do not translate
      "NS_ERROR_NET_INADEQUATE_SECURITY". -->
 <!ENTITY inadequateSecurityError.longDesc "<p><span class='hostname'></span> uses security technology that is outdated and vulnerable to attack. An attacker could easily reveal information which you thought to be safe. The website administrator will need to fix the server first before you can visit the site.</p><p>Error code: NS_ERROR_NET_INADEQUATE_SECURITY</p>">
--- a/dom/mathml/nsMathMLElement.cpp
+++ b/dom/mathml/nsMathMLElement.cpp
@@ -468,16 +468,17 @@ nsMathMLElement::ParseNumericValue(const
   else if (unit.EqualsLiteral("em")) cssUnit = eCSSUnit_EM;
   else if (unit.EqualsLiteral("ex")) cssUnit = eCSSUnit_XHeight;
   else if (unit.EqualsLiteral("px")) cssUnit = eCSSUnit_Pixel;
   else if (unit.EqualsLiteral("in")) cssUnit = eCSSUnit_Inch;
   else if (unit.EqualsLiteral("cm")) cssUnit = eCSSUnit_Centimeter;
   else if (unit.EqualsLiteral("mm")) cssUnit = eCSSUnit_Millimeter;
   else if (unit.EqualsLiteral("pt")) cssUnit = eCSSUnit_Point;
   else if (unit.EqualsLiteral("pc")) cssUnit = eCSSUnit_Pica;
+  else if (unit.EqualsLiteral("q")) cssUnit = eCSSUnit_Quarter;
   else { // unexpected unit
     if (!(aFlags & PARSE_SUPPRESS_WARNINGS)) {
       ReportLengthParseError(aString, aDocument);
     }
     return false;
   }
 
   aCSSValue.SetFloatValue(floatValue, cssUnit);
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -1246,17 +1246,18 @@ MediaDecoder::OnSeekResolved(SeekResolve
     UnpinForSeek();
     fireEnded = aVal.mAtEnd;
     if (aVal.mAtEnd) {
       ChangeState(PLAY_STATE_ENDED);
     }
     mLogicallySeeking = false;
   }
 
-  UpdateLogicalPosition(aVal.mEventVisibility);
+  // Ensure logical position is updated after seek.
+  UpdateLogicalPositionInternal(aVal.mEventVisibility);
 
   if (aVal.mEventVisibility != MediaDecoderEventVisibility::Suppressed) {
     mOwner->SeekCompleted();
     if (fireEnded) {
       mOwner->PlaybackEnded();
     }
   }
 }
@@ -1297,24 +1298,20 @@ MediaDecoder::ChangeState(PlayState aSta
   }
 
   CancelDormantTimer();
   // Start dormant timer if necessary
   StartDormantTimer();
 }
 
 void
-MediaDecoder::UpdateLogicalPosition(MediaDecoderEventVisibility aEventVisibility)
+MediaDecoder::UpdateLogicalPositionInternal(MediaDecoderEventVisibility aEventVisibility)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  if (mShuttingDown)
-    return;
-
-  // Per spec, offical position remains stable during pause and seek.
-  if (mPlayState == PLAY_STATE_PAUSED || IsSeeking()) {
+  if (mShuttingDown) {
     return;
   }
 
   double currentPosition = static_cast<double>(CurrentPosition()) / static_cast<double>(USECS_PER_S);
   bool logicalPositionChanged = mLogicalPosition != currentPosition;
   mLogicalPosition = currentPosition;
 
   // Invalidate the frame so any video data is displayed.
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -406,21 +406,25 @@ private:
     MOZ_ASSERT(NS_IsMainThread());
     mIgnoreProgressData = mLogicallySeeking;
   }
 
   // Seeking has started. Inform the element on the main
   // thread.
   void SeekingStarted(MediaDecoderEventVisibility aEventVisibility = MediaDecoderEventVisibility::Observable);
 
-  void UpdateLogicalPosition(MediaDecoderEventVisibility aEventVisibility);
+  void UpdateLogicalPositionInternal(MediaDecoderEventVisibility aEventVisibility);
   void UpdateLogicalPosition()
   {
     MOZ_ASSERT(NS_IsMainThread());
-    UpdateLogicalPosition(MediaDecoderEventVisibility::Observable);
+    // Per spec, offical position remains stable during pause and seek.
+    if (mPlayState == PLAY_STATE_PAUSED || IsSeeking()) {
+      return;
+    }
+    UpdateLogicalPositionInternal(MediaDecoderEventVisibility::Observable);
   }
 
   // Find the end of the cached data starting at the current decoder
   // position.
   int64_t GetDownloadPosition();
 
   // Notifies the element that decoding has failed.
   void DecodeError();
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -82,20 +82,16 @@ MediaFormatReader::~MediaFormatReader()
   MOZ_COUNT_DTOR(MediaFormatReader);
 }
 
 RefPtr<ShutdownPromise>
 MediaFormatReader::Shutdown()
 {
   MOZ_ASSERT(OnTaskQueue());
 
-  if (HasVideo()) {
-    ReportDroppedFramesTelemetry();
-  }
-
   mDemuxerInitRequest.DisconnectIfExists();
   mMetadataPromise.RejectIfExists(ReadMetadataFailureReason::METADATA_ERROR, __func__);
   mSeekPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
   mSkipRequest.DisconnectIfExists();
 
   if (mAudio.mDecoder) {
     Reset(TrackInfo::kAudioTrack);
     if (mAudio.HasPromise()) {
@@ -954,19 +950,16 @@ MediaFormatReader::HandleDemuxedSamples(
         return;
       }
 
       if (decoder.mNextStreamSourceID.isNothing() ||
           decoder.mNextStreamSourceID.ref() != info->GetID()) {
         LOG("%s stream id has changed from:%d to:%d, draining decoder.",
             TrackTypeToStr(aTrack), decoder.mLastStreamSourceID,
             info->GetID());
-        if (aTrack == TrackType::kVideoTrack) {
-          ReportDroppedFramesTelemetry();
-        }
         decoder.mNeedDraining = true;
         decoder.mNextStreamSourceID = Some(info->GetID());
         ScheduleUpdate(aTrack);
         return;
       }
 
       LOG("%s stream id has changed from:%d to:%d, recreating decoder.",
           TrackTypeToStr(aTrack), decoder.mLastStreamSourceID,
@@ -1155,17 +1148,16 @@ MediaFormatReader::Update(TrackType aTra
     if (decoder.mOutput.Length()) {
       RefPtr<MediaData> output = decoder.mOutput[0];
       decoder.mOutput.RemoveElementAt(0);
       decoder.mSizeOfQueue -= 1;
       decoder.mLastSampleTime =
         Some(TimeInterval(TimeUnit::FromMicroseconds(output->mTime),
                           TimeUnit::FromMicroseconds(output->GetEndTime())));
       decoder.mNumSamplesOutputTotal++;
-      decoder.mNumSamplesOutputTotalSinceTelemetry++;
       ReturnOutput(output, aTrack);
       // We have a decoded sample ready to be returned.
       if (aTrack == TrackType::kVideoTrack) {
         uint64_t delta =
           decoder.mNumSamplesOutputTotal - mLastReportedNumDecodedFrames;
         a.mDecoded = static_cast<uint32_t>(delta);
         mLastReportedNumDecodedFrames = decoder.mNumSamplesOutputTotal;
         nsCString error;
@@ -1200,16 +1192,25 @@ MediaFormatReader::Update(TrackType aTra
       }
       // Now that draining has completed, we check if we have received
       // new data again as the result may now be different from the earlier
       // run.
       if (UpdateReceivedNewData(aTrack) || decoder.mSeekRequest.Exists()) {
         LOGV("Nothing more to do");
         return;
       }
+    } else if (decoder.mDemuxEOS && !decoder.mNeedDraining &&
+               !decoder.mDraining && !decoder.mDrainComplete &&
+               decoder.mQueuedSamples.IsEmpty()) {
+      // It is possible to transition from WAITING_FOR_DATA directly to EOS
+      // state during the internal seek; in which case no draining would occur.
+      // There is no more samples left to be decoded and we are already in
+      // EOS state. We can immediately reject the data promise.
+      LOG("Rejecting %s promise: EOS", TrackTypeToStr(aTrack));
+      decoder.RejectPromise(END_OF_STREAM, __func__);
     }
   }
 
   if (decoder.mNeedDraining) {
     DrainDecoder(aTrack);
     return;
   }
 
@@ -1447,17 +1448,16 @@ MediaFormatReader::VideoSkipReset(uint32
   mVideo.mDemuxRequest.DisconnectIfExists();
   Reset(TrackType::kVideoTrack);
 
   if (mDecoder) {
     mDecoder->NotifyDecodedFrames(aSkipped, 0, aSkipped);
   }
 
   mVideo.mNumSamplesSkippedTotal += aSkipped;
-  mVideo.mNumSamplesSkippedTotalSinceTelemetry += aSkipped;
 }
 
 void
 MediaFormatReader::OnVideoSkipCompleted(uint32_t aSkipped)
 {
   MOZ_ASSERT(OnTaskQueue());
   LOG("Skipping succeeded, skipped %u frames", aSkipped);
   mSkipRequest.Complete();
@@ -1889,56 +1889,9 @@ MediaFormatReader::GetMozDebugReaderData
                               mVideo.mNumSamplesInput, mVideo.mNumSamplesOutput,
                               unsigned(size_t(mVideo.mSizeOfQueue)),
                               unsigned(mVideo.mOutput.Length()),
                               mVideo.mWaitingForData, mVideo.mLastStreamSourceID);
   }
   aString += NS_ConvertUTF8toUTF16(result);
 }
 
-void
-MediaFormatReader::ReportDroppedFramesTelemetry()
-{
-  MOZ_ASSERT(OnTaskQueue());
-
-  const VideoInfo* info =
-    mVideo.mInfo ? mVideo.mInfo->GetAsVideoInfo() : &mInfo.mVideo;
-
-  if (!info || !mVideo.mDecoder) {
-    return;
-  }
-
-  nsCString keyPhrase = nsCString("MimeType=");
-  keyPhrase.Append(info->mMimeType);
-  keyPhrase.Append("; ");
-
-  keyPhrase.Append("Resolution=");
-  keyPhrase.AppendInt(info->mDisplay.width);
-  keyPhrase.Append('x');
-  keyPhrase.AppendInt(info->mDisplay.height);
-  keyPhrase.Append("; ");
-
-  keyPhrase.Append("HardwareAcceleration=");
-  if (VideoIsHardwareAccelerated()) {
-    keyPhrase.Append(mVideo.mDecoder->GetDescriptionName());
-    keyPhrase.Append("enabled");
-  } else {
-    keyPhrase.Append("disabled");
-  }
-
-  if (mVideo.mNumSamplesOutputTotalSinceTelemetry) {
-    uint32_t percentage =
-      100 * mVideo.mNumSamplesSkippedTotalSinceTelemetry /
-            mVideo.mNumSamplesOutputTotalSinceTelemetry;
-    nsCOMPtr<nsIRunnable> task = NS_NewRunnableFunction([=]() -> void {
-      LOG("Reporting telemetry DROPPED_FRAMES_IN_VIDEO_PLAYBACK");
-      Telemetry::Accumulate(Telemetry::VIDEO_DETAILED_DROPPED_FRAMES_PROPORTION,
-                            keyPhrase,
-                            percentage);
-    });
-    AbstractThread::MainThread()->Dispatch(task.forget());
-  }
-
-  mVideo.mNumSamplesSkippedTotalSinceTelemetry = 0;
-  mVideo.mNumSamplesOutputTotalSinceTelemetry = 0;
-}
-
 } // namespace mozilla
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -238,18 +238,16 @@ private:
       , mError(false)
       , mNeedDraining(false)
       , mDraining(false)
       , mDrainComplete(false)
       , mNumSamplesInput(0)
       , mNumSamplesOutput(0)
       , mNumSamplesOutputTotal(0)
       , mNumSamplesSkippedTotal(0)
-      , mNumSamplesOutputTotalSinceTelemetry(0)
-      , mNumSamplesSkippedTotalSinceTelemetry(0)
       , mSizeOfQueue(0)
       , mIsHardwareAccelerated(false)
       , mLastStreamSourceID(UINT32_MAX)
     {}
 
     MediaFormatReader* mOwner;
     // Disambiguate Audio vs Video.
     MediaData::Type mType;
@@ -320,19 +318,16 @@ private:
     // Decoded samples returned my mDecoder awaiting being returned to
     // state machine upon request.
     nsTArray<RefPtr<MediaData>> mOutput;
     uint64_t mNumSamplesInput;
     uint64_t mNumSamplesOutput;
     uint64_t mNumSamplesOutputTotal;
     uint64_t mNumSamplesSkippedTotal;
 
-    uint64_t mNumSamplesOutputTotalSinceTelemetry;
-    uint64_t mNumSamplesSkippedTotalSinceTelemetry;
-
     // These get overridden in the templated concrete class.
     // Indicate if we have a pending promise for decoded frame.
     // Rejecting the promise will stop the reader from decoding ahead.
     virtual bool HasPromise() const = 0;
     virtual RefPtr<MediaDataPromise> EnsurePromise(const char* aMethodName) = 0;
     virtual void ResolvePromise(MediaData* aData, const char* aMethodName) = 0;
     virtual void RejectPromise(MediaDecoderReader::NotDecodedReason aReason,
                                const char* aMethodName) = 0;
@@ -533,19 +528,16 @@ private:
   bool mSeekScheduled;
 
   void DoAudioSeek();
   void OnAudioSeekCompleted(media::TimeUnit aTime);
   void OnAudioSeekFailed(DemuxerFailureReason aFailure)
   {
     OnSeekFailed(TrackType::kAudioTrack, aFailure);
   }
-
-  void ReportDroppedFramesTelemetry();
-
   // The SeekTarget that was last given to Seek()
   SeekTarget mOriginalSeekTarget;
   // Temporary seek information while we wait for the data
   Maybe<media::TimeUnit> mFallbackSeekTime;
   Maybe<media::TimeUnit> mPendingSeekTime;
   MozPromiseHolder<SeekPromise> mSeekPromise;
 
   RefPtr<VideoFrameContainer> mVideoFrameContainer;
--- a/dom/media/MediaShutdownManager.cpp
+++ b/dom/media/MediaShutdownManager.cpp
@@ -1,26 +1,27 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "MediaShutdownManager.h"
-#include "nsContentUtils.h"
+#include "mozilla/Logging.h"
 #include "mozilla/StaticPtr.h"
+#include "nsContentUtils.h"
+
 #include "MediaDecoder.h"
-#include "mozilla/Logging.h"
+#include "MediaShutdownManager.h"
 
 namespace mozilla {
 
 extern LazyLogModule gMediaDecoderLog;
 #define DECODER_LOG(type, msg) MOZ_LOG(gMediaDecoderLog, type, msg)
 
-NS_IMPL_ISUPPORTS(MediaShutdownManager, nsIObserver)
+NS_IMPL_ISUPPORTS(MediaShutdownManager, nsIAsyncShutdownBlocker)
 
 MediaShutdownManager::MediaShutdownManager()
   : mIsObservingShutdown(false)
   , mIsDoingXPCOMShutDown(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_COUNT_CTOR(MediaShutdownManager);
 }
@@ -40,29 +41,50 @@ MediaShutdownManager::Instance()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!sInstance) {
     sInstance = new MediaShutdownManager();
   }
   return *sInstance;
 }
 
+static nsCOMPtr<nsIAsyncShutdownClient>
+GetShutdownBarrier()
+{
+  nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
+  MOZ_RELEASE_ASSERT(svc);
+
+  nsCOMPtr<nsIAsyncShutdownClient> barrier;
+  nsresult rv = svc->GetProfileBeforeChange(getter_AddRefs(barrier));
+  if (!barrier) {
+    // We are probably in a content process.
+    rv = svc->GetContentChildShutdown(getter_AddRefs(barrier));
+  }
+  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+  MOZ_RELEASE_ASSERT(barrier);
+  return barrier.forget();
+}
+
 void
 MediaShutdownManager::EnsureCorrectShutdownObserverState()
 {
   bool needShutdownObserver = mDecoders.Count() > 0;
   if (needShutdownObserver != mIsObservingShutdown) {
     mIsObservingShutdown = needShutdownObserver;
     if (mIsObservingShutdown) {
-      nsContentUtils::RegisterShutdownObserver(this);
+      nsresult rv = GetShutdownBarrier()->AddBlocker(
+        this, NS_LITERAL_STRING(__FILE__), __LINE__,
+        NS_LITERAL_STRING("MediaShutdownManager shutdown"));
+      MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
     } else {
-      nsContentUtils::UnregisterShutdownObserver(this);
+      GetShutdownBarrier()->RemoveBlocker(this);
       // Clear our singleton reference. This will probably delete
       // this instance, so don't deref |this| clearing sInstance.
       sInstance = nullptr;
+      DECODER_LOG(LogLevel::Debug, ("MediaShutdownManager::BlockShutdown() end."));
     }
   }
 }
 
 void
 MediaShutdownManager::Register(MediaDecoder* aDecoder)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -81,53 +103,46 @@ MediaShutdownManager::Unregister(MediaDe
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mDecoders.Contains(aDecoder));
   mDecoders.RemoveEntry(aDecoder);
   EnsureCorrectShutdownObserverState();
 }
 
 NS_IMETHODIMP
-MediaShutdownManager::Observe(nsISupports *aSubjet,
-                              const char *aTopic,
-                              const char16_t *someData)
+MediaShutdownManager::GetName(nsAString& aName)
 {
-  MOZ_ASSERT(NS_IsMainThread());
-  if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
-    Shutdown();
-  }
+  aName = NS_LITERAL_STRING("MediaShutdownManager: shutdown");
   return NS_OK;
 }
 
-void
-MediaShutdownManager::Shutdown()
+NS_IMETHODIMP
+MediaShutdownManager::GetState(nsIPropertyBag**)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+MediaShutdownManager::BlockShutdown(nsIAsyncShutdownClient*)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(sInstance);
 
-  DECODER_LOG(LogLevel::Debug, ("MediaShutdownManager::Shutdown() start..."));
+  DECODER_LOG(LogLevel::Debug, ("MediaShutdownManager::BlockShutdown() start..."));
 
   // Set this flag to ensure no Register() is allowed when Shutdown() begins.
   mIsDoingXPCOMShutDown = true;
 
   DebugOnly<uint32_t> oldCount = mDecoders.Count();
   MOZ_ASSERT(oldCount > 0);
 
   // Iterate over the decoders and shut them down.
   for (auto iter = mDecoders.Iter(); !iter.Done(); iter.Next()) {
     iter.Get()->GetKey()->Shutdown();
     // Check MediaDecoder::Shutdown doesn't call Unregister() synchronously in
     // order not to corrupt our hashtable traversal.
     MOZ_ASSERT(mDecoders.Count() == oldCount);
   }
 
-  // Spin the loop until all decoders are unregistered
-  // which will then clear |sInstance|.
-  while (sInstance) {
-    NS_ProcessNextEvent(NS_GetCurrentThread(), true);
-  }
-
-  // Note: Don't access |this| which might be deleted after clearing sInstance.
-
-  DECODER_LOG(LogLevel::Debug, ("MediaShutdownManager::Shutdown() end."));
+  return NS_OK;
 }
 
 } // namespace mozilla
--- a/dom/media/MediaShutdownManager.h
+++ b/dom/media/MediaShutdownManager.h
@@ -2,70 +2,62 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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/. */
 
 #if !defined(MediaShutdownManager_h_)
 #define MediaShutdownManager_h_
 
-#include "nsIObserver.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/StaticPtr.h"
+#include "nsCOMPtr.h"
+#include "nsIAsyncShutdown.h"
 #include "nsIThread.h"
-#include "nsCOMPtr.h"
+#include "nsHashKeys.h"
 #include "nsTHashtable.h"
-#include "nsHashKeys.h"
 
 namespace mozilla {
 
 class MediaDecoder;
 
 // The MediaShutdownManager manages shutting down the MediaDecoder
-// infrastructure in response to an xpcom-shutdown notification. This happens
-// when Gecko is shutting down in the middle of operation. This is tricky, as
-// there are a number of moving parts that must be shutdown in a particular
-// order. Additionally the xpcom-shutdown observer *must* block until all
-// threads are shutdown, which is tricky since we have a number of threads
-// here and their shutdown is asynchronous. We can't have each element of
-// our pipeline listening for xpcom-shutdown, as if each observer blocks
-// waiting for its threads to shutdown it will block other xpcom-shutdown
-// notifications from firing, and shutdown of one part of the media pipeline
-// (say the State Machine thread) may depend another part to be shutdown
-// first (the MediaDecoder threads). The MediaShutdownManager encapsulates
-// all these dependencies, and provides a single xpcom-shutdown listener
-// for the MediaDecoder infrastructure, to ensure that no shutdown order
-// dependencies leak out of the MediaDecoder stack. The MediaShutdownManager
-// is a singleton.
+// infrastructure in response to an xpcom-shutdown notification.
+// This happens when Gecko is shutting down in the middle of operation.
+// This is tricky, as there are a number of moving parts that must
+// be shutdown in a particular order. The MediaShutdownManager
+// encapsulates all these dependencies to ensure that no shutdown
+// order dependencies leak out of the MediaDecoder stack.
+// The MediaShutdownManager is a singleton.
 //
-// The MediaShutdownManager ensures that the MediaDecoder stack is shutdown
-// before returning from its xpcom-shutdown observer by keeping track of all
-// the active MediaDecoders, and upon xpcom-shutdown calling Shutdown() on
-// every MediaDecoder and then spinning the main thread event loop until all
-// SharedThreadPools have shutdown. Once the SharedThreadPools are shutdown,
-// all the state machines and their threads have been shutdown, the
-// xpcom-shutdown observer returns.
+// The MediaShutdownManager ensures that the MediaDecoder stack
+// is shutdown before exiting xpcom-shutdown stage by registering
+// itself with nsIAsyncShutdownService to receive notification
+// when the stage of shutdown has started and then calls Shutdown()
+// on every MediaDecoder. Shutdown will not proceed until all
+// MediaDecoders finish shutdown and MediaShutdownManager unregisters
+// itself from the async shutdown service.
 //
 // Note that calling the Unregister() functions may result in the singleton
 // being deleted, so don't store references to the singleton, always use the
 // singleton by derefing the referenced returned by
 // MediaShutdownManager::Instance(), which ensures that the singleton is
 // created when needed.
 // i.e. like this:
 //    MediaShutdownManager::Instance()::Unregister(someDecoder);
 //    MediaShutdownManager::Instance()::Register(someOtherDecoder);
 // Not like this:
 //  MediaShutdownManager& instance = MediaShutdownManager::Instance();
 //  instance.Unregister(someDecoder); // Warning! May delete instance!
 //  instance.Register(someOtherDecoder); // BAD! instance may be dangling!
-class MediaShutdownManager : public nsIObserver {
+class MediaShutdownManager : public nsIAsyncShutdownBlocker {
 public:
   NS_DECL_ISUPPORTS
-  NS_DECL_NSIOBSERVER
+  NS_DECL_NSIASYNCSHUTDOWNBLOCKER
 
   // The MediaShutdownManager is a singleton, access its instance with
   // this accessor.
   static MediaShutdownManager& Instance();
 
   // Notifies the MediaShutdownManager that it needs to track the shutdown
   // of this MediaDecoder.
   void Register(MediaDecoder* aDecoder);
@@ -75,18 +67,16 @@ public:
   // xpcom-shutdown listener.
   void Unregister(MediaDecoder* aDecoder);
 
 private:
 
   MediaShutdownManager();
   virtual ~MediaShutdownManager();
 
-  void Shutdown();
-
   // Ensures we have a shutdown listener if we need one, and removes the
   // listener and destroys the singleton if we don't.
   void EnsureCorrectShutdownObserverState();
 
   static StaticRefPtr<MediaShutdownManager> sInstance;
 
   // References to the MediaDecoder. The decoders unregister themselves
   // in their Shutdown() method, so we'll drop the reference naturally when
--- a/dom/media/platforms/agnostic/OpusDecoder.cpp
+++ b/dom/media/platforms/agnostic/OpusDecoder.cpp
@@ -5,37 +5,39 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "OpusDecoder.h"
 #include "TimeUnits.h"
 #include "VorbisUtils.h"
 #include "VorbisDecoder.h" // For VorbisLayout
 #include "mozilla/Endian.h"
 #include "mozilla/PodOperations.h"
+#include "mozilla/SyncRunnable.h"
 
 #include <stdint.h>
 #include <inttypes.h>  // For PRId64
 
 extern mozilla::LogModule* GetPDMLog();
 #define OPUS_DEBUG(arg, ...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, \
     ("OpusDataDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
 
 namespace mozilla {
 
 OpusDataDecoder::OpusDataDecoder(const AudioInfo& aConfig,
-                                 FlushableTaskQueue* aTaskQueue,
+                                 TaskQueue* aTaskQueue,
                                  MediaDataDecoderCallback* aCallback)
   : mInfo(aConfig)
   , mTaskQueue(aTaskQueue)
   , mCallback(aCallback)
   , mOpusDecoder(nullptr)
   , mSkip(0)
   , mDecodedHeader(false)
   , mPaddingDiscarded(false)
   , mFrames(0)
+  , mIsFlushing(false)
 {
 }
 
 OpusDataDecoder::~OpusDataDecoder()
 {
   if (mOpusDecoder) {
     opus_multistream_decoder_destroy(mOpusDecoder);
     mOpusDecoder = nullptr;
@@ -128,25 +130,27 @@ OpusDataDecoder::DecodeHeader(const unsi
 
   return NS_OK;
 }
 
 nsresult
 OpusDataDecoder::Input(MediaRawData* aSample)
 {
   mTaskQueue->Dispatch(NewRunnableMethod<RefPtr<MediaRawData>>(
-                         this, &OpusDataDecoder::Decode,
-                         RefPtr<MediaRawData>(aSample)));
+                       this, &OpusDataDecoder::ProcessDecode, aSample));
 
   return NS_OK;
 }
 
 void
-OpusDataDecoder::Decode(MediaRawData* aSample)
+OpusDataDecoder::ProcessDecode(MediaRawData* aSample)
 {
+  if (mIsFlushing) {
+    return;
+  }
   if (DoDecode(aSample) == -1) {
     mCallback->Error();
   } else if(mTaskQueue->IsEmpty()) {
     mCallback->InputExhausted();
   }
 }
 
 int
@@ -294,39 +298,45 @@ OpusDataDecoder::DoDecode(MediaRawData* 
                                   Move(buffer),
                                   mOpusParser->mChannels,
                                   mOpusParser->mRate));
   mFrames += frames;
   return frames;
 }
 
 void
-OpusDataDecoder::DoDrain()
+OpusDataDecoder::ProcessDrain()
 {
   mCallback->DrainComplete();
 }
 
 nsresult
 OpusDataDecoder::Drain()
 {
-  mTaskQueue->Dispatch(NewRunnableMethod(this, &OpusDataDecoder::DoDrain));
+  mTaskQueue->Dispatch(NewRunnableMethod(this, &OpusDataDecoder::ProcessDrain));
   return NS_OK;
 }
 
 nsresult
 OpusDataDecoder::Flush()
 {
-  mTaskQueue->Flush();
-  if (mOpusDecoder) {
+  if (!mOpusDecoder) {
+    return NS_OK;
+  }
+  mIsFlushing = true;
+  nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction([this] () {
+    MOZ_ASSERT(mOpusDecoder);
     // Reset the decoder.
     opus_multistream_decoder_ctl(mOpusDecoder, OPUS_RESET_STATE);
     mSkip = mOpusParser->mPreSkip;
     mPaddingDiscarded = false;
     mLastFrameTime.reset();
-  }
+  });
+  SyncRunnable::DispatchToThread(mTaskQueue, runnable);
+  mIsFlushing = false;
   return NS_OK;
 }
 
 /* static */
 bool
 OpusDataDecoder::IsOpus(const nsACString& aMimeType)
 {
   return aMimeType.EqualsLiteral("audio/webm; codecs=opus") ||
--- a/dom/media/platforms/agnostic/OpusDecoder.h
+++ b/dom/media/platforms/agnostic/OpusDecoder.h
@@ -13,17 +13,17 @@
 #include "nsAutoPtr.h"
 
 namespace mozilla {
 
 class OpusDataDecoder : public MediaDataDecoder
 {
 public:
   OpusDataDecoder(const AudioInfo& aConfig,
-                  FlushableTaskQueue* aTaskQueue,
+                  TaskQueue* aTaskQueue,
                   MediaDataDecoderCallback* aCallback);
   ~OpusDataDecoder();
 
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
   nsresult Flush() override;
   nsresult Drain() override;
   nsresult Shutdown() override;
@@ -33,34 +33,36 @@ public:
   }
 
   // Return true if mimetype is Opus
   static bool IsOpus(const nsACString& aMimeType);
 
 private:
   nsresult DecodeHeader(const unsigned char* aData, size_t aLength);
 
-  void Decode (MediaRawData* aSample);
-  int DoDecode (MediaRawData* aSample);
-  void DoDrain ();
+  void ProcessDecode(MediaRawData* aSample);
+  int DoDecode(MediaRawData* aSample);
+  void ProcessDrain();
 
   const AudioInfo& mInfo;
-  RefPtr<FlushableTaskQueue> mTaskQueue;
+  const RefPtr<TaskQueue> mTaskQueue;
   MediaDataDecoderCallback* mCallback;
 
   // Opus decoder state
   nsAutoPtr<OpusParser> mOpusParser;
   OpusMSDecoder* mOpusDecoder;
 
   uint16_t mSkip;        // Samples left to trim before playback.
   bool mDecodedHeader;
 
   // Opus padding should only be discarded on the final packet.  Once this
   // is set to true, if the reader attempts to decode any further packets it
   // will raise an error so we can indicate that the file is invalid.
   bool mPaddingDiscarded;
   int64_t mFrames;
   Maybe<int64_t> mLastFrameTime;
   uint8_t mMappingTable[MAX_AUDIO_CHANNELS]; // Channel mapping table.
+
+  Atomic<bool> mIsFlushing;
 };
 
 } // namespace mozilla
 #endif
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -803,27 +803,16 @@ WMFVideoMFTManager::Shutdown()
 
 bool
 WMFVideoMFTManager::IsHardwareAccelerated(nsACString& aFailureReason) const
 {
   aFailureReason = mDXVAFailureReason;
   return mDecoder && mUseHwAccel;
 }
 
-const char*
-WMFVideoMFTManager::GetDescriptionName() const
-{
-  if (mDecoder && mUseHwAccel && mDXVA2Manager) {
-    return (mDXVA2Manager->IsD3D11()) ?
-      "D3D11 Hardware Decoder" : "D3D9 Hardware Decoder";
-  } else {
-    return "wmf software video decoder";
-  }
-}
-
 void
 WMFVideoMFTManager::ConfigurationChanged(const TrackInfo& aConfig)
 {
   MOZ_ASSERT(aConfig.GetAsVideoInfo());
   mVideoInfo = *aConfig.GetAsVideoInfo();
   mImageSize = mVideoInfo.mImage;
 }
 
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.h
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.h
@@ -36,17 +36,22 @@ public:
   bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
 
   TrackInfo::TrackType GetType() override {
     return TrackInfo::kVideoTrack;
   }
 
   void ConfigurationChanged(const TrackInfo& aConfig) override;
 
-  const char* GetDescriptionName() const override;
+  const char* GetDescriptionName() const override
+  {
+    nsCString failureReason;
+    return IsHardwareAccelerated(failureReason)
+      ? "wmf hardware video decoder" : "wmf software video decoder";
+  }
 
 private:
 
   bool InitializeDXVA(bool aForceD3D9);
 
   bool InitInternal(bool aForceD3D9);
 
   HRESULT ConfigureVideoFrameGeometry();
--- a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
+++ b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp
@@ -86,18 +86,16 @@ MediaEngineRemoteVideoSource::Shutdown()
     }
     MOZ_ASSERT(mState == kStopped);
   }
 
   if (mState == kAllocated || mState == kStopped) {
     Deallocate();
   }
 
-  mozilla::camera::Shutdown();
-
   mState = kReleased;
   mInitDone = false;
   return;
 }
 
 nsresult
 MediaEngineRemoteVideoSource::Allocate(const dom::MediaTrackConstraints& aConstraints,
                                        const MediaEnginePrefs& aPrefs,
--- a/dom/push/test/lifetime_worker.js
+++ b/dom/push/test/lifetime_worker.js
@@ -50,26 +50,30 @@ onmessage = function(event) {
   event.source.postMessage({type: "message", state: state});
   state = event.data;
   if (event.data === "release") {
     resolvePromise();
   }
 }
 
 onpush = function(event) {
+  var pushResolve;
+  event.waitUntil(new Promise(function(resolve) {
+    pushResolve = resolve;
+  }));
+
   // FIXME(catalinb): push message carry no data. So we assume the only
   // push message we get is "wait"
   clients.matchAll().then(function(client) {
     if (client.length == 0) {
       dump("ERROR: no clients to send the response to.\n");
     }
 
     client[0].postMessage({type: "push", state: state});
 
     state = "wait";
-    event.waitUntil(new Promise(function(res, rej) {
-      if (resolvePromiseCallback) {
-        dump("ERROR: service worker was already waiting on a promise.\n");
-      }
-      resolvePromiseCallback = res;
-    }));
+    if (resolvePromiseCallback) {
+      dump("ERROR: service worker was already waiting on a promise.\n");
+    } else {
+      resolvePromiseCallback = pushResolve;
+    }
   });
 }
--- a/dom/push/test/test_serviceworker_lifetime.html
+++ b/dom/push/test/test_serviceworker_lifetime.html
@@ -252,17 +252,17 @@
         .then(closeIframe)
         .then(cancelShutdownObserver)
 
         // Test with push events and message events
         .then(createIframe)
         .then(setShutdownObserver(false))
         .then(checkStateAndUpdate(pushEvent, "from_scope", "wait"))
         .then(setShutdownObserver(true))
-        .then(checkStateAndUpdate(messageEventIframe, "wait", "update"))
+        .then(checkStateAndUpdate(messageEventIframe, "wait", "release"))
         .then(waitOnShutdownObserver)
         .then(closeIframe)
     }
   }
 
   var test2 = {
     prefs: [
       ["dom.serviceWorkers.idle_timeout", 0],
--- a/dom/tests/mochitest/bugs/test_window_bar.html
+++ b/dom/tests/mochitest/bugs/test_window_bar.html
@@ -53,17 +53,16 @@ function testWindow(w)
       var enabled = w.location.search == '?true';
       is(w[feature].visible, enabled, feature + ' should follow window.open settings.');
     }
   }
 
   checkFeature('menubar');
   checkFeature('toolbar');
   checkFeature('personalbar');
-  checkFeature('scrollbars');
   checkFeature('statusbar', 'status');
   checkFeature('locationbar', 'location');
 
   w.close();
 
   numWindows++;
   if (numWindows == 3) {
     // We're done!
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -200,52 +200,162 @@ ServiceWorkerPrivate::CheckScriptEvaluat
 
   return NS_OK;
 }
 
 namespace {
 
 // Holds the worker alive until the waitUntil promise is resolved or
 // rejected.
-class KeepAliveHandler final : public PromiseNativeHandler
+class KeepAliveHandler final
 {
-  nsMainThreadPtrHandle<KeepAliveToken> mKeepAliveToken;
+  // Use an internal class to listen for the promise resolve/reject
+  // callbacks.  This class also registers a feature so that it can
+  // preemptively cleanup if the service worker is timed out and
+  // terminated.
+  class InternalHandler final : public PromiseNativeHandler
+                              , public WorkerFeature
+  {
+    nsMainThreadPtrHandle<KeepAliveToken> mKeepAliveToken;
+
+    // Worker thread only
+    WorkerPrivate* mWorkerPrivate;
+    RefPtr<Promise> mPromise;
+    bool mFeatureAdded;
+
+    ~InternalHandler()
+    {
+      MaybeCleanup();
+    }
+
+    bool
+    AddFeature()
+    {
+      MOZ_ASSERT(mWorkerPrivate);
+      mWorkerPrivate->AssertIsOnWorkerThread();
+      MOZ_ASSERT(!mFeatureAdded);
+      mFeatureAdded = mWorkerPrivate->AddFeature(this);
+      return mFeatureAdded;
+    }
+
+    void
+    MaybeCleanup()
+    {
+      MOZ_ASSERT(mWorkerPrivate);
+      mWorkerPrivate->AssertIsOnWorkerThread();
+      if (!mPromise) {
+        return;
+      }
+      if (mFeatureAdded) {
+        mWorkerPrivate->RemoveFeature(this);
+      }
+      mPromise = nullptr;
+      mKeepAliveToken = nullptr;
+    }
+
+    void
+    ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+    {
+      MOZ_ASSERT(mWorkerPrivate);
+      mWorkerPrivate->AssertIsOnWorkerThread();
+      MaybeCleanup();
+    }
 
-  virtual ~KeepAliveHandler()
-  {}
+    void
+    RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+    {
+      MOZ_ASSERT(mWorkerPrivate);
+      mWorkerPrivate->AssertIsOnWorkerThread();
+      MaybeCleanup();
+    }
+
+    bool
+    Notify(Status aStatus) override
+    {
+      MOZ_ASSERT(mWorkerPrivate);
+      mWorkerPrivate->AssertIsOnWorkerThread();
+      if (aStatus < Terminating) {
+        return true;
+      }
+      MaybeCleanup();
+      return true;
+    }
+
+    InternalHandler(const nsMainThreadPtrHandle<KeepAliveToken>& aKeepAliveToken,
+                    WorkerPrivate* aWorkerPrivate,
+                    Promise* aPromise)
+      : mKeepAliveToken(aKeepAliveToken)
+      , mWorkerPrivate(aWorkerPrivate)
+      , mPromise(aPromise)
+      , mFeatureAdded(false)
+    {
+      MOZ_ASSERT(mKeepAliveToken);
+      MOZ_ASSERT(mWorkerPrivate);
+      MOZ_ASSERT(mPromise);
+    }
+
+  public:
+    static already_AddRefed<InternalHandler>
+    Create(const nsMainThreadPtrHandle<KeepAliveToken>& aKeepAliveToken,
+           WorkerPrivate* aWorkerPrivate,
+           Promise* aPromise)
+    {
+      RefPtr<InternalHandler> ref = new InternalHandler(aKeepAliveToken,
+                                                        aWorkerPrivate,
+                                                        aPromise);
+
+      if (NS_WARN_IF(!ref->AddFeature())) {
+        return nullptr;
+      }
+
+      return ref.forget();
+    }
+
+    NS_DECL_ISUPPORTS
+  };
+
+  // This is really just a wrapper class to keep the InternalHandler
+  // private.  We don't want any code to accidentally call
+  // Promise::AppendNativeHandler() without also referencing the promise.
+  // Therefore we force all code through the static CreateAndAttachToPromise()
+  // and use the private InternalHandler object.
+  KeepAliveHandler() = delete;
+  ~KeepAliveHandler() = delete;
 
 public:
-  NS_DECL_ISUPPORTS
-
-  explicit KeepAliveHandler(const nsMainThreadPtrHandle<KeepAliveToken>& aKeepAliveToken)
-    : mKeepAliveToken(aKeepAliveToken)
-  { }
-
-  void
-  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+  // Create a private handler object and attach it to the given Promise.
+  // This will also create a strong ref to the Promise in a ref cycle.  The
+  // ref cycle is broken when the Promise is fulfilled or the worker thread
+  // is Terminated.
+  static void
+  CreateAndAttachToPromise(const nsMainThreadPtrHandle<KeepAliveToken>& aKeepAliveToken,
+                           Promise* aPromise)
   {
-#ifdef DEBUG
     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(workerPrivate);
     workerPrivate->AssertIsOnWorkerThread();
-#endif
-  }
+    MOZ_ASSERT(aKeepAliveToken);
+    MOZ_ASSERT(aPromise);
 
-  void
-  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
-  {
-#ifdef DEBUG
-    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
-    MOZ_ASSERT(workerPrivate);
-    workerPrivate->AssertIsOnWorkerThread();
-#endif
+    // This creates a strong ref to the promise.
+    RefPtr<InternalHandler> handler = InternalHandler::Create(aKeepAliveToken,
+                                                              workerPrivate,
+                                                              aPromise);
+    if (NS_WARN_IF(!handler)) {
+      return;
+    }
+
+    // This then creates a strong ref cycle between the promise and the
+    // handler.  The cycle is broken when the Promise is fulfilled or
+    // the worker thread is Terminated.
+    aPromise->AppendNativeHandler(handler);
   }
 };
 
-NS_IMPL_ISUPPORTS0(KeepAliveHandler)
+NS_IMPL_ISUPPORTS0(KeepAliveHandler::InternalHandler)
 
 class RegistrationUpdateRunnable : public Runnable
 {
   nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
   const bool mNeedTimeCheck;
 
 public:
   RegistrationUpdateRunnable(nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
@@ -306,19 +416,18 @@ public:
     RefPtr<Promise> waitUntilPromise = aEvent->GetPromise();
     if (!waitUntilPromise) {
       waitUntilPromise =
         Promise::Resolve(sgo, aCx, JS::UndefinedHandleValue, result);
       MOZ_RELEASE_ASSERT(!result.Failed());
     }
 
     MOZ_ASSERT(waitUntilPromise);
-    RefPtr<KeepAliveHandler> keepAliveHandler =
-      new KeepAliveHandler(mKeepAliveToken);
-    waitUntilPromise->AppendNativeHandler(keepAliveHandler);
+    KeepAliveHandler::CreateAndAttachToPromise(mKeepAliveToken,
+                                               waitUntilPromise);
 
     if (aWaitUntilPromise) {
       waitUntilPromise.forget(aWaitUntilPromise);
     }
   }
 };
 
 // Handle functional event
@@ -1384,19 +1493,18 @@ private:
                                              NS_ERROR_INTERCEPTION_FAILED);
       }
 
       MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
     }
 
     RefPtr<Promise> waitUntilPromise = event->GetPromise();
     if (waitUntilPromise) {
-      RefPtr<KeepAliveHandler> keepAliveHandler =
-        new KeepAliveHandler(mKeepAliveToken);
-      waitUntilPromise->AppendNativeHandler(keepAliveHandler);
+      KeepAliveHandler::CreateAndAttachToPromise(mKeepAliveToken,
+                                                 waitUntilPromise);
     }
 
     return true;
   }
 
   nsresult
   HandleBodyWithHeaders(nsIInputStream* aUploadStream)
   {
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/blocking_install_event_worker.js
@@ -0,0 +1,23 @@
+function postMessageToTest(msg) {
+  return clients.matchAll({ includeUncontrolled: true })
+    .then(list => {
+      for (var client of list) {
+        if (client.url.endsWith('test_install_event_gc.html')) {
+          client.postMessage(msg);
+          break;
+        }
+      }
+    });
+}
+
+addEventListener('install', evt => {
+  // This must be a simple promise to trigger the CC failure.
+  evt.waitUntil(new Promise(function() { }));
+  postMessageToTest({ type: 'INSTALL_EVENT' });
+});
+
+addEventListener('message', evt => {
+  if (evt.data.type === 'ping') {
+    postMessageToTest({ type: 'pong' });
+  }
+});
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -198,16 +198,17 @@ support-files =
   openWindow_worker.js
   redirect.sjs
   open_window/client.html
   lorem_script.js
   file_blob_response_worker.js
   !/dom/security/test/cors/file_CrossSiteXHR_server.sjs
   !/dom/tests/mochitest/notification/MockServices.js
   !/dom/tests/mochitest/notification/NotificationTest.js
+  blocking_install_event_worker.js
 
 [test_bug1151916.html]
 [test_bug1240436.html]
 [test_claim.html]
 [test_claim_fetch.html]
 [test_claim_oninstall.html]
 [test_close.html]
 [test_controller.html]
@@ -230,16 +231,17 @@ skip-if = (debug && e10s) # Bug 1262224
 [test_https_origin_after_redirect_cached.html]
 [test_https_synth_fetch_from_cached_sw.html]
 [test_imagecache.html]
 [test_imagecache_max_age.html]
 [test_importscript.html]
 [test_importscript_mixedcontent.html]
 tags = mcb
 [test_install_event.html]
+[test_install_event_gc.html]
 [test_installation_simple.html]
 [test_match_all.html]
 [test_match_all_advanced.html]
 [test_match_all_client_id.html]
 [test_match_all_client_properties.html]
 [test_navigator.html]
 [test_not_intercept_plugin.html]
 [test_notification_constructor_error.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_install_event_gc.html
@@ -0,0 +1,121 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test install event being GC'd before waitUntil fulfills</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script class="testbody" type="text/javascript">
+var script = 'blocking_install_event_worker.js';
+var scope = 'sw_clients/simple.html?install-event-gc';
+var registration;
+
+function register() {
+  return navigator.serviceWorker.register(script, { scope: scope })
+    .then(swr => registration = swr);
+}
+
+function unregister() {
+  if (!registration) {
+    return;
+  }
+  return registration.unregister();
+}
+
+function waitForInstallEvent() {
+  return new Promise((resolve, reject) => {
+    navigator.serviceWorker.addEventListener('message', evt => {
+      if (evt.data.type === 'INSTALL_EVENT') {
+        resolve();
+      }
+    });
+  });
+}
+
+function gcWorker() {
+  return new Promise(function(resolve, reject) {
+    // We are able to trigger asynchronous garbage collection and cycle
+    // collection by emitting "child-cc-request" and "child-gc-request"
+    // observer notifications.  The worker RuntimeService will translate
+    // these notifications into the appropriate operation on all known
+    // worker threads.
+    //
+    // In the failure case where GC/CC causes us to abort the installation,
+    // we will know something happened from the statechange event.
+    const statechangeHandler = evt => {
+      // Reject rather than resolving to avoid the possibility of us seeing
+      // an unrelated racing statechange somehow.  Since in the success case we
+      // will still see a state change on termination, we do explicitly need to
+      // be removed on the success path.
+      ok(registration.installing, 'service worker is still installing?');
+      reject();
+    };
+    registration.installing.addEventListener('statechange', statechangeHandler);
+    // In the success case since the service worker installation is effectively
+    // hung, we instead depend on sending a 'ping' message to the service worker
+    // and hearing it 'pong' back.  Since we issue our postMessage after we
+    // trigger the GC/CC, our 'ping' will only be processed after the GC/CC and
+    // therefore the pong will also strictly occur after the cycle collection.
+    navigator.serviceWorker.addEventListener('message', evt => {
+      if (evt.data.type === 'pong') {
+        registration.installing.removeEventListener(
+          'statechange', statechangeHandler);
+        resolve();
+      }
+    });
+    // At the current time, the service worker will exist in our same process
+    // and notifyObservers is synchronous.  However, in the future, service
+    // workers may end up in a separate process and in that case it will be
+    // appropriate to use notifyObserversInParentProcess or something like it.
+    // (notifyObserversInParentProcess is a synchronous IPC call to the parent
+    // process's main thread.  IPDL PContent::CycleCollect is an async message.
+    // Ordering will be maintained if the postMessage goes via PContent as well,
+    // but that seems unlikely.)
+    SpecialPowers.notifyObservers(null, 'child-gc-request', null);
+    SpecialPowers.notifyObservers(null, 'child-cc-request', null);
+    SpecialPowers.notifyObservers(null, 'child-gc-request', null);
+    // (Only send the ping after we set the gc/cc/gc in motion.)
+    registration.installing.postMessage({ type: 'ping' });
+  });
+}
+
+function terminateWorker() {
+  return SpecialPowers.pushPrefEnv({
+    set: [
+      ["dom.serviceWorkers.idle_timeout", 0],
+      ["dom.serviceWorkers.idle_extended_timeout", 0]
+    ]
+  }).then(_ => {
+    registration.installing.postMessage({ type: 'RESET_TIMER' });
+  });
+}
+
+function runTest() {
+  Promise.all([
+    waitForInstallEvent(),
+    register()
+  ]).then(_ => ok(registration.installing, 'service worker is installing'))
+    .then(gcWorker)
+    .then(_ => ok(registration.installing, 'service worker is still installing'))
+    .then(terminateWorker)
+    .catch(e => ok(false, e))
+    .then(unregister)
+    .then(SimpleTest.finish);
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({"set": [
+  ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+  ["dom.serviceWorkers.enabled", true],
+  ["dom.serviceWorkers.testing.enabled", true],
+  ["dom.caches.enabled", true],
+]}, runTest);
+</script>
+</pre>
+</body>
+</html>
--- a/dom/xbl/nsXBLBinding.cpp
+++ b/dom/xbl/nsXBLBinding.cpp
@@ -53,16 +53,17 @@
 // Nasty hack.  Maybe we could move some of the classinfo utility methods
 // (e.g. WrapNative) over to nsContentUtils?
 #include "nsDOMClassInfo.h"
 
 #include "mozilla/DeferredFinalize.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/ShadowRoot.h"
+#include "mozilla/ServoStyleSet.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 // Helper classes
 
 /***********************************************************************/
 //
@@ -198,16 +199,20 @@ nsXBLBinding::InstallAnonymousContent(ns
   // element's document, assuming that the bound element is in a document
   // Note that we don't change the current doc of aAnonParent here, since that
   // quite simply does not matter.  aAnonParent is just a way of keeping refs
   // to all its kids, which are anonymous content from the point of view of
   // aElement.
   // (2) The children's parent back pointer should not be to this synthetic root
   // but should instead point to the enclosing parent element.
   nsIDocument* doc = aElement->GetUncomposedDoc();
+  ServoStyleSet* servoStyleSet = nullptr;
+  if (nsIPresShell* presShell = aElement->OwnerDoc()->GetShell()) {
+    servoStyleSet = presShell->StyleSet()->GetAsServo();
+  }
   bool allowScripts = AllowScripts();
 
   nsAutoScriptBlocker scriptBlocker;
   for (nsIContent* child = aAnonParent->GetFirstChild();
        child;
        child = child->GetNextSibling()) {
     child->UnbindFromTree();
     if (aChromeOnlyContent) {
@@ -228,16 +233,20 @@ nsXBLBinding::InstallAnonymousContent(ns
 #ifdef MOZ_XUL
     // To make XUL templates work (and other goodies that happen when
     // an element is added to a XUL document), we need to notify the
     // XUL document using its special API.
     nsCOMPtr<nsIXULDocument> xuldoc(do_QueryInterface(doc));
     if (xuldoc)
       xuldoc->AddSubtreeToDocument(child);
 #endif
+
+    if (servoStyleSet) {
+      servoStyleSet->RestyleSubtree(child);
+    }
   }
 }
 
 void
 nsXBLBinding::UninstallAnonymousContent(nsIDocument* aDocument,
                                         nsIContent* aAnonParent)
 {
   nsAutoScriptBlocker scriptBlocker;
--- a/embedding/browser/nsContextMenuInfo.cpp
+++ b/embedding/browser/nsContextMenuInfo.cpp
@@ -291,17 +291,17 @@ nsContextMenuInfo::GetBackgroundImageReq
       primitiveValue = do_QueryInterface(cssValue);
       if (primitiveValue) {
         primitiveValue->GetStringValue(bgStringValue);
         if (!bgStringValue.EqualsLiteral("none")) {
           nsCOMPtr<nsIURI> bgUri;
           NS_NewURI(getter_AddRefs(bgUri), bgStringValue);
           NS_ENSURE_TRUE(bgUri, NS_ERROR_FAILURE);
 
-          RefPtr<imgLoader> il = imgLoader::GetInstance();
+          imgLoader* il = imgLoader::NormalLoader();
           NS_ENSURE_TRUE(il, NS_ERROR_FAILURE);
 
           return il->LoadImage(bgUri, nullptr, nullptr,
                                doc->GetReferrerPolicy(), principal, nullptr,
                                nullptr, nullptr, nullptr, nsIRequest::LOAD_NORMAL,
                                nullptr, nsIContentPolicy::TYPE_INTERNAL_IMAGE,
                                EmptyString(), aRequest);
         }
--- a/embedding/components/find/nsFind.cpp
+++ b/embedding/components/find/nsFind.cpp
@@ -1073,41 +1073,45 @@ nsFind::Find(const char16_t* aPatText, n
       patc = patStr[pindex];
     }
     if (!inWhitespace && IsSpace(patc)) {
       inWhitespace = true;
     } else if (!inWhitespace && !mCaseSensitive && IsUpperCase(c)) {
       c = ToLowerCase(c);
     }
 
-    switch (c) {
+    if (c == CH_SHY) {
       // ignore soft hyphens in the document
-      case CH_SHY:
-        continue;
-      // treat curly and straight quotes as identical
-      case CH_LEFT_SINGLE_QUOTE:
-      case CH_RIGHT_SINGLE_QUOTE:
-        c = CH_APOSTROPHE;
-        break;
-      case CH_LEFT_DOUBLE_QUOTE:
-      case CH_RIGHT_DOUBLE_QUOTE:
-        c = CH_QUOTE;
-        break;
+      continue;
     }
 
-    switch (patc) {
-      // treat curly and straight quotes as identical
-      case CH_LEFT_SINGLE_QUOTE:
-      case CH_RIGHT_SINGLE_QUOTE:
-        patc = CH_APOSTROPHE;
-        break;
-      case CH_LEFT_DOUBLE_QUOTE:
-      case CH_RIGHT_DOUBLE_QUOTE:
-        patc = CH_QUOTE;
-        break;
+    if (!mCaseSensitive) {
+      switch (c) {
+        // treat curly and straight quotes as identical
+        case CH_LEFT_SINGLE_QUOTE:
+        case CH_RIGHT_SINGLE_QUOTE:
+          c = CH_APOSTROPHE;
+          break;
+        case CH_LEFT_DOUBLE_QUOTE:
+        case CH_RIGHT_DOUBLE_QUOTE:
+          c = CH_QUOTE;
+          break;
+      }
+
+      switch (patc) {
+        // treat curly and straight quotes as identical
+        case CH_LEFT_SINGLE_QUOTE:
+        case CH_RIGHT_SINGLE_QUOTE:
+          patc = CH_APOSTROPHE;
+          break;
+        case CH_LEFT_DOUBLE_QUOTE:
+        case CH_RIGHT_DOUBLE_QUOTE:
+          patc = CH_QUOTE;
+          break;
+      }
     }
 
     // a '\n' between CJ characters is ignored
     if (pindex != (mFindBackward ? patLen : 0) && c != patc && !inWhitespace) {
       if (c == '\n' && t2b && IS_CJ_CHAR(prevChar)) {
         int32_t nindex = findex + incr;
         if (mFindBackward ? (nindex >= 0) : (nindex < fragLen)) {
           if (IS_CJ_CHAR(t2b[nindex])) {
--- a/embedding/components/windowwatcher/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/nsWindowWatcher.cpp
@@ -1538,17 +1538,17 @@ nsWindowWatcher::URIfromURL(const char* 
 
   // build and return the absolute URI
   return NS_NewURI(aURI, aURL, baseURI);
 }
 
 #define NS_CALCULATE_CHROME_FLAG_FOR(feature, flag)                            \
   prefBranch->GetBoolPref(feature, &forceEnable);                              \
   if (forceEnable && !(aDialog && !openedFromContentScript) &&                 \
-      !(!openedFromContentScript && aHasChromeParent) && !aChromeURL) {                  \
+      !(!openedFromContentScript && aHasChromeParent) && !aChromeURL) {        \
     chromeFlags |= flag;                                                       \
   } else {                                                                     \
     chromeFlags |=                                                             \
       WinHasOption(aFeatures, feature, 0, &presenceFlag) ? flag : 0;           \
   }
 
 /**
  * Calculate the chrome bitmask from a string list of features.
@@ -1653,23 +1653,26 @@ nsWindowWatcher::CalculateChromeFlags(mo
   NS_CALCULATE_CHROME_FLAG_FOR("location",
                                nsIWebBrowserChrome::CHROME_LOCATIONBAR);
   NS_CALCULATE_CHROME_FLAG_FOR("personalbar",
                                nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR);
   NS_CALCULATE_CHROME_FLAG_FOR("status",
                                nsIWebBrowserChrome::CHROME_STATUSBAR);
   NS_CALCULATE_CHROME_FLAG_FOR("menubar",
                                nsIWebBrowserChrome::CHROME_MENUBAR);
-  NS_CALCULATE_CHROME_FLAG_FOR("scrollbars",
-                               nsIWebBrowserChrome::CHROME_SCROLLBARS);
   NS_CALCULATE_CHROME_FLAG_FOR("resizable",
                                nsIWebBrowserChrome::CHROME_WINDOW_RESIZE);
   NS_CALCULATE_CHROME_FLAG_FOR("minimizable",
                                nsIWebBrowserChrome::CHROME_WINDOW_MIN);
 
+  // default scrollbar to "on," unless explicitly turned off
+  if (WinHasOption(aFeatures, "scrollbars", 1, &presenceFlag) || !presenceFlag) {
+    chromeFlags |= nsIWebBrowserChrome::CHROME_SCROLLBARS;
+  }
+
   chromeFlags |= WinHasOption(aFeatures, "popup", 0, &presenceFlag) ?
     nsIWebBrowserChrome::CHROME_WINDOW_POPUP : 0;
 
   /* OK.
      Normal browser windows, in spite of a stated pattern of turning off
      all chrome not mentioned explicitly, will want the new OS chrome (window
      borders, titlebars, closebox) on, unless explicitly turned off.
      Dialogs, on the other hand, take the absence of any explicit settings
--- a/embedding/test/test_nsFind.html
+++ b/embedding/test/test_nsFind.html
@@ -214,12 +214,28 @@ https://bugzilla.mozilla.org/show_bug.cg
   assertFound(quotes, "\u2018doesn't\u2018");
   assertFound(quotes, "\u2019doesn't\u2019");
   assertFound(quotes, "\u2018doesn\u2019t\u2019");
   assertFound(quotes, "\u2019doesn\u2018t\u2019");
   assertFound(quotes, "\u2018doesn\u2019t\u2018");
 
   assertNotFound(quotes, "\"doesn't\"");
   assertNotFound(quotes, "\u201Cdoesn't\u201D");
+
+  // Curly quotes and straight quotes should not match.
+  rf.caseSensitive = true;
+
+  assertFound(quotes, "\"straight\"");
+  assertNotFound(quotes, "\u201Cstraight\u201D");
+
+  assertNotFound(quotes, "\"curly\"");
+  assertFound(quotes, "\u201Ccurly\u201D");
+
+  assertFound(quotes, "\u2018didn't\u2019");
+  assertNotFound(quotes, "'didn't'");
+
+  assertFound(quotes, "'doesn\u2019t'");
+  assertNotFound(quotes, "'doesn\u2018t'");
+  assertNotFound(quotes, "'doesn't'");
 </script>
 </pre>
 </body>
 </html>
--- a/extensions/auth/nsHttpNegotiateAuth.cpp
+++ b/extensions/auth/nsHttpNegotiateAuth.cpp
@@ -35,27 +35,32 @@
 #include "prprf.h"
 #include "mozilla/Logging.h"
 #include "prmem.h"
 #include "prnetdb.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Snprintf.h"
 #include "nsIChannel.h"
 #include "nsNetUtil.h"
+#include "nsThreadUtils.h"
+#include "nsIHttpAuthenticatorCallback.h"
+#include "mozilla/Mutex.h"
+#include "nsICancelable.h"
 
 //-----------------------------------------------------------------------------
 
 static const char kNegotiate[] = "Negotiate";
 static const char kNegotiateAuthTrustedURIs[] = "network.negotiate-auth.trusted-uris";
 static const char kNegotiateAuthDelegationURIs[] = "network.negotiate-auth.delegation-uris";
 static const char kNegotiateAuthAllowProxies[] = "network.negotiate-auth.allow-proxies";
 static const char kNegotiateAuthAllowNonFqdn[] = "network.negotiate-auth.allow-non-fqdn";
 static const char kNegotiateAuthSSPI[] = "network.auth.use-sspi";
 
 #define kNegotiateLen  (sizeof(kNegotiate)-1)
+#define DEFAULT_THREAD_TIMEOUT_MS 30000
 
 //-----------------------------------------------------------------------------
 
 // Return false when the channel comes from a Private browsing window.
 static bool
 TestNotInPBMode(nsIHttpAuthenticableChannel *authChannel)
 {
     nsCOMPtr<nsIChannel> bareChannel = do_QueryInterface(authChannel);
@@ -179,17 +184,247 @@ nsHttpNegotiateAuth::ChallengeReceived(n
         return rv;
     }
 
     *continuationState = module;
     return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS(nsHttpNegotiateAuth, nsIHttpAuthenticator)
-   
+
+namespace {
+
+//
+// GetNextTokenCompleteEvent
+//
+// This event is fired on main thread when async call of
+// nsHttpNegotiateAuth::GenerateCredentials is finished. During the Run()
+// method the nsIHttpAuthenticatorCallback::OnCredsAvailable is called with
+// obtained credentials, flags and NS_OK when successful, otherwise 
+// NS_ERROR_FAILURE is returned as a result of failed operation.
+//
+class GetNextTokenCompleteEvent final : public nsIRunnable,
+                                        public nsICancelable
+{
+    virtual ~GetNextTokenCompleteEvent()
+    {
+        if (mCreds) {
+            free(mCreds);
+        }
+    };
+
+public:
+    NS_DECL_THREADSAFE_ISUPPORTS
+
+    explicit GetNextTokenCompleteEvent(nsIHttpAuthenticatorCallback* aCallback)
+        : mCallback(aCallback)
+        , mCreds(nullptr)
+        , mCancelled(false)
+    {
+    }
+
+    NS_IMETHODIMP DispatchSuccess(char *aCreds,
+                                  uint32_t aFlags,
+                                  nsISupports *aSessionState,
+                                  nsISupports *aContinuationState)
+    {
+        // Called from worker thread
+        MOZ_ASSERT(!NS_IsMainThread());
+
+        mCreds = aCreds;
+        mFlags = aFlags;
+        mResult = NS_OK;
+        mSessionState = aSessionState;
+        mContinuationState = aContinuationState;
+        return NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
+    }
+
+    NS_IMETHODIMP DispatchError()
+    {
+        // Called from worker thread
+        MOZ_ASSERT(!NS_IsMainThread());
+
+        mResult = NS_ERROR_FAILURE;
+        return NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL);
+    }
+
+    NS_IMETHODIMP Run() override
+    {
+        // Runs on main thread
+        MOZ_ASSERT(NS_IsMainThread());
+
+        if (!mCancelled) {
+            nsCOMPtr<nsIHttpAuthenticatorCallback> callback;
+            callback.swap(mCallback);
+            callback->OnCredsGenerated(mCreds, mFlags, mResult, mSessionState, mContinuationState);
+        }
+        return NS_OK;
+    }
+
+    NS_IMETHODIMP Cancel(nsresult aReason) override
+    {
+        // Supposed to be called from main thread
+        MOZ_ASSERT(NS_IsMainThread());
+
+        mCancelled = true;
+        return NS_OK;
+    }
+
+private:
+    nsCOMPtr<nsIHttpAuthenticatorCallback> mCallback;
+    char *mCreds; // This class owns it, freed in destructor
+    uint32_t mFlags;
+    nsresult mResult;
+    bool mCancelled;
+    nsCOMPtr<nsISupports> mSessionState;
+    nsCOMPtr<nsISupports> mContinuationState;
+};
+
+NS_IMPL_ISUPPORTS(GetNextTokenCompleteEvent, nsIRunnable, nsICancelable)
+
+//
+// GetNextTokenRunnable
+//
+// This runnable is created by GenerateCredentialsAsync and it runs
+// in nsHttpNegotiateAuth::mNegotiateThread and calling GenerateCredentials.
+//
+class GetNextTokenRunnable final : public mozilla::Runnable
+{
+    virtual ~GetNextTokenRunnable() {}
+    public:
+        GetNextTokenRunnable(nsIHttpAuthenticableChannel *authChannel,
+                             const char *challenge,
+                             bool isProxyAuth,
+                             const char16_t *domain,
+                             const char16_t *username,
+                             const char16_t *password,
+                             nsISupports *sessionState,
+                             nsISupports *continuationState,
+                             GetNextTokenCompleteEvent *aCompleteEvent
+                             )
+            : mAuthChannel(authChannel)
+            , mChallenge(challenge)
+            , mIsProxyAuth(isProxyAuth)
+            , mDomain(domain)
+            , mUsername(username)
+            , mPassword(password)
+            , mSessionState(sessionState)
+            , mContinuationState(continuationState)
+            , mCompleteEvent(aCompleteEvent)
+        {
+        }
+
+        NS_IMETHODIMP Run() override
+        {
+           // Runs on worker thread
+           MOZ_ASSERT(!NS_IsMainThread());
+
+           char *creds;
+           uint32_t flags;
+           nsresult rv = ObtainCredentialsAndFlags(&creds, &flags);
+           if (NS_FAILED(rv)) {
+               return mCompleteEvent->DispatchError();
+           }
+
+           return mCompleteEvent->DispatchSuccess(creds, flags,  mSessionState, mContinuationState);
+        }
+
+        NS_IMETHODIMP ObtainCredentialsAndFlags(char **aCreds, uint32_t *aFlags)
+        {
+           // Use negotiate service to call GenerateCredentials outside of main thread
+           nsAutoCString contractId;
+           contractId.Assign(NS_HTTP_AUTHENTICATOR_CONTRACTID_PREFIX);
+           contractId.Append("negotiate");
+           nsresult rv;
+           nsCOMPtr<nsIHttpAuthenticator> authenticator = do_GetService(contractId.get(), &rv);
+           NS_ENSURE_SUCCESS(rv, rv);
+           nsISupports *sessionState = mSessionState;
+           nsISupports *continuationState = mContinuationState;
+           // The continuationState is for the sake of completeness propagated
+           // to the caller (despite it is not changed in any GenerateCredentials
+           // implementation).
+           //
+           // The only implementation that use sessionState is the
+           // nsHttpDigestAuth::GenerateCredentials. Since there's no reason
+           // to implement nsHttpDigestAuth::GenerateCredentialsAsync
+           // because digest auth does not block the main thread, we won't
+           // propagate changes to sessionState to the caller because of
+           // the change is too complicated on the caller side.
+           rv = authenticator->GenerateCredentials(mAuthChannel,
+                                                   mChallenge.get(),
+                                                   mIsProxyAuth,
+                                                   mDomain.get(),
+                                                   mUsername.get(),
+                                                   mPassword.get(),
+                                                   &sessionState,
+                                                   &continuationState,
+                                                   aFlags,
+                                                   aCreds);
+           mSessionState = sessionState;
+           mContinuationState = continuationState;
+           return rv;
+        }
+    private:
+        nsCOMPtr<nsIHttpAuthenticableChannel> mAuthChannel;
+        nsCString mChallenge;
+        bool mIsProxyAuth;
+        nsString mDomain;
+        nsString mUsername;
+        nsString mPassword;
+        nsCOMPtr<nsISupports> mSessionState;
+        nsCOMPtr<nsISupports> mContinuationState;
+        RefPtr<GetNextTokenCompleteEvent> mCompleteEvent;
+};
+
+} // anonymous namespace
+
+NS_IMETHODIMP
+nsHttpNegotiateAuth::GenerateCredentialsAsync(nsIHttpAuthenticableChannel *authChannel,
+                                              nsIHttpAuthenticatorCallback* aCallback,
+                                              const char *challenge,
+                                              bool isProxyAuth,
+                                              const char16_t *domain,
+                                              const char16_t *username,
+                                              const char16_t *password,
+                                              nsISupports *sessionState,
+                                              nsISupports *continuationState,
+                                              nsICancelable **aCancelable)
+{
+   NS_ENSURE_ARG(aCallback);
+   NS_ENSURE_ARG_POINTER(aCancelable);
+
+   RefPtr<GetNextTokenCompleteEvent> cancelEvent =
+       new GetNextTokenCompleteEvent(aCallback);
+
+
+   nsCOMPtr<nsIRunnable> getNextTokenRunnable =
+       new GetNextTokenRunnable(authChannel,
+                                challenge,
+                                isProxyAuth,
+                                domain,
+                                username,
+                                password,
+                                sessionState,
+                                continuationState,
+                                cancelEvent);
+   cancelEvent.forget(aCancelable);
+
+   nsresult rv;
+   if (!mNegotiateThread) {
+       mNegotiateThread =
+           new mozilla::LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
+                                       NS_LITERAL_CSTRING("NegotiateAuth"));
+       NS_ENSURE_TRUE(mNegotiateThread, NS_ERROR_OUT_OF_MEMORY);
+   }
+   rv = mNegotiateThread->Dispatch(getNextTokenRunnable, NS_DISPATCH_NORMAL);
+   NS_ENSURE_SUCCESS(rv, rv);
+
+   return NS_OK;
+}
+
 //
 // GenerateCredentials
 //
 // This routine is responsible for creating the correct authentication
 // blob to pass to the server that requested "Negotiate" authentication.
 //
 NS_IMETHODIMP
 nsHttpNegotiateAuth::GenerateCredentials(nsIHttpAuthenticableChannel *authChannel,
--- a/extensions/auth/nsHttpNegotiateAuth.h
+++ b/extensions/auth/nsHttpNegotiateAuth.h
@@ -5,24 +5,25 @@
 
 #ifndef nsHttpNegotiateAuth_h__
 #define nsHttpNegotiateAuth_h__
 
 #include "nsIHttpAuthenticator.h"
 #include "nsIURI.h"
 #include "nsSubstring.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/LazyIdleThread.h"
 
 // The nsHttpNegotiateAuth class provides responses for the GSS-API Negotiate method
 // as specified by Microsoft in draft-brezak-spnego-http-04.txt
 
 class nsHttpNegotiateAuth final : public nsIHttpAuthenticator
 {
 public:
-    NS_DECL_ISUPPORTS
+    NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSIHTTPAUTHENTICATOR
 
 private:
     ~nsHttpNegotiateAuth() {}
 
     // returns the value of the given boolean pref
     bool TestBoolPref(const char *pref);
 
@@ -32,10 +33,12 @@ private:
     // returns true if URI is accepted by the list of hosts in the pref
     bool TestPref(nsIURI *, const char *pref);
 
     bool MatchesBaseURI(const nsCSubstring &scheme,
                           const nsCSubstring &host,
                           int32_t             port,
                           const char         *baseStart,
                           const char         *baseEnd);
+    // Thread for GenerateCredentialsAsync
+    RefPtr<mozilla::LazyIdleThread> mNegotiateThread;
 };
 #endif /* nsHttpNegotiateAuth_h__ */
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -1013,19 +1013,23 @@ DrawTargetSkia::FillGlyphs(ScaledFont *a
   if (ShouldUseCGToFillGlyphs(aRenderingOptions, aPattern)) {
     if (FillGlyphsWithCG(aFont, aBuffer, aPattern, aOptions, aRenderingOptions)) {
       return;
     }
   }
 #endif
 
   ScaledFontBase* skiaFont = static_cast<ScaledFontBase*>(aFont);
+  SkTypeface* typeface = skiaFont->GetSkTypeface();
+  if (!typeface) {
+    return;
+  }
 
   AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern);
-  paint.mPaint.setTypeface(skiaFont->GetSkTypeface());
+  paint.mPaint.setTypeface(typeface);
   paint.mPaint.setTextSize(SkFloatToScalar(skiaFont->mSize));
   paint.mPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
 
   bool shouldLCDRenderText = ShouldLCDRenderText(aFont->GetType(), aOptions.mAntialiasMode);
   paint.mPaint.setLCDRenderText(shouldLCDRenderText);
 
   if (aRenderingOptions && aRenderingOptions->GetType() == FontType::CAIRO) {
     const GlyphRenderingOptionsCairo* cairoOptions =
--- a/gfx/2d/ScaledFontDWrite.cpp
+++ b/gfx/2d/ScaledFontDWrite.cpp
@@ -110,50 +110,90 @@ ScaledFontDWrite::GetPathForGlyphs(const
 
   CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
 
   return pathBuilder->Finish();
 }
 
 
 #ifdef USE_SKIA
+bool
+ScaledFontDWrite::DefaultToArialFont(IDWriteFontCollection* aSystemFonts)
+{
+  // If we can't find the same font face as we're given, fallback to arial
+  static const WCHAR fontFamilyName[] = L"Arial";
+
+  UINT32 fontIndex;
+  BOOL exists;
+  HRESULT hr = aSystemFonts->FindFamilyName(fontFamilyName, &fontIndex, &exists);
+  if (FAILED(hr)) {
+    gfxCriticalNote << "Failed to get backup arial font font from system fonts. Code: " << hexa(hr);
+    return false;
+  }
+
+  hr = aSystemFonts->GetFontFamily(fontIndex, getter_AddRefs(mFontFamily));
+  if (FAILED(hr)) {
+    gfxCriticalNote << "Failed to get font family for arial. Code: " << hexa(hr);
+    return false;
+  }
+
+  hr = mFontFamily->GetFirstMatchingFont(DWRITE_FONT_WEIGHT_NORMAL,
+                                         DWRITE_FONT_STRETCH_NORMAL,
+                                         DWRITE_FONT_STYLE_NORMAL,
+                                         getter_AddRefs(mFont));
+  if (FAILED(hr)) {
+    gfxCriticalNote << "Failed to get a matching font for arial. Code: " << hexa(hr);
+    return false;
+  }
+
+  return true;
+}
+
 // This can happen if we have mixed backends which create DWrite
 // fonts in a mixed environment. e.g. a cairo content backend
 // but Skia canvas backend.
-void
+bool
 ScaledFontDWrite::GetFontDataFromSystemFonts(IDWriteFactory* aFactory)
 {
   MOZ_ASSERT(mFontFace);
   RefPtr<IDWriteFontCollection> systemFonts;
   HRESULT hr = aFactory->GetSystemFontCollection(getter_AddRefs(systemFonts));
   if (FAILED(hr)) {
-    gfxWarning() << "Failed to get system font collection from file data. Code: " << hexa(hr);
-    return;
+    gfxCriticalNote << "Failed to get system font collection from file data. Code: " << hexa(hr);
+    return false;
   }
 
   hr = systemFonts->GetFontFromFontFace(mFontFace, getter_AddRefs(mFont));
   if (FAILED(hr)) {
-    gfxWarning() << "Failed to get system font from font face. Code: " << hexa(hr);
-    return;
+    gfxCriticalNote << "Failed to get system font from font face. Code: " << hexa(hr);
+    return DefaultToArialFont(systemFonts);
   }
 
   hr = mFont->GetFontFamily(getter_AddRefs(mFontFamily));
   if (FAILED(hr)) {
-    gfxWarning() << "Failed to get font family from font face. Code: " << hexa(hr);
-    return;
+    gfxCriticalNote << "Failed to get font family from font face. Code: " << hexa(hr);
+    return DefaultToArialFont(systemFonts);
   }
+
+  return true;
 }
 
 SkTypeface*
 ScaledFontDWrite::GetSkTypeface()
 {
   if (!mTypeface) {
     IDWriteFactory *factory = DrawTargetD2D1::GetDWriteFactory();
+    if (!factory) {
+      return nullptr;
+    }
+
     if (!mFont || !mFontFamily) {
-      GetFontDataFromSystemFonts(factory);
+      if (!GetFontDataFromSystemFonts(factory)) {
+        return nullptr;
+      }
     }
 
     mTypeface = SkCreateTypefaceFromDWriteFont(factory, mFontFace, mFont, mFontFamily);
   }
   return mTypeface;
 }
 #endif
 
--- a/gfx/2d/ScaledFontDWrite.h
+++ b/gfx/2d/ScaledFontDWrite.h
@@ -41,17 +41,18 @@ public:
   void CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink);
 
   virtual bool GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton);
 
   virtual AntialiasMode GetDefaultAAMode();
 
 #ifdef USE_SKIA
   virtual SkTypeface* GetSkTypeface();
-  void GetFontDataFromSystemFonts(IDWriteFactory* aFactory);
+  bool GetFontDataFromSystemFonts(IDWriteFactory* aFactory);
+  bool DefaultToArialFont(IDWriteFontCollection* aSystemFonts);
 #endif
 
   // The font and font family are only used with Skia
   RefPtr<IDWriteFont> mFont;
   RefPtr<IDWriteFontFamily> mFontFamily;
   RefPtr<IDWriteFontFace> mFontFace;
 
 protected:
--- a/gfx/config/gfxFeature.h
+++ b/gfx/config/gfxFeature.h
@@ -15,17 +15,17 @@ namespace mozilla {
 namespace gfx {
 
 #define GFX_FEATURE_MAP(_)                                                        \
   /* Name,                        Type,         Description */                    \
   _(HW_COMPOSITING,               Feature,      "Compositing")                    \
   _(D3D11_COMPOSITING,            Feature,      "Direct3D11 Compositing")         \
   _(D3D9_COMPOSITING,             Feature,      "Direct3D9 Compositing")          \
   _(DIRECT2D,                     Feature,      "Direct2D")                       \
-  _(D3D11_ANGLE,                  Feature,      "Direct3D11 ANGLE")               \
+  _(D3D11_HW_ANGLE,               Feature,      "Direct3D11 hardware ANGLE")               \
   /* Add new entries above this comment */
 
 enum class Feature : uint32_t {
 #define MAKE_ENUM(name, type, desc) name,
   GFX_FEATURE_MAP(MAKE_ENUM)
 #undef MAKE_ENUM
   NumValues
 };
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -165,25 +165,25 @@ GetAndInitDisplay(GLLibraryEGL& egl, voi
     return display;
 }
 
 static EGLDisplay
 GetAndInitDisplayForAccelANGLE(GLLibraryEGL& egl)
 {
     EGLDisplay ret = 0;
 
-    FeatureState& d3d11ANGLE = gfxConfig::GetFeature(Feature::D3D11_ANGLE);
+    FeatureState& d3d11ANGLE = gfxConfig::GetFeature(Feature::D3D11_HW_ANGLE);
 
     if (!gfxPrefs::WebGLANGLETryD3D11())
         d3d11ANGLE.UserDisable("User disabled D3D11 ANGLE by pref");
 
     if (gfxPrefs::WebGLANGLEForceD3D11())
         d3d11ANGLE.UserForceEnable("User force-enabled D3D11 ANGLE on disabled hardware");
 
-    if (gfxConfig::IsForcedOnByUser(Feature::D3D11_ANGLE))
+    if (gfxConfig::IsForcedOnByUser(Feature::D3D11_HW_ANGLE))
         return GetAndInitDisplay(egl, LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE);
 
     if (d3d11ANGLE.IsEnabled()) {
         ret = GetAndInitDisplay(egl, LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE);
     }
 
     if (!ret) {
         ret = GetAndInitDisplay(egl, EGL_DEFAULT_DISPLAY);
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -214,20 +214,20 @@ LayerManager::AreComponentAlphaLayersEna
 }
 
 /*static*/ void
 LayerManager::LayerUserDataDestroy(void* data)
 {
   delete static_cast<LayerUserData*>(data);
 }
 
-nsAutoPtr<LayerUserData>
+UniquePtr<LayerUserData>
 LayerManager::RemoveUserData(void* aKey)
 {
-  nsAutoPtr<LayerUserData> d(static_cast<LayerUserData*>(mUserData.Remove(static_cast<gfx::UserDataKey*>(aKey))));
+  UniquePtr<LayerUserData> d(static_cast<LayerUserData*>(mUserData.Remove(static_cast<gfx::UserDataKey*>(aKey))));
   return d;
 }
 
 //--------------------------------------------------
 // Layer
 
 Layer::Layer(LayerManager* aManager, void* aImplData) :
   mManager(aManager),
@@ -2143,20 +2143,20 @@ Layer::IsBackfaceHidden()
         return container->GetEffectiveTransform().IsBackfaceVisible();
       }
       return container->GetBaseTransform().IsBackfaceVisible();
     }
   }
   return false;
 }
 
-nsAutoPtr<LayerUserData>
+UniquePtr<LayerUserData>
 Layer::RemoveUserData(void* aKey)
 {
-  nsAutoPtr<LayerUserData> d(static_cast<LayerUserData*>(mUserData.Remove(static_cast<gfx::UserDataKey*>(aKey))));
+  UniquePtr<LayerUserData> d(static_cast<LayerUserData*>(mUserData.Remove(static_cast<gfx::UserDataKey*>(aKey))));
   return d;
 }
 
 void
 PaintedLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix)
 {
   Layer::PrintInfo(aStream, aPrefix);
   if (!mValidRegion.IsEmpty()) {
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -500,17 +500,17 @@ public:
    */
   void SetUserData(void* aKey, LayerUserData* aData)
   {
     mUserData.Add(static_cast<gfx::UserDataKey*>(aKey), aData, LayerUserDataDestroy);
   }
   /**
    * This can be used anytime. Ownership passes to the caller!
    */
-  nsAutoPtr<LayerUserData> RemoveUserData(void* aKey);
+  UniquePtr<LayerUserData> RemoveUserData(void* aKey);
 
   /**
    * This getter can be used anytime.
    */
   bool HasUserData(void* aKey)
   {
     return mUserData.Has(static_cast<gfx::UserDataKey*>(aKey));
   }
@@ -1457,17 +1457,17 @@ public:
    */
   void SetUserData(void* aKey, LayerUserData* aData)
   {
     mUserData.Add(static_cast<gfx::UserDataKey*>(aKey), aData, LayerManager::LayerUserDataDestroy);
   }
   /**
    * This can be used anytime. Ownership passes to the caller!
    */
-  nsAutoPtr<LayerUserData> RemoveUserData(void* aKey);
+  UniquePtr<LayerUserData> RemoveUserData(void* aKey);
   /**
    * This getter can be used anytime.
    */
   bool HasUserData(void* aKey)
   {
     return mUserData.Has(static_cast<gfx::UserDataKey*>(aKey));
   }
   /**
--- a/gfx/layers/apz/test/mochitest/mochitest.ini
+++ b/gfx/layers/apz/test/mochitest/mochitest.ini
@@ -42,8 +42,10 @@ skip-if = (os == 'android') || (os == 'b
 [test_frame_reconstruction.html]
 [test_group_touchevents.html]
 # Windows touch injection doesn't work in automation, but this test can be run locally on a windows touch device.
 # On OS X we don't support touch events at all.
 skip-if = (toolkit == 'windows') || (toolkit == 'cocoa')
 [test_group_wheelevents.html]
 skip-if = (toolkit == 'android') # wheel events not supported on mobile
 [test_group_mouseevents.html]
+[test_touch_listeners_impacting_wheel.html]
+skip-if = (toolkit == 'android') || (toolkit == 'cocoa') # wheel events not supported on mobile, and synthesized wheel smooth-scrolling not supported on OS X
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/test/mochitest/test_touch_listeners_impacting_wheel.html
@@ -0,0 +1,159 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1203140
+-->
+<head>
+  <title>Test for Bug 1203140</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
+  <script type="application/javascript" src="apz_test_utils.js"></script>
+  <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <style>
+  </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1203140">Mozilla Bug 1203140</a>
+<p id="display"></p>
+<div id="content" style="overflow-y:scroll; height: 400px">
+  <p>The box below has a touch listener and a passive wheel listener. With touch events disabled, APZ shouldn't wait for any listeners.</p>
+  <div id="box" style="width: 200px; height: 200px; background-color: blue"></div>
+  <div style="height: 1000px; width: 10px">Div to make 'content' scrollable</div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+const kResponseTimeoutMs = 2 * 60 * 1000; // 2 minutes
+
+function parentProcessSnapshot() {
+  addMessageListener('snapshot', function(rect) {
+    Components.utils.import('resource://gre/modules/Services.jsm');
+    var topWin = Services.wm.getMostRecentWindow('navigator:browser');
+
+    // reposition the rect relative to the top-level browser window
+    rect = JSON.parse(rect);
+    rect.x -= topWin.mozInnerScreenX;
+    rect.y -= topWin.mozInnerScreenY;
+
+    // take the snapshot
+    var canvas = topWin.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+    canvas.width = rect.w;
+    canvas.height = rect.h;
+    var ctx = canvas.getContext("2d");
+    ctx.drawWindow(topWin, rect.x, rect.y, rect.w, rect.h, 'rgb(255,255,255)', ctx.DRAWWINDOW_DRAW_VIEW | ctx.DRAWWINDOW_USE_WIDGET_LAYERS | ctx.DRAWWINDOW_DRAW_CARET);
+    return canvas.toDataURL();
+  });
+}
+
+function takeSnapshots(e) {
+  // Grab some snapshots, and make sure some of them are different (i.e. check
+  // the page is scrolling in the compositor, concurrently with this wheel
+  // listener running).
+  // Note that we want this function to take less time than the content response
+  // timeout, otherwise the scrolling will start even if we haven't returned,
+  // and that would invalidate purpose of the test.
+  var start = Date.now();
+  var lastSnapshot = null;
+  var success = false;
+
+  var chromeHelper = SpecialPowers.loadChromeScript(parentProcessSnapshot);
+  SimpleTest.registerCleanupFunction(function() { chromeHelper.destroy() });
+
+  // Get the position of the 'content' div relative to the screen
+  var contentDiv = document.getElementById('content');
+  var rect = coordinatesRelativeToWindow(0, 0, contentDiv);
+  rect.w = contentDiv.getBoundingClientRect().width * window.devicePixelRatio;
+  rect.h = contentDiv.getBoundingClientRect().height * window.devicePixelRatio;
+
+  for (var i = 0; i < 10; i++) {
+    SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(16);
+    var snapshot = chromeHelper.sendSyncMessage('snapshot', JSON.stringify(rect)).toString();
+    //dump("Took snapshot " + snapshot + "\n"); // this might help with debugging
+
+    if (lastSnapshot && lastSnapshot != snapshot) {
+      ok(true, "Found some different pixels in snapshot " + i + " compared to previous");
+      success = true;
+    }
+    lastSnapshot = snapshot;
+  }
+  ok(success, "Found some snapshots that were different");
+  ok((Date.now() - start) < kResponseTimeoutMs, "Snapshotting ran quickly enough");
+
+  // Until now, no scroll events will have been dispatched to content. That's
+  // because scroll events are dispatched on the main thread, which we've been
+  // hogging with the code above. At this point we restore the normal refresh
+  // behaviour and let the main thread go back to C++ code, so the scroll events
+  // fire and we unwind from the main test continuation.
+  SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
+}
+
+function* runTest() {
+  var box = document.getElementById('box');
+  box.addEventListener('touchstart', function(e) {
+    ok(false, "This should never be run");
+  }, false);
+  box.addEventListener('wheel', takeSnapshots, { capture: false, passive: true });
+
+  // Let the event regions propagate to the APZ
+  yield waitForAllPaints(function() {
+    flushApzRepaints(driveTest);
+  });
+
+  // Take over control of the refresh driver and compositor
+  var utils = SpecialPowers.DOMWindowUtils;
+  utils.advanceTimeAndRefresh(0);
+
+  // Trigger an APZ scroll using a wheel event. If APZ is waiting for a
+  // content response, it will wait for takeSnapshots to finish running before
+  // it starts scrolling, which will cause the checks in takeSnapshots to fail.
+  yield synthesizeNativeMouseMoveAndWaitForMoveEvent(box, 10, 10, driveTest);
+  yield synthesizeNativeWheelAndWaitForScrollEvent(box, 10, 10, 0, -50, driveTest);
+}
+
+var gTestContinuation = null;
+function driveTest() {
+  if (!gTestContinuation) {
+    gTestContinuation = runTest();
+  }
+  var ret = gTestContinuation.next();
+  if (ret.done) {
+    SimpleTest.finish();
+  }
+}
+
+function startTest() {
+  // This test requires APZ - if it's not enabled, skip it.
+  var apzEnabled = SpecialPowers.getDOMWindowUtils(window).asyncPanZoomEnabled;
+  if (!apzEnabled) {
+    ok(true, "APZ not enabled, skipping test");
+    SimpleTest.finish();
+    return;
+  }
+
+  waitForAllPaints(function() {
+    flushApzRepaints(driveTest);
+  })
+}
+
+SimpleTest.waitForExplicitFinish();
+
+// Disable touch events, so that APZ knows not to wait for touch listeners.
+// Also explicitly set the content response timeout, so we know how long it
+// is (see comment in takeSnapshots).
+// Finally, enable smooth scrolling, so that the wheel-scroll we do as part
+// of the test triggers an APZ animation rather than doing an instant scroll.
+// Note that this pref doesn't work for the synthesized wheel events on OS X,
+// those are hard-coded to be instant scrolls.
+SpecialPowers.pushPrefEnv({"set": [["dom.w3c_touch_events.enabled", 0],
+                                   ["apz.content_response_timeout", kResponseTimeoutMs],
+                                   ["general.smoothscroll", true]]},
+                          function() {
+                            SimpleTest.waitForFocus(startTest, window);
+                          });
+</script>
+</pre>
+
+</body>
+</html>
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -501,18 +501,19 @@ BasicCompositor::BeginFrame(const nsIntR
 
   if (aRenderBoundsOut) {
     *aRenderBoundsOut = IntRect();
   }
 
   BufferMode bufferMode = BufferMode::BUFFERED;
   if (mTarget) {
     // If we have a copy target, then we don't have a widget-provided mDrawTarget (currently). Use a dummy
-    // placeholder so that CreateRenderTarget() works.
-    mDrawTarget = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
+    // placeholder so that CreateRenderTarget() works. This is only used to create a new buffered
+    // draw target that we composite into, then copy the results the destination.
+    mDrawTarget = mTarget;
   } else {
     // StartRemoteDrawingInRegion can mutate mInvalidRegion.
     mDrawTarget = mWidget->StartRemoteDrawingInRegion(mInvalidRegion, &bufferMode);
     if (!mDrawTarget) {
       return;
     }
     mInvalidRect = mInvalidRegion.GetBounds();
     if (mInvalidRect.IsEmpty()) {
--- a/gfx/src/nsThemeConstants.h
+++ b/gfx/src/nsThemeConstants.h
@@ -54,17 +54,17 @@
 // A status bar in a main application window.
 #define NS_THEME_STATUSBAR                                 23
 
 // A single pane of a status bar.
 #define NS_THEME_STATUSBARPANEL                            24
 
 // The resizer background area in a status bar 
 // for the resizer widget in the corner of a window.
-#define NS_THEME_RESIZER_PANEL                             25
+#define NS_THEME_RESIZERPANEL                              25
 
 // The resizer itself.
 #define NS_THEME_RESIZER                                   26
 
 // List boxes
 #define NS_THEME_LISTBOX                                   31
 
 // A listbox item
@@ -169,17 +169,17 @@
 
 // A non-disappearing scrollbar.
 #define NS_THEME_SCROLLBAR_NON_DISAPPEARING                92
 
 // A textfield or text area
 #define NS_THEME_TEXTFIELD                                 95
 
 // The caret of a text area
-#define NS_THEME_TEXTFIELD_CARET                           96
+#define NS_THEME_CARET                                     96
 
 // A multiline text field
 #define NS_THEME_TEXTFIELD_MULTILINE                       97
 
 // A searchfield
 #define NS_THEME_SEARCHFIELD                               98
 
 // A dropdown list.
--- a/gfx/tests/gtest/TestLayers.cpp
+++ b/gfx/tests/gtest/TestLayers.cpp
@@ -145,19 +145,19 @@ TEST(Layers, UserData) {
   TestUserData* data2 = new TestUserData;
   TestUserData* data3 = new TestUserData;
 
   layer.SetUserData(key1, data1);
   layer.SetUserData(key2, data2);
   layer.SetUserData(key3, data3);
 
   // Also checking that the user data is returned but not free'd
-  UniquePtr<LayerUserData> d1(layer.RemoveUserData(key1).forget());
-  UniquePtr<LayerUserData> d2(layer.RemoveUserData(key2).forget());
-  UniquePtr<LayerUserData> d3(layer.RemoveUserData(key3).forget());
+  UniquePtr<LayerUserData> d1(layer.RemoveUserData(key1));
+  UniquePtr<LayerUserData> d2(layer.RemoveUserData(key2));
+  UniquePtr<LayerUserData> d3(layer.RemoveUserData(key3));
   ASSERT_EQ(data1, d1.get());
   ASSERT_EQ(data2, d2.get());
   ASSERT_EQ(data3, d3.get());
 
   layer.SetUserData(key1, d1.release());
   layer.SetUserData(key2, d2.release());
   layer.SetUserData(key3, d3.release());
 
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2062,17 +2062,16 @@ gfxPlatform::OptimalFormatForContent(gfx
 
 /**
  * There are a number of layers acceleration (or layers in general) preferences
  * that should be consistent for the lifetime of the application (bug 840967).
  * As such, we will evaluate them all as soon as one of them is evaluated
  * and remember the values.  Changing these preferences during the run will
  * not have any effect until we restart.
  */
-bool gANGLESupportsD3D11 = false;
 static mozilla::Atomic<bool> sLayersSupportsHardwareVideoDecoding(false);
 static bool sLayersHardwareVideoDecodingFailed = false;
 static bool sBufferRotationCheckPref = true;
 static bool sPrefBrowserTabsRemoteAutostart = false;
 
 static mozilla::Atomic<bool> sLayersAccelerationPrefsInitialized(false);
 
 void
@@ -2091,25 +2090,16 @@ gfxPlatform::InitAcceleration()
   MOZ_ASSERT(NS_IsMainThread(), "can only initialize prefs on the main thread");
 
   gfxPrefs::GetSingleton();
   sPrefBrowserTabsRemoteAutostart = BrowserTabsRemoteAutostart();
 
   nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
   nsCString discardFailureId;
   int32_t status;
-#ifdef XP_WIN
-  if (!gfxPrefs::LayersAccelerationDisabledDoNotUseDirectly() && gfxInfo) {
-    if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE, discardFailureId, &status))) {
-      if (status == nsIGfxInfo::FEATURE_STATUS_OK) {
-        gANGLESupportsD3D11 = true;
-      }
-    }
-  }
-#endif
 
   if (Preferences::GetBool("media.hardware-video-decoding.enabled", false) &&
 #ifdef XP_WIN
     Preferences::GetBool("media.windows-media-foundation.use-dxva", true) &&
 #endif
       NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING,
                                                discardFailureId, &status))) {
       if (status == nsIGfxInfo::FEATURE_STATUS_OK || gfxPrefs::HardwareVideoDecodingForceEnabled()) {
--- a/gfx/thebes/gfxTextRun.h
+++ b/gfx/thebes/gfxTextRun.h
@@ -92,51 +92,51 @@ public:
     virtual ~gfxTextRun();
 
     typedef gfxFont::RunMetrics Metrics;
     typedef mozilla::gfx::DrawTarget DrawTarget;
 
     // Public textrun API for general use
 
     bool IsClusterStart(uint32_t aPos) const {
-        NS_ASSERTION(aPos < GetLength(), "aPos out of range");
+        MOZ_ASSERT(aPos < GetLength());
         return mCharacterGlyphs[aPos].IsClusterStart();
     }
     bool IsLigatureGroupStart(uint32_t aPos) const {
-        NS_ASSERTION(aPos < GetLength(), "aPos out of range");
+        MOZ_ASSERT(aPos < GetLength());
         return mCharacterGlyphs[aPos].IsLigatureGroupStart();
     }
     bool CanBreakLineBefore(uint32_t aPos) const {
         return CanBreakBefore(aPos) == CompressedGlyph::FLAG_BREAK_TYPE_NORMAL;
     }
     bool CanHyphenateBefore(uint32_t aPos) const {
         return CanBreakBefore(aPos) == CompressedGlyph::FLAG_BREAK_TYPE_HYPHEN;
     }
 
     // Returns a gfxShapedText::CompressedGlyph::FLAG_BREAK_TYPE_* value
     // as defined in gfxFont.h (may be NONE, NORMAL or HYPHEN).
     uint8_t CanBreakBefore(uint32_t aPos) const {
-        NS_ASSERTION(aPos < GetLength(), "aPos out of range");
+        MOZ_ASSERT(aPos < GetLength());
         return mCharacterGlyphs[aPos].CanBreakBefore();
     }
 
     bool CharIsSpace(uint32_t aPos) const {
-        NS_ASSERTION(aPos < GetLength(), "aPos out of range");
+        MOZ_ASSERT(aPos < GetLength());
         return mCharacterGlyphs[aPos].CharIsSpace();
     }
     bool CharIsTab(uint32_t aPos) const {
-        NS_ASSERTION(aPos < GetLength(), "aPos out of range");
+        MOZ_ASSERT(aPos < GetLength());
         return mCharacterGlyphs[aPos].CharIsTab();
     }
     bool CharIsNewline(uint32_t aPos) const {
-        NS_ASSERTION(aPos < GetLength(), "aPos out of range");
+        MOZ_ASSERT(aPos < GetLength());
         return mCharacterGlyphs[aPos].CharIsNewline();
     }
     bool CharMayHaveEmphasisMark(uint32_t aPos) const {
-        NS_ASSERTION(aPos < GetLength(), "aPos out of range");
+        MOZ_ASSERT(aPos < GetLength());
         return mCharacterGlyphs[aPos].CharMayHaveEmphasisMark();
     }
 
     // All offsets are in terms of the string passed into MakeTextRun.
 
     // Describe range [start, end) of a text run. The range is
     // restricted to grapheme cluster boundaries.
     struct Range
@@ -389,17 +389,17 @@ public:
      * the maximal N such that
      *       N < aMaxLength && line break at N && GetAdvanceWidth(aStart, N) <= aWidth
      *   OR  N < aMaxLength && hyphen break at N && GetAdvanceWidth(aStart, N) + GetHyphenWidth() <= aWidth
      * or UINT32_MAX if no such N exists, where GetAdvanceWidth assumes
      * the effect of
      * SetLineBreaks(aStart, N, aLineBreakBefore, N < aMaxLength, aProvider)
      *
      * @param aCanWordWrap true if we can break between any two grapheme
-     * clusters. This is set by word-wrap: break-word
+     * clusters. This is set by overflow-wrap|word-wrap: break-word
      *
      * @param aBreakPriority in/out the priority of the break opportunity
      * saved in the line. If we are prioritizing break opportunities, we will
      * not set a break with a lower priority. @see gfxBreakPriority.
      * 
      * Note that negative advance widths are possible especially if negative
      * spacing is provided.
      */
@@ -508,17 +508,17 @@ public:
     nsresult AddGlyphRun(gfxFont *aFont, uint8_t aMatchType,
                          uint32_t aStartCharIndex, bool aForceNewRun,
                          uint16_t aOrientation);
     void ResetGlyphRuns() { mGlyphRuns.Clear(); }
     void SortGlyphRuns();
     void SanitizeGlyphRuns();
 
     CompressedGlyph* GetCharacterGlyphs() final {
-        NS_ASSERTION(mCharacterGlyphs, "failed to initialize mCharacterGlyphs");
+        MOZ_ASSERT(mCharacterGlyphs, "failed to initialize mCharacterGlyphs");
         return mCharacterGlyphs;
     }
 
     // clean out results from shaping in progress, used for fallback scenarios
     void ClearGlyphsAndCharacters();
 
     void SetSpaceGlyph(gfxFont* aFont, DrawTarget* aDrawTarget,
                        uint32_t aCharIndex, uint16_t aOrientation);
--- a/gfx/thebes/gfxTypes.h
+++ b/gfx/thebes/gfxTypes.h
@@ -19,17 +19,17 @@ typedef void (*thebes_destroy_func_t) (v
  */
 typedef double gfxFloat;
 
 /**
  * Priority of a line break opportunity.
  *
  * eNoBreak       The line has no break opportunities
  * eWordWrapBreak The line has a break opportunity only within a word. With
- *                word-wrap: break-word we will break at this point only if
+ *                overflow-wrap|word-wrap: break-word we will break at this point only if
  *                there are no other break opportunities in the line.
  * eNormalBreak   The line has a break opportunity determined by the standard
  *                line-breaking algorithm.
  *
  * Future expansion: split eNormalBreak into multiple priorities, e.g.
  *                    punctuation break and whitespace break (bug 389710).
  *                   As and when we implement it, text-wrap: unrestricted will
  *                    mean that priorities are ignored and all line-break
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -368,32 +368,41 @@ gfxWindowsPlatform::~gfxWindowsPlatform(
   mAdapter = nullptr;
 
   /* 
    * Uninitialize COM 
    */ 
   CoUninitialize();
 }
 
+static void
+UpdateANGLEConfig()
+{
+  if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
+    gfxConfig::Disable(Feature::D3D11_HW_ANGLE, FeatureStatus::Disabled, "D3D11 compositing is disabled");
+  }
+}
+
 void
 gfxWindowsPlatform::InitAcceleration()
 {
   gfxPlatform::InitAcceleration();
 
   // Set up the D3D11 feature levels we can ask for.
   if (IsWin8OrLater()) {
     mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_1);
   }
   mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_0);
   mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_1);
   mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_0);
   mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_9_3);
 
   InitializeConfig();
   InitializeDevices();
+  UpdateANGLEConfig();
   UpdateRenderMode();
 }
 
 bool
 gfxWindowsPlatform::CanUseHardwareVideoDecoding()
 {
   if (!gfxPrefs::LayersPreferD3D9() && !mCompositorD3D11TextureSharingWorks) {
     return false;
@@ -450,21 +459,24 @@ gfxWindowsPlatform::HandleDeviceReset()
   // Reset local state. Note: we leave feature status variables as-is. They
   // will be recomputed by InitializeDevices().
   mHasDeviceReset = false;
   mHasFakeDeviceReset = false;
   mHasD3D9DeviceReset = false;
   mCompositorD3D11TextureSharingWorks = false;
   mDeviceResetReason = DeviceResetReason::OK;
 
-  imgLoader::Singleton()->ClearCache(true);
-  imgLoader::Singleton()->ClearCache(false);
+  imgLoader::NormalLoader()->ClearCache(true);
+  imgLoader::NormalLoader()->ClearCache(false);
+  imgLoader::PrivateBrowsingLoader()->ClearCache(true);
+  imgLoader::PrivateBrowsingLoader()->ClearCache(false);
   gfxAlphaBoxBlur::ShutdownBlurCache();
 
   InitializeDevices();
+  UpdateANGLEConfig();
   BumpDeviceCounter();
   return true;
 }
 
 static const BackendType SOFTWARE_BACKEND = BackendType::CAIRO;
 
 void
 gfxWindowsPlatform::UpdateBackendPrefs()
@@ -1915,26 +1927,46 @@ IsWARPStable()
 {
   // It seems like nvdxgiwrap makes a mess of WARP. See bug 1154703.
   if (!IsWin8OrLater() || GetModuleHandleA("nvdxgiwrap.dll")) {
     return false;
   }
   return true;
 }
 
+static void
+InitializeANGLEConfig()
+{
+  FeatureState& d3d11ANGLE = gfxConfig::GetFeature(Feature::D3D11_HW_ANGLE);
+
+  if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
+    d3d11ANGLE.DisableByDefault(FeatureStatus::Unavailable, "D3D11 compositing is disabled");
+    return;
+  }
+
+  d3d11ANGLE.EnableByDefault();
+
+  nsCString message;
+  if (!IsGfxInfoStatusOkay(nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE, &message)) {
+    d3d11ANGLE.Disable(FeatureStatus::Blacklisted, message.get());
+  }
+
+}
+
 void
 gfxWindowsPlatform::InitializeConfig()
 {
   if (!XRE_IsParentProcess()) {
     // Child processes init their configuration via UpdateDeviceInitData().
     return;
   }
 
   InitializeD3D9Config();
   InitializeD3D11Config();
+  InitializeANGLEConfig();
   InitializeD2DConfig();
 }
 
 void
 gfxWindowsPlatform::InitializeD3D9Config()
 {
   MOZ_ASSERT(XRE_IsParentProcess());
 
@@ -2028,16 +2060,19 @@ gfxWindowsPlatform::UpdateDeviceInitData
   }
 
   gfxConfig::InitOrUpdate(
     Feature::DIRECT2D,
     GetParentDevicePrefs().useD2D1(),
     FeatureStatus::Disabled,
     "Disabled by parent process");
 
+
+  InitializeANGLEConfig();
+
   return true;
 }
 
 // We don't have access to the D3D11CreateDevice type in gfxWindowsPlatform.h,
 // since it doesn't include d3d11.h, so we use a static here. It should only
 // be used within InitializeD3D11.
 decltype(D3D11CreateDevice)* sD3D11CreateDeviceFn = nullptr;
 
@@ -2092,23 +2127,23 @@ gfxWindowsPlatform::AttemptD3D11DeviceCr
     mD3D11Device = device;
   }
 
   // Only test this when not using WARP since it can fail and cause
   // GetDeviceRemovedReason to return weird values.
   mCompositorD3D11TextureSharingWorks = ::DoesD3D11TextureSharingWork(mD3D11Device);
 
   if (!mCompositorD3D11TextureSharingWorks) {
-    gfxConfig::SetFailed(Feature::D3D11_ANGLE,
+    gfxConfig::SetFailed(Feature::D3D11_HW_ANGLE,
                          FeatureStatus::Broken,
                          "Texture sharing doesn't work");
   }
 
   if (DoesRenderTargetViewNeedsRecreating(mD3D11Device)) {
-    gfxConfig::SetFailed(Feature::D3D11_ANGLE,
+    gfxConfig::SetFailed(Feature::D3D11_HW_ANGLE,
                          FeatureStatus::Broken,
                          "RenderTargetViews need recreating");
   }
 
   mD3D11Device->SetExceptionMode(0);
   mIsWARP = false;
 }
 
@@ -2347,22 +2382,16 @@ gfxWindowsPlatform::InitializeDevices()
                          "Harware acceleration crashed during startup in a previous session");
     return;
   }
 
   // First, initialize D3D11. If this succeeds we attempt to use Direct2D.
   InitializeD3D11();
   InitializeD2D();
 
-  if (!gfxConfig::IsEnabled(Feature::D3D11_COMPOSITING)) {
-    gfxConfig::DisableByDefault(Feature::D3D11_ANGLE, FeatureStatus::Disabled, "D3D11 compositing is disabled");
-  } else {
-    gfxConfig::EnableByDefault(Feature::D3D11_ANGLE);
-  }
-
   if (!gfxConfig::IsEnabled(Feature::DIRECT2D)) {
     if (XRE_IsContentProcess() && GetParentDevicePrefs().useD2D1()) {
       RecordContentDeviceFailure(TelemetryDeviceCode::D2D1);
     }
 
     // Usually we want D2D in order to use DWrite, but if the users have it
     // forced, we'll let them have it, as unsupported configuration.
     if (gfxPrefs::DirectWriteFontRenderingForceEnabled() && !mDWriteFactory) {
--- a/hal/Hal.h
+++ b/hal/Hal.h
@@ -389,17 +389,17 @@ void GetCurrentScreenConfiguration(hal::
  * @param aScreenConfiguration The new screen orientation.
  */
 void NotifyScreenConfigurationChange(const hal::ScreenConfiguration& aScreenConfiguration);
 
 /**
  * Lock the screen orientation to the specific orientation.
  * @return Whether the lock has been accepted.
  */
-bool LockScreenOrientation(const dom::ScreenOrientationInternal& aOrientation);
+MOZ_MUST_USE bool LockScreenOrientation(const dom::ScreenOrientationInternal& aOrientation);
 
 /**
  * Unlock the screen orientation.
  */
 void UnlockScreenOrientation();
 
 /**
  * Register an observer for the switch of given SwitchDevice.
@@ -433,17 +433,17 @@ void NotifySwitchStateFromInputDevice(ha
                                       hal::SwitchState aState);
 
 /**
  * Register an observer that is notified when a programmed alarm
  * expires.
  *
  * Currently, there can only be 0 or 1 alarm observers.
  */
-bool RegisterTheOneAlarmObserver(hal::AlarmObserver* aObserver);
+MOZ_MUST_USE bool RegisterTheOneAlarmObserver(hal::AlarmObserver* aObserver);
 
 /**
  * Unregister the alarm observer.  Doing so will implicitly cancel any
  * programmed alarm.
  */
 void UnregisterTheOneAlarmObserver();
 
 /**
@@ -460,17 +460,17 @@ void NotifyAlarmFired();
  * real-time clock is changed; that is, this alarm respects changes to
  * the real-time clock.  Return true iff the alarm was programmed.
  *
  * The alarm can be reprogrammed at any time.
  *
  * This API is currently only allowed to be used from non-sandboxed
  * contexts.
  */
-bool SetAlarm(int32_t aSeconds, int32_t aNanoseconds);
+MOZ_MUST_USE bool SetAlarm(int32_t aSeconds, int32_t aNanoseconds);
 
 /**
  * Set the priority of the given process.
  *
  * Exactly what this does will vary between platforms.  On *nix we might give
  * background processes higher nice values.  On other platforms, we might
  * ignore this call entirely.
  */
@@ -580,17 +580,17 @@ void CancelFMRadioSeek();
 /**
  * Get FM radio band settings by country.
  */
 hal::FMRadioSettings GetFMBandSettings(hal::FMRadioCountry aCountry);
 
 /**
  * Enable RDS data reception
  */
-bool EnableRDS(uint32_t aMask);
+MOZ_MUST_USE bool EnableRDS(uint32_t aMask);
 
 /**
  * Disable RDS data reception
  */
 void DisableRDS();
 
 /**
  * Start a watchdog to compulsively shutdown the system if it hangs.
--- a/hal/HalInternal.h
+++ b/hal/HalInternal.h
@@ -62,17 +62,17 @@ void EnableSwitchNotifications(hal::Swit
 /**
  * Disable switch notifications from the backend
  */
 void DisableSwitchNotifications(hal::SwitchDevice aDevice);
 
 /**
  * Enable alarm notifications from the backend.
  */
-bool EnableAlarm();
+MOZ_MUST_USE bool EnableAlarm();
 
 /**
  * Disable alarm notifications from the backend.
  */
 void DisableAlarm();
 
 /**
  * Enable system clock change notifications from the backend.
--- a/image/build/nsImageModule.cpp
+++ b/image/build/nsImageModule.cpp
@@ -24,16 +24,18 @@
 #include "nsICOEncoder.h"
 #include "nsPNGEncoder.h"
 #include "nsJPEGEncoder.h"
 #include "nsBMPEncoder.h"
 
 // objects that just require generic constructors
 using namespace mozilla::image;
 
+// XXX We would like to get rid of the imgLoader factory constructor.  See the
+// comment documenting the imgLoader constructor.
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(imgLoader, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(imgRequestProxy)
 NS_GENERIC_FACTORY_CONSTRUCTOR(imgTools)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsICOEncoder)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsJPEGEncoder)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsPNGEncoder)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsBMPEncoder)
 NS_DEFINE_NAMED_CID(NS_IMGLOADER_CID);
@@ -80,19 +82,24 @@ static const mozilla::Module::CategoryEn
   { "Gecko-Content-Viewers", IMAGE_PNG, "@mozilla.org/content/document-loader-factory;1" },
   { "Gecko-Content-Viewers", IMAGE_X_PNG, "@mozilla.org/content/document-loader-factory;1" },
   { "content-sniffing-services", "@mozilla.org/image/loader;1", "@mozilla.org/image/loader;1" },
   { nullptr }
 };
 
 static bool sInitialized = false;
 nsresult
-mozilla::image::InitModule()
+mozilla::image::EnsureModuleInitialized()
 {
   MOZ_ASSERT(NS_IsMainThread());
+
+  if (sInitialized) {
+    return NS_OK;
+  }
+
   // Make sure the preferences are initialized
   gfxPrefs::GetSingleton();
 
   mozilla::image::ShutdownTracker::Initialize();
   mozilla::image::ImageFactory::Initialize();
   mozilla::image::DecodePool::Initialize();
   mozilla::image::SurfaceCache::Initialize();
   mozilla::image::SurfacePipe::Initialize();
@@ -113,16 +120,16 @@ mozilla::image::ShutdownModule()
 }
 
 static const mozilla::Module kImageModule = {
   mozilla::Module::kVersion,
   kImageCIDs,
   kImageContracts,
   kImageCategories,
   nullptr,
-  mozilla::image::InitModule,
+  mozilla::image::EnsureModuleInitialized,
   // We need to be careful about shutdown ordering to avoid intermittent crashes
   // when hashtable enumeration decides to destroy modules in an unfortunate
   // order. So our shutdown is invoked explicitly during layout module shutdown.
   nullptr
 };
 
 NSMODULE_DEFN(nsImageLib2Module) = &kImageModule;
--- a/image/build/nsImageModule.h
+++ b/image/build/nsImageModule.h
@@ -6,15 +6,15 @@
 #ifndef mozilla_image_build_nsImageModule_h
 #define mozilla_image_build_nsImageModule_h
 
 #include "nsError.h"
 
 namespace mozilla {
 namespace image {
 
-nsresult InitModule();
+nsresult EnsureModuleInitialized();
 void ShutdownModule();
 
 } /* namespace image */
 } /* namespace mozilla */
 
 #endif // mozilla_image_build_nsImageModule_h
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -6,16 +6,17 @@
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Move.h"
 #include "mozilla/Preferences.h"
  #include "mozilla/ChaosMode.h"
 
 #include "ImageLogging.h"
+#include "nsImageModule.h"
 #include "nsPrintfCString.h"
 #include "imgLoader.h"
 #include "imgRequestProxy.h"
 
 #include "nsCOMPtr.h"
 
 #include "nsContentPolicyUtils.h"
 #include "nsContentUtils.h"
@@ -1128,60 +1129,59 @@ imgCacheExpirationTracker::NotifyExpired
 
 double imgLoader::sCacheTimeWeight;
 uint32_t imgLoader::sCacheMaxSize;
 imgMemoryReporter* imgLoader::sMemReporter;
 
 NS_IMPL_ISUPPORTS(imgLoader, imgILoader, nsIContentSniffer, imgICache,
                   nsISupportsWeakReference, nsIObserver)
 
-static imgLoader* gSingleton = nullptr;
-static imgLoader* gPBSingleton = nullptr;
-
-imgLoader*
-imgLoader::Singleton()
+static imgLoader* gNormalLoader = nullptr;
+static imgLoader* gPrivateBrowsingLoader = nullptr;
+
+/* static */ already_AddRefed<imgLoader>
+imgLoader::CreateImageLoader()
 {
-  if (!gSingleton) {
-    gSingleton = imgLoader::Create().take();
-  }
-  return gSingleton;
+  // In some cases, such as xpctests, XPCOM modules are not automatically
+  // initialized.  We need to make sure that our module is initialized before
+  // we hand out imgLoader instances and code starts using them.
+  mozilla::image::EnsureModuleInitialized();
+
+  RefPtr<imgLoader> loader = new imgLoader();
+  loader->Init();
+
+  return loader.forget();
 }
 
 imgLoader*
-imgLoader::PBSingleton()
+imgLoader::NormalLoader()
 {
-  if (!gPBSingleton) {
-    gPBSingleton = imgLoader::Create().take();
-    gPBSingleton->RespectPrivacyNotifications();
+  if (!gNormalLoader) {
+    gNormalLoader = CreateImageLoader().take();
   }
-  return gPBSingleton;
+  return gNormalLoader;
+}
+
+imgLoader*
+imgLoader::PrivateBrowsingLoader()
+{
+  if (!gPrivateBrowsingLoader) {
+    gPrivateBrowsingLoader = CreateImageLoader().take();
+    gPrivateBrowsingLoader->RespectPrivacyNotifications();
+  }
+  return gPrivateBrowsingLoader;
 }
 
 imgLoader::imgLoader()
 : mUncachedImagesMutex("imgLoader::UncachedImages"), mRespectPrivacy(false)
 {
   sMemReporter->AddRef();
   sMemReporter->RegisterLoader(this);
 }
 
-already_AddRefed<imgLoader>
-imgLoader::GetInstance()
-{
-  static RefPtr<imgLoader> singleton;
-  if (!singleton) {
-    singleton = imgLoader::Create();
-    if (!singleton) {
-        return nullptr;
-    }
-    ClearOnShutdown(&singleton);
-  }
-  RefPtr<imgLoader> loader = singleton.get();
-  return loader.forget();
-}
-
 imgLoader::~imgLoader()
 {
   ClearChromeImageCache();
   ClearImageCache();
   {
     // If there are any of our imgRequest's left they are in the uncached
     // images set, so clear their pointer to us.
     MutexAutoLock lock(mUncachedImagesMutex);
@@ -1395,18 +1395,20 @@ imgLoader::ClearCacheForControlledDocume
       NS_WARNING("Couldn't remove an entry from the cache in ClearCacheForControlledDocument()\n");
     }
   }
 }
 
 void
 imgLoader::Shutdown()
 {
-  NS_IF_RELEASE(gSingleton);
-  NS_IF_RELEASE(gPBSingleton);
+  NS_IF_RELEASE(gNormalLoader);
+  gNormalLoader = nullptr;
+  NS_IF_RELEASE(gPrivateBrowsingLoader);
+  gPrivateBrowsingLoader = nullptr;
 }
 
 nsresult
 imgLoader::ClearChromeImageCache()
 {
   return EvictEntries(mChromeCache);
 }
 
--- a/image/imgLoader.h
+++ b/image/imgLoader.h
@@ -241,39 +241,59 @@ public:
   typedef mozilla::Mutex Mutex;
 
   NS_DECL_ISUPPORTS
   NS_DECL_IMGILOADER
   NS_DECL_NSICONTENTSNIFFER
   NS_DECL_IMGICACHE
   NS_DECL_NSIOBSERVER
 
-  static imgLoader* Singleton();
-  static imgLoader* PBSingleton();
+  /**
+   * Get the normal image loader instance that is used by gecko code, creating
+   * it if necessary.
+   */
+  static imgLoader* NormalLoader();
 
-  imgLoader();
-
-  nsresult Init();
+  /**
+   * Get the Private Browsing image loader instance that is used by gecko code,
+   * creating it if necessary.
+   *
+   * The nsIChannel objects that this instance creates are created with the
+   * nsILoadInfo::SEC_FORCE_PRIVATE_BROWSING flag.
+   */
+  static imgLoader* PrivateBrowsingLoader();
 
-  static already_AddRefed<imgLoader>
-  Create()
-  {
-      // Unfortunately, we rely on XPCOM module init happening
-      // before imgLoader creation. For now, it's easier
-      // to just call CallCreateInstance() which will init
-      // the image module instead of calling new imgLoader
-      // directly.
-      nsCOMPtr<imgILoader> loader = do_CreateInstance("@mozilla.org/image/loader;1");
-      // There's only one imgLoader implementation so we
-      // can safely cast to it.
-      return loader.forget().downcast<imgLoader>();
-  }
-
-  static already_AddRefed<imgLoader>
-  GetInstance();
+  /**
+   * Gecko code should use NormalLoader() or PrivateBrowsingLoader() to get the
+   * appropriate image loader.
+   *
+   * This constructor is public because the XPCOM module code that creates
+   * instances of "@mozilla.org/image/loader;1" / "@mozilla.org/image/cache;1"
+   * for nsIComponentManager.createInstance()/nsIServiceManager.getService()
+   * calls (now only made by add-ons) needs access to it.
+   *
+   * XXX We would like to get rid of the nsIServiceManager.getService (and
+   * nsIComponentManager.createInstance) method of creating imgLoader objects,
+   * but there are add-ons that are still using it.  These add-ons don't
+   * actually do anything useful with the loaders that they create since nobody
+   * who creates an imgLoader using this method actually QIs to imgILoader and
+   * loads images.  They all just QI to imgICache and either call clearCache()
+   * or findEntryProperties().  Since they're doing this on an imgLoader that
+   * has never loaded images, these calls are useless.  It seems likely that
+   * the code that is doing this is just legacy code left over from a time when
+   * there was only one imgLoader instance for the entire process.  (Nowadays
+   * the correct method to get an imgILoader/imgICache is to call
+   * imgITools::getImgCacheForDocument/imgITools::getImgLoaderForDocument.)
+   * All the same, even though what these add-ons are doing is a no-op,
+   * removing the nsIServiceManager.getService method of creating/getting an
+   * imgLoader objects would cause an exception in these add-ons that could
+   * break things.
+   */
+  imgLoader();
+  nsresult Init();
 
   nsresult LoadImage(nsIURI* aURI,
                      nsIURI* aInitialDocumentURI,
                      nsIURI* aReferrerURI,
                      ReferrerPolicy aReferrerPolicy,
                      nsIPrincipal* aLoadingPrincipal,
                      nsILoadGroup* aLoadGroup,
                      imgINotificationObserver* aObserver,
@@ -368,16 +388,18 @@ public:
   // HasObservers(). The request's cache entry will be re-set before this
   // happens, by calling imgRequest::SetCacheEntry() when an entry with no
   // observers is re-requested.
   bool SetHasNoProxies(imgRequest* aRequest, imgCacheEntry* aEntry);
   bool SetHasProxies(imgRequest* aRequest);
 
 private: // methods
 
+  static already_AddRefed<imgLoader> CreateImageLoader();
+
   bool ValidateEntry(imgCacheEntry* aEntry, nsIURI* aKey,
                      nsIURI* aInitialDocumentURI, nsIURI* aReferrerURI,
                      ReferrerPolicy aReferrerPolicy,
                      nsILoadGroup* aLoadGroup,
                      imgINotificationObserver* aObserver, nsISupports* aCX,
                      nsLoadFlags aLoadFlags,
                      nsContentPolicyType aContentPolicyType,
                      bool aCanMakeNewChannel,
--- a/ipc/glue/MessageLink.cpp
+++ b/ipc/glue/MessageLink.cpp
@@ -21,16 +21,19 @@
 #include "jsprf.h"
 extern "C" char* PrintJSStack();
 #endif
 #endif
 
 #include "mozilla/Assertions.h"
 #include "mozilla/DebugOnly.h"
 #include "nsDebug.h"
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
 #include "nsISupportsImpl.h"
 #include "nsXULAppAPI.h"
 
 using namespace mozilla;
 using namespace std;
 
 // We rely on invariants about the lifetime of the transport:
 //
@@ -153,17 +156,24 @@ ProcessLink::EchoMessage(Message *msg)
 
     mIOLoop->PostTask(NewNonOwningRunnableMethod<Message*>(this, &ProcessLink::OnEchoMessage, msg));
     // OnEchoMessage takes ownership of |msg|
 }
 
 void
 ProcessLink::SendMessage(Message *msg)
 {
-    MOZ_RELEASE_ASSERT(msg->size() < IPC::Channel::kMaximumMessageSize);
+    if (msg->size() > IPC::Channel::kMaximumMessageSize) {
+#ifdef MOZ_CRASHREPORTER
+      CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCMessageName"), nsDependentCString(msg->name()));
+      CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCMessageSize"), nsPrintfCString("%d", msg->size()));
+#endif
+      MOZ_CRASH("IPC message size is too large");
+    }
+
     mChan->AssertWorkerThread();
     mChan->mMonitor->AssertCurrentThreadOwns();
 
 #ifdef MOZ_NUWA_PROCESS
     // Parent to child: check whether we are sending some unexpected message to
     // the Nuwa process.
     if (mIsToNuwaProcess && mozilla::dom::ContentParent::IsNuwaReady()) {
         switch (msg->type()) {
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -1846,20 +1846,21 @@ class ValueOperations
     int32_t toInt32() const { return value().toInt32(); }
     double toDouble() const { return value().toDouble(); }
     JSString* toString() const { return value().toString(); }
     JS::Symbol* toSymbol() const { return value().toSymbol(); }
     JSObject& toObject() const { return value().toObject(); }
     JSObject* toObjectOrNull() const { return value().toObjectOrNull(); }
     gc::Cell* toGCThing() const { return value().toGCThing(); }
     JS::TraceKind traceKind() const { return value().traceKind(); }
-    uint64_t asRawBits() const { return value().asRawBits(); }
+    void* toPrivate() const { return value().toPrivate(); }
+    uint32_t toPrivateUint32() const { return value().toPrivateUint32(); }
 
+    uint64_t asRawBits() const { return value().asRawBits(); }
     JSValueType extractNonDoubleType() const { return value().extractNonDoubleType(); }
-    uint32_t toPrivateUint32() const { return value().toPrivateUint32(); }
 
     JSWhyMagic whyMagic() const { return value().whyMagic(); }
     uint32_t magicUint32() const { return value().magicUint32(); }
 };
 
 /**
  * A class designed for CRTP use in implementing all the mutating parts of the
  * Value interface in Value-like classes.  Outer must be a class inheriting
@@ -1880,16 +1881,18 @@ class MutableValueOperations : public Va
     void setBoolean(bool b) { value().setBoolean(b); }
     void setMagic(JSWhyMagic why) { value().setMagic(why); }
     bool setNumber(uint32_t ui) { return value().setNumber(ui); }
     bool setNumber(double d) { return value().setNumber(d); }
     void setString(JSString* str) { this->value().setString(str); }
     void setSymbol(JS::Symbol* sym) { this->value().setSymbol(sym); }
     void setObject(JSObject& obj) { this->value().setObject(obj); }
     void setObjectOrNull(JSObject* arg) { this->value().setObjectOrNull(arg); }
+    void setPrivate(void* ptr) { this->value().setPrivate(ptr); }
+    void setPrivateUint32(uint32_t ui) { this->value().setPrivateUint32(ui); }
     void setPrivateGCThing(js::gc::Cell* cell) { this->value().setPrivateGCThing(cell); }
 };
 
 /*
  * Augment the generic Heap<T> interface when T = Value with
  * type-querying, value-extracting, and mutating operations.
  */
 template <>
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -243,17 +243,16 @@ endif
 ###############################################
 # Generating source package tarballs
 # (only possible when tar is found)
 ifneq (,$(TAR))
 
 source-package:
 	SRCDIR=$(srcdir) \
 	DIST=$(DIST) \
-	MAKE=$(MAKE) \
 	MKDIR=$(MKDIR) \
 	TAR=$(TAR) \
 	MOZJS_MAJOR_VERSION=$(MOZJS_MAJOR_VERSION) \
 	MOZJS_MINOR_VERSION=$(MOZJS_MINOR_VERSION) \
 	MOZJS_PATCH_VERSION=$(MOZJS_PATCH_VERSION) \
 	MOZJS_ALPHA=$(MOZJS_ALPHA) \
 	$(srcdir)/make-source-package.sh
 
--- a/js/src/asmjs/WasmBinaryToAST.cpp
+++ b/js/src/asmjs/WasmBinaryToAST.cpp
@@ -73,46 +73,56 @@ class AstDecodeContext
     Maybe<uint32_t> initialSizePages;
     Maybe<uint32_t> maxSizePages;
 
   private:
     AstModule& module_;
     AstIndexVector funcSigs_;
     AstDecodeExprIter *iter_;
     const ValTypeVector* locals_;
+    AstNameVector blockLabels_;
+    uint32_t currentLabelIndex_;
 
   public:
     AstDecodeContext(JSContext* cx, LifoAlloc& lifo, Decoder& d, AstModule& module, bool generateNames)
      : cx(cx),
        lifo(lifo),
        d(d),
        generateNames(generateNames),
        numTableElems(0),
        initialSizePages(),
        maxSizePages(),
        module_(module),
        funcSigs_(lifo),
        iter_(nullptr),
-       locals_(nullptr)
+       locals_(nullptr),
+       blockLabels_(lifo)
     {}
 
     AstModule& module() { return module_; }
     AstIndexVector& funcSigs() { return funcSigs_; }
     AstDecodeExprIter& iter() { return *iter_; }
     const ValTypeVector& locals() { return *locals_; }
+    AstNameVector& blockLabels() { return blockLabels_; }
 
     void startFunction(AstDecodeExprIter *iter, const ValTypeVector* locals)
     {
         iter_ = iter;
         locals_ = locals;
+        currentLabelIndex_ = 0;
     }
     void endFunction()
     {
         iter_ = nullptr;
         locals_ = nullptr;
+        MOZ_ASSERT(blockLabels_.length() == 0);
+    }
+    uint32_t nextLabelIndex()
+    {
+        return currentLabelIndex_++;
     }
 };
 
 static bool
 AstDecodeFail(AstDecodeContext& c, const char* str)
 {
     uint32_t offset = c.d.currentOffset();
     char offsetStr[sizeof "4294967295"];
@@ -227,21 +237,22 @@ AstDecodeCall(AstDecodeContext& c)
 
     AstExprVector args(c.lifo);
     if (!AstDecodeCallArgs(c, arity, *sig, &args))
         return false;
 
     if (!AstDecodeCallReturn(c, *sig))
         return false;
 
+    uint32_t argsLength = args.length();
     AstCall* call = new(c.lifo) AstCall(Expr::Call, funcRef, Move(args));
     if (!call)
         return false;
 
-    c.iter().setResult(AstDecodeStackItem(call, args.length()));
+    c.iter().setResult(AstDecodeStackItem(call, argsLength));
     return true;
 }
 
 static bool
 AstDecodeCallIndirect(AstDecodeContext& c)
 {
     uint32_t sigIndex;
     uint32_t arity;
@@ -263,21 +274,22 @@ AstDecodeCallIndirect(AstDecodeContext& 
     AstDecodeStackItem index;
     if (!c.iter().readCallIndirectCallee(&index))
         return false;
 
 
     if (!AstDecodeCallReturn(c, *sig))
         return false;
 
+    uint32_t argsLength = args.length();
     AstCallIndirect* call = new(c.lifo) AstCallIndirect(sigRef, index.expr, Move(args));
     if (!call)
         return false;
 
-    c.iter().setResult(AstDecodeStackItem(call, 1 + args.length()));
+    c.iter().setResult(AstDecodeStackItem(call, 1 + argsLength));
     return true;
 }
 
 static bool
 AstDecodeCallImport(AstDecodeContext& c)
 {
     uint32_t importIndex;
     uint32_t arity;
@@ -295,21 +307,41 @@ AstDecodeCallImport(AstDecodeContext& c)
 
     AstExprVector args(c.lifo);
     if (!AstDecodeCallArgs(c, arity, *sig, &args))
         return false;
 
     if (!AstDecodeCallReturn(c, *sig))
         return false;
 
+    uint32_t argsLength = args.length();
     AstCall* call = new(c.lifo) AstCall(Expr::CallImport, funcRef, Move(args));
     if (!call)
         return false;
 
-    c.iter().setResult(AstDecodeStackItem(call, args.length()));
+    c.iter().setResult(AstDecodeStackItem(call, argsLength));
+    return true;
+}
+
+static bool
+AstDecodeGetBlockRef(AstDecodeContext& c, uint32_t depth, AstRef* ref)
+{
+    if (!c.generateNames || depth >= c.blockLabels().length()) {
+        // Also ignoring if it's a function body label.
+        *ref = AstRef(AstName(), depth);
+        return true;
+    }
+
+    uint32_t index = c.blockLabels().length() - depth - 1;
+    if (c.blockLabels()[index].empty()) {
+        if (!AstDecodeGenerateName(c, AstName(MOZ_UTF16("$label$"), 7), c.nextLabelIndex(), &c.blockLabels()[index]))
+            return false;
+    }
+    *ref = AstRef(c.blockLabels()[index], AstNoIndex);
+    ref->setIndex(depth);
     return true;
 }
 
 static bool
 AstDecodeBrTable(AstDecodeContext& c)
 {
     uint32_t tableLength;
     ExprType type;
@@ -321,41 +353,49 @@ AstDecodeBrTable(AstDecodeContext& c)
     AstRefVector table(c.lifo);
     if (!table.resize(tableLength))
         return false;
 
     uint32_t depth;
     for (size_t i = 0, e = tableLength; i < e; ++i) {
         if (!c.iter().readBrTableEntry(type, &depth))
             return false;
-        table[i] = AstRef(AstName(), depth);
+        if (!AstDecodeGetBlockRef(c, depth, &table[i]))
+            return false;
     }
 
     // Read the default label.
     if (!c.iter().readBrTableEntry(type, &depth))
         return false;
 
-    AstRef def(AstName(), depth);
+    AstRef def;
+    if (!AstDecodeGetBlockRef(c, depth, &def))
+        return false;
+
     AstBranchTable* branchTable = new(c.lifo) AstBranchTable(*index.expr, def, Move(table), value.expr);
     if (!branchTable)
         return false;
 
     c.iter().setResult(AstDecodeStackItem(branchTable, value.expr ? 2 : 1));
     return true;
 }
 
 static bool
 AstDecodeBlock(AstDecodeContext& c, Expr expr)
 {
     MOZ_ASSERT(expr == Expr::Block || expr == Expr::Loop);
 
     if (expr == Expr::Loop) {
+      if (!c.blockLabels().append(AstName()) || !c.blockLabels().append(AstName()))
+          return false;
       if (!c.iter().readLoop())
           return false;
     } else {
+      if (!c.blockLabels().append(AstName()))
+          return false;
       if (!c.iter().readBlock())
           return false;
     }
 
     AstExprVector exprs(c.lifo);
     while (true) {
         if (!AstDecodeExpr(c))
             return false;
@@ -364,66 +404,77 @@ AstDecodeBlock(AstDecodeContext& c, Expr
         if (!item.expr) // Expr::End was found
             break;
 
         exprs.shrinkBy(item.popped);
         if (!exprs.append(item.expr))
             return false;
     }
 
-    AstBlock* block = new(c.lifo) AstBlock(expr, AstName(), AstName(), Move(exprs));
+    AstName continueName;
+    if (expr == Expr::Loop)
+        continueName = c.blockLabels().popCopy();
+    AstName breakName = c.blockLabels().popCopy();
+    AstBlock* block = new(c.lifo) AstBlock(expr, breakName, continueName, Move(exprs));
     if (!block)
         return false;
 
     c.iter().setResult(AstDecodeStackItem(block));
     return true;
 }
 
 static bool
 AstDecodeIf(AstDecodeContext& c)
 {
     AstDecodeStackItem cond;
     if (!c.iter().readIf(&cond))
         return false;
 
     bool hasElse = false;
 
+    if (!c.blockLabels().append(AstName()))
+        return false;
     AstExprVector thenExprs(c.lifo);
     while (true) {
         if (!AstDecodeExpr(c))
             return false;
 
         AstDecodeStackItem item = c.iter().getResult();
         if (!item.expr) {
             hasElse = item.terminationKind == AstDecodeTerminationKind::Else;
             break;
         }
 
         thenExprs.shrinkBy(item.popped);
         if (!thenExprs.append(item.expr))
             return false;
     }
+    AstName thenName = c.blockLabels().popCopy();
 
+    AstName elseName;
     AstExprVector elseExprs(c.lifo);
     if (hasElse) {
+        if (!c.blockLabels().append(AstName()))
+            return false;
         while (true) {
             if (!AstDecodeExpr(c))
                 return false;
 
             AstDecodeStackItem item = c.iter().getResult();
             if (!item.expr) // Expr::End was found
                 break;
 
             elseExprs.shrinkBy(item.popped);
             if (!elseExprs.append(item.expr))
                 return false;
         }
+        elseName = c.blockLabels().popCopy();
     }
 
-    AstIf* if_ = new(c.lifo) AstIf(cond.expr, AstName(), Move(thenExprs), AstName(), Move(elseExprs));
+    AstIf* if_ = new(c.lifo) AstIf(cond.expr, thenName, Move(thenExprs), elseName, Move(elseExprs));
     if (!if_)
         return false;
 
     c.iter().setResult(AstDecodeStackItem(if_, 1));
     return true;
 }
 
 static bool
@@ -585,17 +636,20 @@ AstDecodeBranch(AstDecodeContext& c, Exp
             return false;
         popped = value.expr ? 1 : 0;
     } else {
         if (!c.iter().readBrIf(&depth, &type, &value, &cond))
             return false;
         popped = value.expr ? 2 : 1;
     }
 
-    AstRef depthRef(AstName(), depth);
+    AstRef depthRef;
+    if (!AstDecodeGetBlockRef(c, depth, &depthRef))
+        return false;
+
     AstBranch* branch = new(c.lifo) AstBranch(expr, cond.expr, depthRef, value.expr);
     if (!branch)
         return false;
 
     c.iter().setResult(AstDecodeStackItem(branch, popped));
     return true;
 }
 
@@ -1182,29 +1236,29 @@ AstDecodeMemorySection(AstDecodeContext&
         return AstDecodeFail(c, "failed to start section");
     if (sectionStart == Decoder::NotStarted)
         return true;
 
     uint32_t initialSizePages;
     if (!c.d.readVarU32(&initialSizePages))
         return AstDecodeFail(c, "expected initial memory size");
 
-    CheckedInt<int32_t> initialSize = initialSizePages;
+    CheckedInt<uint32_t> initialSize = initialSizePages;
     initialSize *= PageSize;
     if (!initialSize.isValid())
         return AstDecodeFail(c, "initial memory size too big");
 
     uint32_t maxSizePages;
     if (!c.d.readVarU32(&maxSizePages))
         return AstDecodeFail(c, "expected initial memory size");
 
-    CheckedInt<int32_t> maxSize = maxSizePages;
+    CheckedInt<uint32_t> maxSize = maxSizePages;
     maxSize *= PageSize;
     if (!maxSize.isValid())
-        return AstDecodeFail(c, "initial memory size too big");
+        return AstDecodeFail(c, "maximum memory size too big");
 
     uint8_t exported;
     if (!c.d.readFixedU8(&exported))
         return AstDecodeFail(c, "expected exported byte");
 
     c.initialSizePages.emplace(initialSizePages);
     if (initialSizePages != maxSizePages) {
       c.maxSizePages.emplace(maxSizePages);
@@ -1299,16 +1353,20 @@ AstDecodeFunctionBody(AstDecodeContext &
     if (!locals.appendAll(sig->args()))
         return false;
 
     if (!DecodeLocalEntries(c.d, &locals))
         return AstDecodeFail(c, "failed decoding local entries");
 
     c.startFunction(&iter, &locals);
 
+    AstName funcName;
+    if (!AstDecodeGenerateName(c, AstName(MOZ_UTF16("$func$"), 6), funcIndex, &funcName))
+        return false;
+
     uint32_t numParams = sig->args().length();
     uint32_t numLocals = locals.length();
     for (uint32_t i = numParams; i < numLocals; i++) {
         if (!vars.append(locals[i]))
             return false;
     }
     for (uint32_t i = 0; i < numLocals; i++) {
         AstName varName;
@@ -1334,20 +1392,16 @@ AstDecodeFunctionBody(AstDecodeContext &
     if (!c.iter().readFunctionEnd(sig->ret(), &tmp))
         return false;
 
     c.endFunction();
 
     if (c.d.currentPosition() != bodyEnd)
         return AstDecodeFail(c, "function body length mismatch");
 
-    AstName funcName;
-    if (!AstDecodeGenerateName(c, AstName(MOZ_UTF16("$func$"), 6), funcIndex, &funcName))
-        return false;
-
     AstRef sigRef;
     if (!AstDecodeGenerateRef(c, AstName(MOZ_UTF16("$type$"), 6), sigIndex, &sigRef))
         return false;
 
     *func = new(c.lifo) AstFunc(funcName, sigRef, Move(vars), Move(localsNames), Move(body));
     if (!*func)
         return false;
 
new file mode 100644
--- /dev/null
+++ b/js/src/asmjs/WasmBinaryToExperimentalText.cpp
@@ -0,0 +1,1647 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2015 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "asmjs/WasmBinaryToExperimentalText.h"
+
+#include "mozilla/CheckedInt.h"
+
+#include "jsnum.h"
+#include "jsprf.h"
+
+#include "asmjs/Wasm.h"
+#include "asmjs/WasmAST.h"
+#include "asmjs/WasmBinaryToAST.h"
+#include "asmjs/WasmTypes.h"
+#include "vm/ArrayBufferObject.h"
+#include "vm/StringBuffer.h"
+
+using namespace js;
+using namespace js::wasm;
+
+using mozilla::CheckedInt;
+using mozilla::IsInfinite;
+using mozilla::IsNaN;
+using mozilla::IsNegativeZero;
+
+enum PrintOperatorPrecedence
+{
+    ExpressionPrecedence = 0,
+    AssignmentPrecedence = 1,
+    StoreOperatorPrecedence = 1,
+    SelectPrecedence = 2,
+    BitwiseOrPrecedence = 3,
+    BitwiseXorPrecedence = 4,
+    BitwiseAndPrecedence = 5,
+    EqualityPrecedence = 6,
+    ComparisonPrecedence = 7,
+    BitwiseShiftPrecedence = 8,
+    MinMaxPrecedence = 9,
+    AdditionPrecedence = 10,
+    MultiplicationPrecedence = 11,
+    CopySignPrecedence = 12,
+    ConversionPrecedence = 13,
+    UnaryOperatorPrecedence = 13,
+    LoadOperatorPrecedence = 14,
+    CallPrecedence = 15,
+    GroupPrecedence = 16,
+};
+
+struct WasmPrintContext
+{
+    JSContext* cx;
+    AstModule* module;
+    StringBuffer& buffer;
+    const ExperimentalTextFormatting& f;
+    uint32_t indent;
+
+    uint32_t currentFuncIndex;
+    PrintOperatorPrecedence currentPrecedence;
+
+    WasmPrintContext(JSContext* cx, AstModule* module, StringBuffer& buffer, const ExperimentalTextFormatting& f)
+      : cx(cx),
+        module(module),
+        buffer(buffer),
+        f(f),
+        indent(0),
+        currentFuncIndex(0),
+        currentPrecedence(PrintOperatorPrecedence::ExpressionPrecedence)
+    {}
+};
+
+/*****************************************************************************/
+// utilities
+
+static bool
+PrintIndent(WasmPrintContext& c)
+{
+    for (uint32_t i = 0; i < c.indent; i++) {
+        if (!c.buffer.append("  "))
+            return false;
+    }
+    return true;
+}
+
+static bool
+PrintInt32(WasmPrintContext& c, int32_t num, bool printSign = false)
+{
+    // Negative sign will be printed, printing '+' for non-negative values.
+    if (printSign && num >= 0) {
+        if (!c.buffer.append("+"))
+            return false;
+    }
+    return NumberValueToStringBuffer(c.cx, Int32Value(num), c.buffer);
+}
+
+static bool
+PrintInt64(WasmPrintContext& c, int64_t num)
+{
+    if (num < 0 && !c.buffer.append("-"))
+        return false;
+    if (!num)
+        return c.buffer.append("0");
+
+    uint64_t abs = mozilla::Abs(num);
+    uint64_t n = abs;
+    uint64_t pow = 1;
+    while (n) {
+        pow *= 10;
+        n /= 10;
+    }
+    pow /= 10;
+
+    n = abs;
+    while (pow) {
+        if (!c.buffer.append((char16_t)(MOZ_UTF16('0') + n / pow)))
+            return false;
+        n -= (n / pow) * pow;
+        pow /= 10;
+    }
+
+    return true;
+}
+
+static bool
+PrintDouble(WasmPrintContext& c, double num)
+{
+    if (IsNegativeZero(num))
+        return c.buffer.append("-0.0");
+    if (IsNaN(num))
+        return c.buffer.append("nan");
+    if (IsInfinite(num)) {
+        if (num > 0)
+            return c.buffer.append("infinity");
+        return c.buffer.append("-infinity");
+    }
+
+    uint32_t startLength = c.buffer.length();
+    if (!NumberValueToStringBuffer(c.cx, DoubleValue(num), c.buffer))
+        return false;
+    MOZ_ASSERT(startLength < c.buffer.length());
+
+    // Checking if we need to end number with '.0'.
+    for (uint32_t i = c.buffer.length() - 1; i >= startLength; i--) {
+        char16_t ch = c.buffer.getChar(i);
+        if (ch == '.' || ch == 'e')
+            return true;
+    }
+    return c.buffer.append(".0");
+}
+
+static bool
+PrintEscapedString(WasmPrintContext& c, const AstName& s)
+{
+    size_t length = s.length();
+    const char16_t* p = s.begin();
+    for (size_t i = 0; i < length; i++) {
+        char16_t byte = p[i];
+        switch (byte) {
+          case '\n':
+            if (!c.buffer.append("\\n"))
+                return false;
+            break;
+          case '\r':
+            if (!c.buffer.append("\\0d"))
+                return false;
+            break;
+          case '\t':
+            if (!c.buffer.append("\\t"))
+                return false;
+            break;
+          case '\f':
+            if (!c.buffer.append("\\0c"))
+                return false;
+            break;
+          case '\b':
+            if (!c.buffer.append("\\08"))
+                return false;
+            break;
+          case '\\':
+            if (!c.buffer.append("\\\\"))
+                return false;
+            break;
+          case '"' :
+            if (!c.buffer.append("\\\""))
+                return false;
+            break;
+          case '\'':
+            if (!c.buffer.append("\\'"))
+                return false;
+            break;
+          default:
+            if (byte >= 32 && byte < 127) {
+                if (!c.buffer.append((char)byte))
+                    return false;
+            } else {
+                char digit1 = byte / 16, digit2 = byte % 16;
+                if (!c.buffer.append("\\"))
+                    return false;
+                if (!c.buffer.append((char)(digit1 < 10 ? digit1 + '0' : digit1 - 10 + 'a')))
+                    return false;
+                if (!c.buffer.append((char)(digit2 < 10 ? digit2 + '0' : digit2 - 10 + 'a')))
+                    return false;
+            }
+            break;
+        }
+    }
+    return true;
+}
+
+static bool
+PrintExprType(WasmPrintContext& c, ExprType type)
+{
+    switch (type) {
+      case ExprType::Void: return true; // ignoring void
+      case ExprType::I32: return c.buffer.append("i32");
+      case ExprType::I64: return c.buffer.append("i64");
+      case ExprType::F32: return c.buffer.append("f32");
+      case ExprType::F64: return c.buffer.append("f64");
+      default:;
+    }
+
+    MOZ_CRASH("bad type");
+}
+
+static bool
+PrintValType(WasmPrintContext& c, ValType type)
+{
+    return PrintExprType(c, ToExprType(type));
+}
+
+static bool
+PrintName(WasmPrintContext& c, const AstName& name)
+{
+    return c.buffer.append(name.begin(), name.end());
+}
+
+static bool
+PrintRef(WasmPrintContext& c, const AstRef& ref)
+{
+    if (ref.name().empty())
+        return PrintInt32(c, ref.index());
+
+    return PrintName(c, ref.name());
+}
+
+static bool
+PrintExpr(WasmPrintContext& c, AstExpr& expr);
+
+static bool
+PrintFullLine(WasmPrintContext& c, AstExpr& expr)
+{
+    if (!PrintIndent(c))
+        return false;
+    if (!PrintExpr(c, expr))
+        return false;
+    return c.buffer.append('\n');
+}
+
+/*****************************************************************************/
+// binary format parsing and rendering
+
+static bool
+PrintNop(WasmPrintContext& c, AstNop& nop)
+{
+    return c.buffer.append("nop");
+}
+
+static bool
+PrintUnreachable(WasmPrintContext& c, AstUnreachable& unreachable)
+{
+    return c.buffer.append("unreachable");
+}
+
+static bool
+PrintCallArgs(WasmPrintContext& c, const AstExprVector& args)
+{
+    PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
+    c.currentPrecedence = ExpressionPrecedence;
+
+    if (!c.buffer.append("("))
+        return false;
+    for (uint32_t i = 0; i < args.length(); i++) {
+        if (!PrintExpr(c, *args[i]))
+            return false;
+        if (i + 1 == args.length())
+            break;
+        if (!c.buffer.append(","))
+            return false;
+    }
+    if (!c.buffer.append(")"))
+        return false;
+
+    c.currentPrecedence = lastPrecedence;
+    return true;
+}
+
+static bool
+PrintCall(WasmPrintContext& c, AstCall& call)
+{
+    if (call.expr() == Expr::Call) {
+        if (!c.buffer.append("call "))
+            return false;
+    } else if (call.expr() == Expr::CallImport) {
+        if (!c.buffer.append("call_import "))
+            return false;
+    } else {
+        return false;
+    }
+
+    if (!PrintRef(c, call.func()))
+        return false;
+
+    if (!c.buffer.append(" "))
+        return false;
+
+    if (!PrintCallArgs(c, call.args()))
+        return false;
+
+    return true;
+}
+
+static bool
+PrintCallIndirect(WasmPrintContext& c, AstCallIndirect& call)
+{
+    if (!c.buffer.append("call_indirect "))
+        return false;
+    if (!PrintRef(c, call.sig()))
+        return false;
+
+    if (!c.buffer.append(" ["))
+        return false;
+
+    if (!PrintExpr(c, *call.index()))
+        return false;
+
+    if (!c.buffer.append("] "))
+        return false;
+    if (!PrintCallArgs(c, call.args()))
+        return false;
+    return true;
+}
+
+static bool
+PrintConst(WasmPrintContext& c, AstConst& cst)
+{
+    switch (ToExprType(cst.val().type())) {
+      case ExprType::I32:
+        if (!PrintInt32(c, (uint32_t)cst.val().i32()))
+            return false;
+        break;
+      case ExprType::I64:
+        if (!PrintInt64(c, (uint32_t)cst.val().i64()))
+            return false;
+        if (!c.buffer.append("i64"))
+            return false;
+        break;
+      case ExprType::F32:
+        if (!PrintDouble(c, (double)cst.val().f32()))
+            return false;
+        if (!c.buffer.append("f"))
+            return false;
+        break;
+      case ExprType::F64:
+        if (!PrintDouble(c, cst.val().f64()))
+            return false;
+        break;
+      default:
+        MOZ_CRASH("bad const type");
+        break;
+    }
+
+    return true;
+}
+
+static bool
+PrintGetLocal(WasmPrintContext& c, AstGetLocal& gl)
+{
+    if (!PrintRef(c, gl.local()))
+        return false;
+    return true;
+}
+
+static bool
+PrintSetLocal(WasmPrintContext& c, AstSetLocal& sl)
+{
+    PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
+
+    if (c.f.reduceParens ? lastPrecedence > AssignmentPrecedence : lastPrecedence != ExpressionPrecedence) {
+      if (!c.buffer.append("("))
+          return false;
+    }
+
+    if (!PrintRef(c, sl.local()))
+        return false;
+    if (!c.buffer.append(" = "))
+        return false;
+
+    c.currentPrecedence = AssignmentPrecedence;
+
+    if (!PrintExpr(c, sl.value()))
+        return false;
+
+    if (c.f.reduceParens ? lastPrecedence > AssignmentPrecedence : lastPrecedence != ExpressionPrecedence) {
+      if (!c.buffer.append(")"))
+          return false;
+    }
+
+    c.currentPrecedence = lastPrecedence;
+    return true;
+}
+
+static bool
+PrintExprList(WasmPrintContext& c, const AstExprVector& exprs, uint32_t startFrom = 0)
+{
+    for (uint32_t i = startFrom; i < exprs.length(); i++) {
+        if (!PrintFullLine(c, *exprs[i]))
+            return false;
+    }
+    return true;
+}
+
+static bool
+PrintGroupedBlock(WasmPrintContext& c, AstBlock& block)
+{
+    uint32_t skip = 0;
+    if (block.exprs().length() > 0 &&
+        block.exprs()[0]->kind() == AstExprKind::Block) {
+        if (!PrintGroupedBlock(c, *static_cast<AstBlock*>(block.exprs()[0])))
+            return false;
+        skip = 1;
+    }
+    c.indent++;
+    if (!PrintExprList(c, block.exprs(), skip))
+        return false;
+    c.indent--;
+    if (!PrintIndent(c))
+        return false;
+    if (!PrintName(c, block.breakName()))
+        return false;
+    if (!c.buffer.append(":\n"))
+        return false;
+    return true;
+}
+
+static bool
+PrintBlockName(WasmPrintContext& c, const AstName& name) {
+    if (name.empty())
+        return true;
+
+    if (!PrintIndent(c))
+        return false;
+    if (!PrintName(c, name))
+        return false;
+    return c.buffer.append(":\n");
+}
+
+static bool
+PrintBlock(WasmPrintContext& c, AstBlock& block)
+{
+    PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
+    if (block.expr() == Expr::Block) {
+        if (!c.buffer.append("{\n"))
+            return false;
+    } else if (block.expr() == Expr::Loop) {
+        if (!c.buffer.append("loop"))
+            return false;
+        if (!block.continueName().empty()) {
+            if (!c.buffer.append(" "))
+                return false;
+            if (!PrintName(c, block.continueName()))
+                return false;
+        }
+        if (!c.buffer.append(" {\n"))
+            return false;
+    } else
+        return false;
+
+    c.currentPrecedence = ExpressionPrecedence;
+
+    bool skip = 0;
+    if (c.f.groupBlocks && block.expr() == Expr::Block &&
+        block.exprs().length() > 0 && block.exprs()[0]->kind() == AstExprKind::Block) {
+        if (!PrintGroupedBlock(c, *static_cast<AstBlock*>(block.exprs()[0])))
+            return false;
+        skip = 1;
+        if (block.exprs().length() == 1 && block.breakName().empty()) {
+          // Special case to resolve ambiguity in parsing of optional end block label.
+          if (!PrintIndent(c))
+              return false;
+          if (!c.buffer.append("$exit$:\n"))
+              return false;
+        }
+    }
+
+    c.indent++;
+    if (!PrintExprList(c, block.exprs(), skip))
+        return false;
+    c.indent--;
+    c.currentPrecedence = lastPrecedence;
+
+    if (!PrintBlockName(c, block.breakName()))
+      return false;
+
+    if (!PrintIndent(c))
+        return false;
+
+    return c.buffer.append("}");
+}
+
+static bool
+PrintUnaryOperator(WasmPrintContext& c, AstUnaryOperator& op)
+{
+    PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
+
+    const char* opStr;
+    const char* prefixStr = nullptr;
+    switch (op.expr()) {
+      case Expr::I32Eqz:     opStr = "i32.eqz"; prefixStr = "!"; break;
+      case Expr::I32Clz:     opStr = "i32.clz"; break;
+      case Expr::I32Ctz:     opStr = "i32.ctz"; break;
+      case Expr::I32Popcnt:  opStr = "i32.popcnt"; break;
+      case Expr::I64Eqz:     opStr = "i64.eqz"; prefixStr = "!"; break;
+      case Expr::I64Clz:     opStr = "i64.clz"; break;
+      case Expr::I64Ctz:     opStr = "i64.ctz"; break;
+      case Expr::I64Popcnt:  opStr = "i64.popcnt"; break;
+      case Expr::F32Abs:     opStr = "f32.abs"; break;
+      case Expr::F32Neg:     opStr = "f32.neg"; prefixStr = "-"; break;
+      case Expr::F32Ceil:    opStr = "f32.ceil"; break;
+      case Expr::F32Floor:   opStr = "f32.floor"; break;
+      case Expr::F32Sqrt:    opStr = "f32.sqrt"; break;
+      case Expr::F32Trunc:   opStr = "f32.trunc"; break;
+      case Expr::F32Nearest: opStr = "f32.nearest"; break;
+      case Expr::F64Abs:     opStr = "f64.abs"; break;
+      case Expr::F64Neg:     opStr = "f64.neg"; prefixStr = "-"; break;
+      case Expr::F64Ceil:    opStr = "f64.ceil"; break;
+      case Expr::F64Floor:   opStr = "f64.floor"; break;
+      case Expr::F64Sqrt:    opStr = "f64.sqrt"; break;
+      default: return false;
+    }
+
+    if (c.f.reduceParens && lastPrecedence > UnaryOperatorPrecedence) {
+      if (!c.buffer.append("("))
+          return false;
+    }
+
+    c.currentPrecedence = UnaryOperatorPrecedence;
+    if (c.f.allowAsciiOperators && prefixStr) {
+      if (!c.buffer.append(prefixStr, strlen(prefixStr)))
+          return false;
+    } else {
+        if (!c.buffer.append(opStr, strlen(opStr)))
+            return false;
+
+        if (!c.buffer.append(" "))
+            return false;
+    }
+
+    if (!PrintExpr(c, *op.op()))
+        return false;
+
+    if (c.f.reduceParens && lastPrecedence > UnaryOperatorPrecedence) {
+      if (!c.buffer.append(")"))
+          return false;
+    }
+    c.currentPrecedence = lastPrecedence;
+
+    return true;
+}
+
+static bool
+PrintBinaryOperator(WasmPrintContext& c, AstBinaryOperator& op)
+{
+    PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
+
+    const char* opStr;
+    const char* infixStr = nullptr;
+    PrintOperatorPrecedence precedence;
+    switch (op.expr()) {
+      case Expr::I32Add:      opStr = "i32.add"; infixStr = "+"; precedence = AdditionPrecedence; break;
+      case Expr::I32Sub:      opStr = "i32.sub"; infixStr = "-"; precedence = AdditionPrecedence; break;
+      case Expr::I32Mul:      opStr = "i32.mul"; infixStr = "*"; precedence = MultiplicationPrecedence; break;
+      case Expr::I32DivS:     opStr = "i32.div_s"; infixStr = "/s"; precedence = MultiplicationPrecedence; break;
+      case Expr::I32DivU:     opStr = "i32.div_u"; infixStr = "/u"; precedence = MultiplicationPrecedence; break;
+      case Expr::I32RemS:     opStr = "i32.rem_s"; infixStr = "%s"; precedence = MultiplicationPrecedence; break;
+      case Expr::I32RemU:     opStr = "i32.rem_u"; infixStr = "%u"; precedence = MultiplicationPrecedence; break;
+      case Expr::I32And:      opStr = "i32.and"; infixStr = "&"; precedence = BitwiseAndPrecedence; break;
+      case Expr::I32Or:       opStr = "i32.or"; infixStr = "|"; precedence = BitwiseOrPrecedence; break;
+      case Expr::I32Xor:      opStr = "i32.xor"; infixStr = "^"; precedence = BitwiseXorPrecedence; break;
+      case Expr::I32Shl:      opStr = "i32.shl"; infixStr = "<<"; precedence = BitwiseShiftPrecedence; break;
+      case Expr::I32ShrS:     opStr = "i32.shr_s"; infixStr = ">>s"; precedence = BitwiseShiftPrecedence; break;
+      case Expr::I32ShrU:     opStr = "i32.shr_u"; infixStr = ">>u"; precedence = BitwiseShiftPrecedence; break;
+      case Expr::I64Add:      opStr = "i64.add"; infixStr = "+"; precedence = AdditionPrecedence; break;
+      case Expr::I64Sub:      opStr = "i64.sub"; infixStr = "-"; precedence = AdditionPrecedence; break;
+      case Expr::I64Mul:      opStr = "i64.mul"; infixStr = "*"; precedence = MultiplicationPrecedence; break;
+      case Expr::I64DivS:     opStr = "i64.div_s"; infixStr = "/s"; precedence = MultiplicationPrecedence; break;
+      case Expr::I64DivU:     opStr = "i64.div_u"; infixStr = "/u"; precedence = MultiplicationPrecedence; break;
+      case Expr::I64RemS:     opStr = "i64.rem_s"; infixStr = "%s"; precedence = MultiplicationPrecedence; break;
+      case Expr::I64RemU:     opStr = "i64.rem_u"; infixStr = "%u"; precedence = MultiplicationPrecedence; break;
+      case Expr::I64And:      opStr = "i64.and"; infixStr = "&"; precedence = BitwiseAndPrecedence; break;
+      case Expr::I64Or:       opStr = "i64.or"; infixStr = "|"; precedence = BitwiseOrPrecedence; break;
+      case Expr::I64Xor:      opStr = "i64.xor"; infixStr = "^"; precedence = BitwiseXorPrecedence; break;
+      case Expr::I64Shl:      opStr = "i64.shl"; infixStr = "<<"; precedence = BitwiseShiftPrecedence; break;
+      case Expr::I64ShrS:     opStr = "i64.shr_s"; infixStr = ">>s"; precedence = BitwiseShiftPrecedence; break;
+      case Expr::I64ShrU:     opStr = "i64.shr_u"; infixStr = ">>u"; precedence = BitwiseShiftPrecedence; break;
+      case Expr::F32Add:      opStr = "f32.add"; infixStr = "+"; precedence = AdditionPrecedence; break;
+      case Expr::F32Sub:      opStr = "f32.sub"; infixStr = "-"; precedence = AdditionPrecedence; break;
+      case Expr::F32Mul:      opStr = "f32.mul"; infixStr = "*"; precedence = MultiplicationPrecedence; break;
+      case Expr::F32Div:      opStr = "f32.div"; infixStr = "/"; precedence = MultiplicationPrecedence; break;
+      case Expr::F32Min:      opStr = "f32.min"; precedence = MinMaxPrecedence; break;
+      case Expr::F32Max:      opStr = "f32.max"; precedence = MinMaxPrecedence; break;
+      case Expr::F32CopySign: opStr = "f32.copysign"; precedence = CopySignPrecedence; break;
+      case Expr::F64Add:      opStr = "f64.add"; infixStr = "+"; precedence = AdditionPrecedence; break;
+      case Expr::F64Sub:      opStr = "f64.sub"; infixStr = "-"; precedence = AdditionPrecedence; break;
+      case Expr::F64Mul:      opStr = "f64.mul"; infixStr = "*"; precedence = MultiplicationPrecedence; break;
+      case Expr::F64Div:      opStr = "f64.div"; infixStr = "/"; precedence = MultiplicationPrecedence; break;
+      case Expr::F64Min:      opStr = "f64.min"; precedence = MinMaxPrecedence; break;
+      case Expr::F64Max:      opStr = "f64.max"; precedence = MinMaxPrecedence; break;
+      case Expr::F64CopySign: opStr = "f64.copysign"; precedence = CopySignPrecedence; break;
+      default: return false;
+    }
+
+    c.currentPrecedence = precedence;
+    if (c.f.reduceParens && lastPrecedence > AdditionPrecedence) {
+      if (!c.buffer.append("("))
+          return false;
+    }
+
+    if (!c.f.allowAsciiOperators || !infixStr) {
+      if (!c.buffer.append(opStr, strlen(opStr)))
+          return false;
+      if (!c.buffer.append(" "))
+          return false;
+    }
+    if (!PrintExpr(c, *op.lhs()))
+        return false;
+    if (!c.buffer.append(" "))
+        return false;
+    if (c.f.allowAsciiOperators && infixStr) {
+      // case of  A / (B / C)
+      c.currentPrecedence = (PrintOperatorPrecedence)(precedence + 1);
+
+      if (!c.buffer.append(infixStr, strlen(infixStr)))
+          return false;
+      if (!c.buffer.append(" "))
+          return false;
+    }
+    if (!PrintExpr(c, *op.rhs()))
+        return false;
+
+    if (c.f.reduceParens && lastPrecedence > AdditionPrecedence) {
+      if (!c.buffer.append(")"))
+          return false;
+    }
+    c.currentPrecedence = lastPrecedence;
+
+    return true;
+}
+
+static bool
+PrintTernaryOperator(WasmPrintContext& c, AstTernaryOperator& op)
+{
+    PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
+
+    const char* opStr;
+    switch (op.expr()) {
+      case Expr::Select: opStr = "select"; break;
+      default: return false;
+    }
+
+    c.currentPrecedence = SelectPrecedence;
+    if (c.f.reduceParens && lastPrecedence > SelectPrecedence) {
+      if (!c.buffer.append("("))
+          return false;
+    }
+
+    if (!c.buffer.append(opStr, strlen(opStr)))
+        return false;
+
+    if (!c.buffer.append(" "))
+        return false;
+    if (!PrintExpr(c, *op.op0()))
+        return false;
+    if (!c.buffer.append(","))
+        return false;
+    if (!PrintExpr(c, *op.op1()))
+        return false;
+    if (!c.buffer.append(" ? "))
+        return false;
+    if (!PrintExpr(c, *op.op2()))
+        return false;
+
+    if (c.f.reduceParens && lastPrecedence > SelectPrecedence) {
+      if (!c.buffer.append(")"))
+          return false;
+    }
+    c.currentPrecedence = lastPrecedence;
+
+    return true;
+}
+
+static bool
+PrintComparisonOperator(WasmPrintContext& c, AstComparisonOperator& op)
+{
+    PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
+
+    const char* opStr;
+    const char* infixStr = nullptr;
+    switch (op.expr()) {
+      case Expr::I32Eq:  opStr = "i32.eq"; infixStr = "=="; break;
+      case Expr::I32Ne:  opStr = "i32.ne"; infixStr = "!="; break;
+      case Expr::I32LtS: opStr = "i32.lt_s"; infixStr = "<s"; break;
+      case Expr::I32LtU: opStr = "i32.lt_u"; infixStr = "<u"; break;
+      case Expr::I32LeS: opStr = "i32.le_s"; infixStr = "<=s"; break;
+      case Expr::I32LeU: opStr = "i32.le_u"; infixStr = "<=u"; break;
+      case Expr::I32GtS: opStr = "i32.gt_s"; infixStr = ">s"; break;
+      case Expr::I32GtU: opStr = "i32.gt_u"; infixStr = ">u"; break;
+      case Expr::I32GeS: opStr = "i32.ge_s"; infixStr = ">=s"; break;
+      case Expr::I32GeU: opStr = "i32.ge_u"; infixStr = ">=u"; break;
+      case Expr::I64Eq:  opStr = "i64.eq"; infixStr = "=="; break;
+      case Expr::I64Ne:  opStr = "i64.ne"; infixStr = "!="; break;
+      case Expr::I64LtS: opStr = "i64.lt_s"; infixStr = "<s"; break;
+      case Expr::I64LtU: opStr = "i64.lt_u"; infixStr = "<u"; break;
+      case Expr::I64LeS: opStr = "i64.le_s"; infixStr = "<=s"; break;
+      case Expr::I64LeU: opStr = "i64.le_u"; infixStr = "<=u"; break;
+      case Expr::I64GtS: opStr = "i64.gt_s"; infixStr = ">s"; break;
+      case Expr::I64GtU: opStr = "i64.gt_u"; infixStr = ">u"; break;
+      case Expr::I64GeS: opStr = "i64.ge_s"; infixStr = ">=s"; break;
+      case Expr::I64GeU: opStr = "i64.ge_u"; infixStr = ">=u"; break;
+      case Expr::F32Eq:  opStr = "f32.eq"; infixStr = "=="; break;
+      case Expr::F32Ne:  opStr = "f32.ne"; infixStr = "!="; break;
+      case Expr::F32Lt:  opStr = "f32.lt"; infixStr = "<"; break;
+      case Expr::F32Le:  opStr = "f32.le"; infixStr = "<="; break;
+      case Expr::F32Gt:  opStr = "f32.gt"; infixStr = ">"; break;
+      case Expr::F32Ge:  opStr = "f32.ge"; infixStr = ">="; break;
+      case Expr::F64Eq:  opStr = "f64.eq"; infixStr = "=="; break;
+      case Expr::F64Ne:  opStr = "f64.ne"; infixStr = "!="; break;
+      case Expr::F64Lt:  opStr = "f64.lt"; infixStr = "<"; break;
+      case Expr::F64Le:  opStr = "f64.le"; infixStr = "<="; break;
+      case Expr::F64Gt:  opStr = "f64.gt"; infixStr = ">"; break;
+      case Expr::F64Ge:  opStr = "f64.ge"; infixStr = ">="; break;
+      default: return false;
+    }
+
+    c.currentPrecedence = ComparisonPrecedence;
+    if (c.f.reduceParens && lastPrecedence > ComparisonPrecedence) {
+      if (!c.buffer.append("("))
+          return false;
+    }
+
+    if (!c.f.allowAsciiOperators || !infixStr) {
+        if (!c.buffer.append(opStr, strlen(opStr)))
+            return false;
+        if (!c.buffer.append(" "))
+            return false;
+    }
+    if (!PrintExpr(c, *op.lhs()))
+        return false;
+    if (!c.buffer.append(" "))
+        return false;
+    if (c.f.allowAsciiOperators && infixStr) {
+        if (!c.buffer.append(infixStr, strlen(infixStr)))
+            return false;
+        if (!c.buffer.append(" "))
+            return false;
+    }
+    if (!PrintExpr(c, *op.rhs()))
+        return false;
+
+    if (c.f.reduceParens && lastPrecedence > ComparisonPrecedence) {
+      if (!c.buffer.append(")"))
+          return false;
+    }
+    c.currentPrecedence = lastPrecedence;
+
+    return true;
+}
+
+static bool
+PrintConversionOperator(WasmPrintContext& c, AstConversionOperator& op)
+{
+    PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
+
+    const char* opStr;
+    switch (op.expr()) {
+      case Expr::I32WrapI64:        opStr = "i32.wrap/i64"; break;
+      case Expr::I32TruncSF32:      opStr = "i32.trunc_s/f32"; break;
+      case Expr::I32TruncUF32:      opStr = "i32.trunc_u/f32"; break;
+      case Expr::I32ReinterpretF32: opStr = "i32.reinterpret/f32"; break;
+      case Expr::I32TruncSF64:      opStr = "i32.trunc_s/f64"; break;
+      case Expr::I32TruncUF64:      opStr = "i32.trunc_u/f64"; break;
+      case Expr::I64ExtendSI32:     opStr = "i64.extend_s/i32"; break;
+      case Expr::I64ExtendUI32:     opStr = "i64.extend_u/i32"; break;
+      case Expr::I64TruncSF32:      opStr = "i64.trunc_s/f32"; break;
+      case Expr::I64TruncUF32:      opStr = "i64.trunc_u/f32"; break;
+      case Expr::I64TruncSF64:      opStr = "i64.trunc_s/f64"; break;
+      case Expr::I64TruncUF64:      opStr = "i64.trunc_u/f64"; break;
+      case Expr::I64ReinterpretF64: opStr = "i64.reinterpret/f64"; break;
+      case Expr::F32ConvertSI32:    opStr = "f32.convert_s/i32"; break;
+      case Expr::F32ConvertUI32:    opStr = "f32.convert_u/i32"; break;
+      case Expr::F32ReinterpretI32: opStr = "f32.reinterpret/i32"; break;
+      case Expr::F32ConvertSI64:    opStr = "f32.convert_s/i64"; break;
+      case Expr::F32ConvertUI64:    opStr = "f32.convert_u/i64"; break;
+      case Expr::F32DemoteF64:      opStr = "f32.demote/f64"; break;
+      case Expr::F64ConvertSI32:    opStr = "f64.convert_s/i32"; break;
+      case Expr::F64ConvertUI32:    opStr = "f64.convert_u/i32"; break;
+      case Expr::F64ConvertSI64:    opStr = "f64.convert_s/i64"; break;
+      case Expr::F64ConvertUI64:    opStr = "f64.convert_u/i64"; break;
+      case Expr::F64ReinterpretI64: opStr = "f64.reinterpret/i64"; break;
+      case Expr::F64PromoteF32:     opStr = "f64.promote/f32"; break;
+      default: return false;
+    }
+
+    c.currentPrecedence = ConversionPrecedence;
+    if (c.f.reduceParens && lastPrecedence > ConversionPrecedence) {
+      if (!c.buffer.append("("))
+          return false;
+    }
+
+    if (!c.buffer.append(opStr, strlen(opStr)))
+        return false;
+
+    if (!c.buffer.append(" "))
+        return false;
+
+    if (!PrintExpr(c, *op.op()))
+        return false;
+
+    if (c.f.reduceParens && lastPrecedence > ConversionPrecedence) {
+      if (!c.buffer.append(")"))
+          return false;
+    }
+    c.currentPrecedence = lastPrecedence;
+
+    return true;
+}
+
+static bool
+PrintIf(WasmPrintContext& c, AstIf& if_)
+{
+    PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
+
+    c.currentPrecedence = ExpressionPrecedence;
+    if (!c.buffer.append("if ("))
+        return false;
+    if (!PrintExpr(c, if_.cond()))
+        return false;
+
+    if (!c.buffer.append(") {\n"))
+        return false;
+
+    c.indent++;
+    if (!PrintExprList(c, if_.thenExprs()))
+        return false;
+    c.indent--;
+
+    if (!PrintBlockName(c, if_.thenName()))
+        return false;
+
+    if (if_.hasElse()) {
+        if (!PrintIndent(c))
+            return false;
+        if (!c.buffer.append("} else {\n"))
+            return false;
+
+        c.indent++;
+        if (!PrintExprList(c, if_.elseExprs()))
+            return false;
+        c.indent--;
+        if (!PrintBlockName(c, if_.elseName()))
+            return false;
+    }
+
+    if (!PrintIndent(c))
+        return false;
+
+    c.currentPrecedence = lastPrecedence;
+
+    return c.buffer.append("}");
+}
+
+static bool
+PrintLoadStoreAddress(WasmPrintContext& c, const AstLoadStoreAddress& lsa, uint32_t defaultAlignLog2)
+{
+    PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
+
+    c.currentPrecedence = ExpressionPrecedence;
+
+    if (!c.buffer.append(" ["))
+        return false;
+    if (!PrintExpr(c, lsa.base()))
+        return false;
+
+    if (lsa.offset() != 0) {
+      if (!c.buffer.append(","))
+          return false;
+      if (!PrintInt32(c, lsa.offset(), true))
+          return false;
+    }
+    if (!c.buffer.append("]"))
+        return false;
+
+    uint32_t alignLog2 = lsa.flags();
+    if (defaultAlignLog2 != alignLog2) {
+      if (!c.buffer.append(":align="))
+          return false;
+      if (!PrintInt32(c, 1 << alignLog2))
+          return false;
+    }
+
+    c.currentPrecedence = lastPrecedence;
+    return true;
+}
+
+static bool
+PrintLoad(WasmPrintContext& c, AstLoad& load)
+{
+    PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
+
+    c.currentPrecedence = LoadOperatorPrecedence;
+    if (c.f.reduceParens && lastPrecedence > LoadOperatorPrecedence) {
+      if (!c.buffer.append("("))
+          return false;
+    }
+
+    uint32_t defaultAlignLog2;
+    switch (load.expr()) {
+      case Expr::I32Load8S:
+        if (!c.buffer.append("i32.load8_s"))
+            return false;
+        defaultAlignLog2 = 0;
+        break;
+      case Expr::I64Load8S:
+        if (!c.buffer.append("i64.load8_s"))
+            return false;
+        defaultAlignLog2 = 0;
+        break;
+      case Expr::I32Load8U:
+        if (!c.buffer.append("i32.load8_u"))
+            return false;
+        defaultAlignLog2 = 0;
+        break;
+      case Expr::I64Load8U:
+        if (!c.buffer.append("i64.load8_u"))
+            return false;
+        defaultAlignLog2 = 0;
+        break;
+      case Expr::I32Load16S:
+        if (!c.buffer.append("i32.load16_s"))
+            return false;
+        defaultAlignLog2 = 1;
+        break;
+      case Expr::I64Load16S:
+        if (!c.buffer.append("i64.load16_s"))
+            return false;
+        defaultAlignLog2 = 1;
+        break;
+      case Expr::I32Load16U:
+        if (!c.buffer.append("i32.load16_u"))
+            return false;
+        defaultAlignLog2 = 1;
+        break;
+      case Expr::I64Load16U:
+        if (!c.buffer.append("i64.load16_u"))
+            return false;
+        defaultAlignLog2 = 1;
+        break;
+      case Expr::I64Load32S:
+        if (!c.buffer.append("i64.load32_s"))
+            return false;
+        defaultAlignLog2 = 2;
+        break;
+      case Expr::I64Load32U:
+        if (!c.buffer.append("i64.load32_u"))
+            return false;
+        defaultAlignLog2 = 2;
+        break;
+      case Expr::I32Load:
+        if (!c.buffer.append("i32.load"))
+            return false;
+        defaultAlignLog2 = 2;
+        break;
+      case Expr::I64Load:
+        if (!c.buffer.append("i64.load"))
+            return false;
+        defaultAlignLog2 = 3;
+        break;
+      case Expr::F32Load:
+        if (!c.buffer.append("f32.load"))
+            return false;
+        defaultAlignLog2 = 2;
+        break;
+      case Expr::F64Load:
+        if (!c.buffer.append("f64.load"))
+            return false;
+        defaultAlignLog2 = 3;
+        break;
+      default:
+        return false;
+    }
+
+    if (!PrintLoadStoreAddress(c, load.address(), defaultAlignLog2))
+        return false;
+
+    if (c.f.reduceParens && lastPrecedence > LoadOperatorPrecedence) {
+      if (!c.buffer.append(")"))
+          return false;
+    }
+    c.currentPrecedence = lastPrecedence;
+
+    return true;
+}
+
+static bool
+PrintStore(WasmPrintContext& c, AstStore& store)
+{
+    PrintOperatorPrecedence lastPrecedence = c.currentPrecedence;
+
+    c.currentPrecedence = StoreOperatorPrecedence;
+    if (c.f.reduceParens ? lastPrecedence > StoreOperatorPrecedence : lastPrecedence != ExpressionPrecedence) {
+      if (!c.buffer.append("("))
+          return false;
+    }
+
+    uint32_t defaultAlignLog2;
+    switch (store.expr()) {
+      case Expr::I32Store8:
+        if (!c.buffer.append("i32.store8"))
+            return false;
+        defaultAlignLog2 = 0;
+        break;
+      case Expr::I64Store8:
+        if (!c.buffer.append("i64.store8"))
+            return false;
+        defaultAlignLog2 = 0;
+        break;
+      case Expr::I32Store16:
+        if (!c.buffer.append("i32.store16"))
+            return false;
+        defaultAlignLog2 = 1;
+        break;
+      case Expr::I64Store16:
+        if (!c.buffer.append("i64.store16"))
+            return false;
+        defaultAlignLog2 = 1;
+        break;
+      case Expr::I64Store32:
+        if (!c.buffer.append("i64.store32"))
+            return false;
+        defaultAlignLog2 = 2;
+        break;
+      case Expr::I32Store:
+        if (!c.buffer.append("i32.store"))
+            return false;
+        defaultAlignLog2 = 2;
+        break;
+      case Expr::I64Store:
+        if (!c.buffer.append("i64.store"))
+            return false;
+        defaultAlignLog2 = 3;
+        break;
+      case Expr::F32Store:
+        if (!c.buffer.append("f32.store"))
+            return false;
+        defaultAlignLog2 = 2;
+        break;
+      case Expr::F64Store:
+        if (!c.buffer.append("f64.store"))
+            return false;
+        defaultAlignLog2 = 3;
+        break;
+      default:
+        return false;
+    }
+
+    if (!PrintLoadStoreAddress(c, store.address(), defaultAlignLog2))
+        return false;
+
+    if (!c.buffer.append(","))
+        return false;
+
+    if (!PrintExpr(c, store.value()))
+        return false;
+
+    if (c.f.reduceParens ? lastPrecedence > StoreOperatorPrecedence : lastPrecedence != ExpressionPrecedence) {
+      if (!c.buffer.append(")"))
+          return false;
+    }
+
+    c.currentPrecedence = lastPrecedence;
+    return true;
+}
+
+static bool
+PrintBranch(WasmPrintContext& c, AstBranch& branch)
+{
+    Expr expr = branch.expr();
+    MOZ_ASSERT(expr == Expr::BrIf || expr == Expr::Br);
+
+    if (expr == Expr::BrIf ? !c.buffer.append("br_if ") : !c.buffer.append("br "))
+        return false;
+
+    if (expr == Expr::BrIf) {
+        if (!PrintExpr(c, branch.cond()))
+            return false;
+        if (!c.buffer.append(","))
+            return false;
+    }
+
+    if (branch.maybeValue()) {
+        if (!PrintExpr(c, *(branch.maybeValue())))
+            return false;
+        if (!c.buffer.append(","))
+            return false;
+    }
+
+    if (!PrintRef(c, branch.target()))
+        return false;
+
+    return true;
+}
+
+static bool
+PrintBrTable(WasmPrintContext& c, AstBranchTable& table)
+{
+    if (!c.buffer.append("br_table "))
+        return false;
+
+    // Index
+    if (!PrintExpr(c, table.index()))
+        return false;
+
+    if (!c.buffer.append(","))
+        return false;
+
+    if (table.maybeValue()) {
+      if (!PrintExpr(c, *(table.maybeValue())))
+          return false;
+      if (!c.buffer.append(","))
+          return false;
+    }
+
+    uint32_t tableLength = table.table().length();
+    if (tableLength > 0) {
+        if (!c.buffer.append("["))
+            return false;
+        for (uint32_t i = 0; i < tableLength; i++) {
+            if (!PrintRef(c, table.table()[i]))
+                return false;
+            if (i + 1 == tableLength)
+                break;
+            if (!c.buffer.append(","))
+                return false;
+        }
+        if (!c.buffer.append("],"))
+            return false;
+    }
+
+    if (!PrintRef(c, table.def()))
+        return false;
+
+    return true;
+}
+
+static bool
+PrintReturn(WasmPrintContext& c, AstReturn& ret)
+{
+    if (!c.buffer.append("return"))
+        return false;
+
+    if (ret.maybeExpr()) {
+        if (!c.buffer.append(" "))
+            return false;
+        if (!PrintExpr(c, *(ret.maybeExpr())))
+            return false;
+    }
+
+    return true;
+}
+
+static bool
+PrintExpr(WasmPrintContext& c, AstExpr& expr)
+{
+    switch (expr.kind()) {
+      case AstExprKind::Nop:
+        return PrintNop(c, expr.as<AstNop>());
+      case AstExprKind::Unreachable:
+        return PrintUnreachable(c, expr.as<AstUnreachable>());
+      case AstExprKind::Call:
+        return PrintCall(c, expr.as<AstCall>());
+      case AstExprKind::CallIndirect:
+        return PrintCallIndirect(c, expr.as<AstCallIndirect>());
+      case AstExprKind::Const:
+        return PrintConst(c, expr.as<AstConst>());
+      case AstExprKind::GetLocal:
+        return PrintGetLocal(c, expr.as<AstGetLocal>());
+      case AstExprKind::SetLocal:
+        return PrintSetLocal(c, expr.as<AstSetLocal>());
+      case AstExprKind::Block:
+        return PrintBlock(c, expr.as<AstBlock>());
+      case AstExprKind::If:
+        return PrintIf(c, expr.as<AstIf>());
+      case AstExprKind::UnaryOperator:
+        return PrintUnaryOperator(c, expr.as<AstUnaryOperator>());
+      case AstExprKind::BinaryOperator:
+        return PrintBinaryOperator(c, expr.as<AstBinaryOperator>());
+      case AstExprKind::TernaryOperator:
+        return PrintTernaryOperator(c, expr.as<AstTernaryOperator>());
+      case AstExprKind::ComparisonOperator:
+        return PrintComparisonOperator(c, expr.as<AstComparisonOperator>());
+      case AstExprKind::ConversionOperator:
+        return PrintConversionOperator(c, expr.as<AstConversionOperator>());
+      case AstExprKind::Load:
+        return PrintLoad(c, expr.as<AstLoad>());
+      case AstExprKind::Store:
+        return PrintStore(c, expr.as<AstStore>());
+      case AstExprKind::Branch:
+        return PrintBranch(c, expr.as<AstBranch>());
+      case AstExprKind::BranchTable:
+        return PrintBrTable(c, expr.as<AstBranchTable>());
+      case AstExprKind::Return:
+        return PrintReturn(c, expr.as<AstReturn>());
+      default:
+        // Note: it's important not to remove this default since readExpr()
+        // can return Expr values for which there is no enumerator.
+        break;
+    }
+
+    return false;
+}
+
+static bool
+PrintSignature(WasmPrintContext& c, const AstSig& sig, const AstNameVector* maybeLocals = nullptr)
+{
+    uint32_t paramsNum = sig.args().length();
+
+    if (!c.buffer.append("("))
+        return false;
+    if (maybeLocals) {
+      for (uint32_t i = 0; i < paramsNum; i++) {
+          const AstName& name = (*maybeLocals)[i];
+          if (!name.empty()) {
+              if (!PrintName(c, name))
+                  return false;
+              if (!c.buffer.append(":"))
+                  return false;
+          }
+          ValType arg = sig.args()[i];
+          if (!PrintValType(c, arg))
+              return false;
+          if (i + 1 == paramsNum)
+              break;
+          if (!c.buffer.append(","))
+              return false;
+      }
+    } else if (paramsNum > 0) {
+      for (uint32_t i = 0; i < paramsNum; i++) {
+          ValType arg = sig.args()[i];
+          if (!PrintValType(c, arg))
+              return false;
+          if (i + 1 == paramsNum)
+              break;
+          if (!c.buffer.append(","))
+              return false;
+      }
+    }
+    if (!c.buffer.append(") : ("))
+        return false;
+    if (sig.ret() != ExprType::Void) {
+        if (!PrintExprType(c, sig.ret()))
+            return false;
+    }
+    if (!c.buffer.append(")"))
+        return false;
+    return true;
+}
+
+static bool
+PrintTypeSection(WasmPrintContext& c, const AstModule::SigVector& sigs)
+{
+    uint32_t numSigs = sigs.length();
+    if (!numSigs)
+        return true;
+
+    for (uint32_t sigIndex = 0; sigIndex < numSigs; sigIndex++) {
+        const AstSig* sig = sigs[sigIndex];
+        if (!PrintIndent(c))
+            return false;
+        if (!c.buffer.append("type "))
+            return false;
+        if (!sig->name().empty()) {
+          if (!PrintName(c, sig->name()))
+              return false;
+          if (!c.buffer.append(" of "))
+              return false;
+        }
+        if (!c.buffer.append("function "))
+            return false;
+        if (!PrintSignature(c, *sig))
+            return false;
+        if (!c.buffer.append("\n"))
+            return false;
+    }
+
+    if (!c.buffer.append("\n"))
+        return false;
+
+    return true;
+}
+
+static bool
+PrintTableSection(WasmPrintContext& c, AstTable* maybeTable, const AstModule::FuncVector& funcs)
+{
+    if (!maybeTable)
+        return true;
+
+    uint32_t numTableElems = maybeTable->elems().length();
+
+    if (!c.buffer.append("table ["))
+        return false;
+
+    for (uint32_t i = 0; i < numTableElems; i++) {
+        AstRef& elem = maybeTable->elems()[i];
+        AstFunc* func = funcs[elem.index()];
+        if (func->name().empty()) {
+            if (!PrintInt32(c, elem.index()))
+                return false;
+        } else {
+          if (!PrintName(c, func->name()))
+              return false;
+        }
+        if (i + 1 == numTableElems)
+            break;
+        if (!c.buffer.append(","))
+            return false;
+    }
+
+    if (!c.buffer.append("]\n\n"))
+        return false;
+
+    return true;
+}
+
+static bool
+PrintImport(WasmPrintContext& c, AstImport& import, const AstModule::SigVector& sigs)
+{
+    const AstSig* sig = sigs[import.sig().index()];
+    if (!PrintIndent(c))
+        return false;
+    if (!c.buffer.append("import "))
+        return false;
+    if (!c.buffer.append("\""))
+        return false;
+
+    const AstName& funcName = import.func();
+    if (!PrintEscapedString(c, funcName))
+        return false;
+
+    if (!c.buffer.append("\" as "))
+        return false;
+
+    if (!PrintName(c, import.name()))
+        return false;
+
+    if (!c.buffer.append(" from \""))
+        return false;
+
+    const AstName& moduleName = import.module();
+    if (!PrintEscapedString(c, moduleName))
+        return false;
+
+    if (!c.buffer.append("\" typeof function "))
+        return false;
+
+    if (!PrintSignature(c, *sig))
+        return false;
+    if (!c.buffer.append("\n"))
+        return false;
+
+    return true;
+}
+
+
+static bool
+PrintImportSection(WasmPrintContext& c, const AstModule::ImportVector& imports, const AstModule::SigVector& sigs)
+{
+    uint32_t numImports = imports.length();
+
+    for (uint32_t i = 0; i < numImports; i++) {
+        if (!PrintImport(c, *imports[i], sigs))
+            return false;
+    }
+
+    if (numImports) {
+      if (!c.buffer.append("\n"))
+          return false;
+    }
+
+    return true;
+}
+
+static bool
+PrintExport(WasmPrintContext& c, AstExport& export_, const AstModule::FuncVector& funcs)
+{
+    if (!PrintIndent(c))
+        return false;
+    if (!c.buffer.append("export "))
+        return false;
+    if (export_.kind() == AstExportKind::Memory) {
+        if (!c.buffer.append("memory"))
+          return false;
+    } else {
+        const AstFunc* func = funcs[export_.func().index()];
+        if (func->name().empty()) {
+            if (!PrintInt32(c, export_.func().index()))
+                return false;
+        } else {
+            if (!PrintName(c, func->name()))
+                return false;
+        }
+    }
+    if (!c.buffer.append(" as \""))
+        return false;
+    if (!PrintEscapedString(c, export_.name()))
+        return false;
+    if (!c.buffer.append("\"\n"))
+        return false;
+
+    return true;
+}
+
+static bool
+PrintExportSection(WasmPrintContext& c, const AstModule::ExportVector& exports, const AstModule::FuncVector& funcs)
+{
+    uint32_t numExports = exports.length();
+    for (uint32_t i = 0; i < numExports; i++) {
+        if (!PrintExport(c, *exports[i], funcs))
+            return false;
+    }
+    if (numExports) {
+      if (!c.buffer.append("\n"))
+          return false;
+    }
+    return true;
+}
+
+static bool
+PrintFunctionBody(WasmPrintContext& c, AstFunc& func, const AstModule::SigVector& sigs)
+{
+    const AstSig* sig = sigs[func.sig().index()];
+    c.indent++;
+
+    uint32_t argsNum = sig->args().length();
+    uint32_t localsNum = func.vars().length();
+    if (localsNum > 0) {
+        if (!PrintIndent(c))
+            return false;
+        if (!c.buffer.append("var "))
+            return false;
+        for (uint32_t i = 0; i < localsNum; i++) {
+            const AstName& name = func.locals()[argsNum + i];
+            if (!name.empty()) {
+              if (!PrintName(c, name))
+                  return false;
+              if (!c.buffer.append(":"))
+                  return false;
+            }
+            ValType local = func.vars()[i];
+            if (!PrintValType(c, local))
+                return false;
+            if (i + 1 == localsNum)
+                break;
+            if (!c.buffer.append(","))
+                return false;
+        }
+        if (!c.buffer.append("\n"))
+            return false;
+    }
+
+
+    uint32_t exprsNum = func.body().length();
+    for (uint32_t i = 0; i < exprsNum; i++) {
+      if (!PrintFullLine(c, *func.body()[i]))
+          return false;
+    }
+
+    c.indent--;
+
+    return true;
+}
+
+static bool
+PrintCodeSection(WasmPrintContext& c, const AstModule::FuncVector& funcs, const AstModule::SigVector& sigs)
+{
+    uint32_t numFuncBodies = funcs.length();
+    for (uint32_t funcIndex = 0; funcIndex < numFuncBodies; funcIndex++) {
+        AstFunc* func = funcs[funcIndex];
+        uint32_t sigIndex = func->sig().index();
+        AstSig* sig = sigs[sigIndex];
+
+        if (!PrintIndent(c))
+            return false;
+        if (!c.buffer.append("function "))
+            return false;
+        if (!func->name().empty()) {
+          if (!PrintName(c, func->name()))
+              return false;
+        }
+
+        if (!PrintSignature(c, *sig, &(func->locals())))
+            return false;
+        if (!c.buffer.append(" {\n"))
+            return false;
+
+        c.currentFuncIndex = funcIndex;
+
+        if (!PrintFunctionBody(c, *func, sigs))
+            return false;
+
+        if (!PrintIndent(c))
+            return false;
+        if (!c.buffer.append("}\n\n"))
+            return false;
+    }
+
+   return true;
+}
+
+
+static bool
+PrintDataSection(WasmPrintContext& c, AstMemory* maybeMemory)
+{
+    if (!maybeMemory)
+        return true;
+
+    if (!PrintIndent(c))
+        return false;
+    if (!c.buffer.append("memory "))
+        return false;
+    if (!PrintInt32(c, maybeMemory->initialSize()))
+       return false;
+    Maybe<uint32_t> memMax = maybeMemory->maxSize();
+    if (memMax) {
+        if (!c.buffer.append(","))
+            return false;
+        if (!PrintInt32(c, *memMax))
+            return false;
+    }
+
+    c.indent++;
+
+    uint32_t numSegments = maybeMemory->segments().length();
+    if (!numSegments) {
+      if (!c.buffer.append(" {}\n\n"))
+          return false;
+      return true;
+    }
+    if (!c.buffer.append(" {\n"))
+        return false;
+
+    for (uint32_t i = 0; i < numSegments; i++) {
+        const AstSegment* segment = maybeMemory->segments()[i];
+
+        if (!PrintIndent(c))
+            return false;
+        if (!c.buffer.append("segment "))
+           return false;
+        if (!PrintInt32(c, segment->offset()))
+           return false;
+        if (!c.buffer.append(" \""))
+           return false;
+
+        PrintEscapedString(c, segment->text());
+
+        if (!c.buffer.append("\"\n"))
+           return false;
+    }
+
+    c.indent--;
+    if (!c.buffer.append("}\n\n"))
+        return false;
+
+    return true;
+}
+
+static bool
+PrintModule(WasmPrintContext& c, AstModule& module)
+{
+    if (!PrintTypeSection(c, module.sigs()))
+        return false;
+
+    if (!PrintImportSection(c, module.imports(), module.sigs()))
+        return false;
+
+    if (!PrintTableSection(c, module.maybeTable(), module.funcs()))
+        return false;
+
+    if (!PrintExportSection(c, module.exports(), module.funcs()))
+        return false;
+
+    if (!PrintCodeSection(c, module.funcs(), module.sigs()))
+        return false;
+
+    if (!PrintDataSection(c, module.maybeMemory()))
+        return false;
+
+    return true;
+}
+
+/*****************************************************************************/
+// Top-level functions
+
+bool
+wasm::BinaryToExperimentalText(JSContext* cx, const uint8_t* bytes, size_t length,
+                               StringBuffer& buffer, const ExperimentalTextFormatting& formatting)
+{
+
+    LifoAlloc lifo(AST_LIFO_DEFAULT_CHUNK_SIZE);
+
+    AstModule* module;
+    if (!BinaryToAst(cx, bytes, length, lifo, &module))
+        return false;
+
+    WasmPrintContext c(cx, module, buffer, formatting);
+
+    if (!PrintModule(c, *module)) {
+        if (!cx->isExceptionPending())
+            ReportOutOfMemory(cx);
+        return false;
+    }
+
+    return true;
+}
+
new file mode 100644
--- /dev/null
+++ b/js/src/asmjs/WasmBinaryToExperimentalText.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2015 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef wasm_binary_to_experimental_text_h
+#define wasm_binary_to_experimental_text_h
+
+#include "NamespaceImports.h"
+
+#include "gc/Rooting.h"
+#include "js/Class.h"
+
+namespace js {
+
+class StringBuffer;
+
+namespace wasm {
+
+struct ExperimentalTextFormatting
+{
+    bool allowAsciiOperators:1;
+    bool reduceParens:1;
+    bool groupBlocks:1;
+
+    ExperimentalTextFormatting()
+     : allowAsciiOperators(true),
+       reduceParens(true),
+       groupBlocks(true)
+    {}
+};
+
+// Translate the given binary representation of a wasm module into the module's textual
+// representation.
+
+MOZ_MUST_USE bool
+BinaryToExperimentalText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuffer& buffer,
+                         const ExperimentalTextFormatting& formatting);
+
+}  // namespace wasm
+
+}  // namespace js
+
+#endif // namespace wasm_binary_to_experimental_text_h
--- a/js/src/asmjs/WasmBinaryToText.cpp
+++ b/js/src/asmjs/WasmBinaryToText.cpp
@@ -13,48 +13,40 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "asmjs/WasmBinaryToText.h"
 
-#include "mozilla/CheckedInt.h"
-
 #include "jsnum.h"
 #include "jsprf.h"
 
 #include "asmjs/Wasm.h"
 #include "asmjs/WasmAST.h"
 #include "asmjs/WasmBinaryToAST.h"
 #include "asmjs/WasmTypes.h"
 #include "vm/ArrayBufferObject.h"
 #include "vm/StringBuffer.h"
 
 using namespace js;
 using namespace js::wasm;
 
-using mozilla::CheckedInt;
 using mozilla::IsInfinite;
 using mozilla::IsNaN;
 using mozilla::IsNegativeZero;
 
 struct WasmRenderContext
 {
     JSContext* cx;
     AstModule* module;
     StringBuffer& buffer;
     uint32_t indent;
 
-    DeclaredSigVector signatures;
-    Uint32Vector funcSigs;
-    Uint32Vector funcLocals;
-    Uint32Vector importSigs;
-
     uint32_t currentFuncIndex;
 
     WasmRenderContext(JSContext* cx, AstModule* module, StringBuffer& buffer)
       : cx(cx), module(module), buffer(buffer), indent(0), currentFuncIndex(0)
     {}
 };
 
 /*****************************************************************************/
@@ -293,16 +285,20 @@ RenderCallIndirect(WasmRenderContext& c,
 
     if (!RenderExpr(c, *call.index()))
         return false;
 
     if (!c.buffer.append(" "))
         return false;
     if (!RenderCallArgs(c, call.args()))
         return false;
+
+    if (!c.buffer.append(")"))
+        return false;
+
     return true;
 }
 
 static bool
 RenderConst(WasmRenderContext& c, AstConst& cst)
 {
     if (!c.buffer.append('('))
         return false;
@@ -380,19 +376,35 @@ RenderExprList(WasmRenderContext& c, con
 }
 
 static bool
 RenderBlock(WasmRenderContext& c, AstBlock& block)
 {
     if (block.expr() == Expr::Block) {
         if (!c.buffer.append("(block "))
             return false;
+        if (!RenderName(c, block.breakName()))
+            return false;
     } else if (block.expr() == Expr::Loop) {
         if (!c.buffer.append("(loop "))
             return false;
+        if (block.breakName().empty() && !block.continueName().empty()) {
+            // Giving auto label if continue label is present.
+            if (!c.buffer.append("$exit$"))
+                return false;
+        } else {
+            if (!RenderName(c, block.breakName()))
+                return false;
+        }
+        if (!block.continueName().empty()) {
+          if (!c.buffer.append(" "))
+              return false;
+          if (!RenderName(c, block.continueName()))
+              return false;
+        }
     } else
         return false;
 
     c.indent++;
     if (!RenderExprList(c, block.exprs()))
         return false;
     c.indent--;
 
@@ -650,25 +662,31 @@ RenderIf(WasmRenderContext& c, AstIf& if
     if (!c.buffer.append("(if "))
         return false;
     if (!RenderExpr(c, if_.cond()))
         return false;
 
     if (!c.buffer.append(" (then "))
         return false;
 
+    if (!RenderName(c, if_.thenName()))
+        return false;
+
     c.indent++;
     if (!RenderExprList(c, if_.thenExprs()))
         return false;
     c.indent--;
 
     if (if_.hasElse()) {
         if (!c.buffer.append(") (else "))
             return false;
 
+        if (!RenderName(c, if_.elseName()))
+            return false;
+
         c.indent++;
         if (!RenderExprList(c, if_.elseExprs()))
             return false;
         c.indent--;
     }
 
     return c.buffer.append("))");
 }
@@ -1040,20 +1058,20 @@ RenderTypeSection(WasmRenderContext& c, 
 
     for (uint32_t sigIndex = 0; sigIndex < numSigs; sigIndex++) {
         const AstSig* sig = sigs[sigIndex];
         if (!RenderIndent(c))
             return false;
         if (!c.buffer.append("(type"))
             return false;
         if (!sig->name().empty()) {
-          if (!c.buffer.append(" "))
-              return false;
-          if (!RenderName(c, sig->name()))
-              return false;
+            if (!c.buffer.append(" "))
+                return false;
+            if (!RenderName(c, sig->name()))
+                return false;
         }
         if (!c.buffer.append(" (func"))
             return false;
         if (!RenderSignature(c, *sig))
             return false;
         if (!c.buffer.append("))\n"))
             return false;
     }
@@ -1063,32 +1081,37 @@ RenderTypeSection(WasmRenderContext& c, 
 static bool
 RenderTableSection(WasmRenderContext& c, AstTable* maybeTable, const AstModule::FuncVector& funcs)
 {
     if (!maybeTable)
         return true;
 
     uint32_t numTableElems = maybeTable->elems().length();
 
-    if (!c.buffer.append("(table "))
+    if (!RenderIndent(c))
+        return false;
+
+    if (!c.buffer.append("(table"))
         return false;
 
     for (uint32_t i = 0; i < numTableElems; i++) {
+        if (!c.buffer.append(" "))
+            return false;
         AstRef& elem = maybeTable->elems()[i];
         AstFunc* func = funcs[elem.index()];
         if (func->name().empty()) {
             if (!RenderInt32(c, elem.index()))
                 return false;
         } else {
           if (!RenderName(c, func->name()))
               return false;
         }
     }
 
-    if (!c.buffer.append(")"))
+    if (!c.buffer.append(")\n"))
         return false;
 
     return true;
 }
 
 static bool
 RenderImport(WasmRenderContext& c, AstImport& import, const AstModule::SigVector& sigs)
 {
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -20,16 +20,17 @@
 
 #include "mozilla/Atomics.h"
 #include "mozilla/BinarySearch.h"
 #include "mozilla/EnumeratedRange.h"
 #include "mozilla/PodOperations.h"
 
 #include "jsprf.h"
 
+#include "asmjs/WasmBinaryToExperimentalText.h"
 #include "asmjs/WasmBinaryToText.h"
 #include "asmjs/WasmSerialize.h"
 #include "builtin/AtomicsObject.h"
 #include "builtin/SIMD.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "jit/BaselineJIT.h"
@@ -1668,17 +1669,17 @@ const char enabledMessage[] =
 
 JSString*
 Module::createText(JSContext* cx)
 {
     StringBuffer buffer(cx);
     if (!source_.empty()) {
         if (!buffer.append(experimentalWarning))
             return nullptr;
-        if (!BinaryToText(cx, source_.begin(), source_.length(), buffer))
+        if (!BinaryToExperimentalText(cx, source_.begin(), source_.length(), buffer, ExperimentalTextFormatting()))
             return nullptr;
     } else {
         if (!buffer.append(enabledMessage))
             return nullptr;
     }
     return buffer.finishString();
 }
 
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -462,36 +462,36 @@ typedef UniquePtr<ModuleData> UniqueModu
 // callExport().
 
 class Module : public mozilla::LinkedListElement<Module>
 {
     typedef UniquePtr<const ModuleData> UniqueConstModuleData;
     struct ImportExit {
         void* code;
         jit::BaselineScript* baselineScript;
-        HeapPtrFunction fun;
-        static_assert(sizeof(HeapPtrFunction) == sizeof(void*), "for JIT access");
+        GCPtrFunction fun;
+        static_assert(sizeof(GCPtrFunction) == sizeof(void*), "for JIT access");
     };
     struct EntryArg {
         uint64_t lo;
         uint64_t hi;
     };
     typedef int32_t (*EntryFuncPtr)(EntryArg* args, uint8_t* global);
     struct FuncPtrTable {
         uint32_t globalDataOffset;
         uint32_t numElems;
         explicit FuncPtrTable(const StaticLinkData::FuncPtrTable& table)
           : globalDataOffset(table.globalDataOffset),
             numElems(table.elemOffsets.length())
         {}
     };
     typedef Vector<FuncPtrTable, 0, SystemAllocPolicy> FuncPtrTableVector;
     typedef Vector<CacheableChars, 0, SystemAllocPolicy> FuncLabelVector;
-    typedef RelocatablePtrArrayBufferObjectMaybeShared BufferPtr;
-    typedef HeapPtr<WasmModuleObject*> ModuleObjectPtr;
+    typedef HeapPtr<ArrayBufferObjectMaybeShared*> BufferPtr;
+    typedef GCPtr<WasmModuleObject*> ModuleObjectPtr;
 
     // Initialized when constructed:
     const UniqueConstModuleData  module_;
 
     // Initialized during staticallyLink:
     bool                         staticallyLinked_;
     uint8_t*                     interrupt_;
     uint8_t*                     outOfBounds_;
@@ -533,17 +533,17 @@ class Module : public mozilla::LinkedLis
 
     explicit Module(UniqueModuleData module);
     virtual ~Module();
     virtual void trace(JSTracer* trc);
     virtual void readBarrier();
     virtual void addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data);
 
     void setOwner(WasmModuleObject* owner) { MOZ_ASSERT(!ownerObject_); ownerObject_ = owner; }
-    inline const HeapPtr<WasmModuleObject*>& owner() const;
+    inline const GCPtr<WasmModuleObject*>& owner() const;
 
     void setSource(Bytes&& source) { source_ = Move(source); }
 
     uint8_t* code() const { return module_->code.get(); }
     uint32_t codeBytes() const { return module_->codeBytes; }
     uint8_t* globalData() const { return code() + module_->codeBytes; }
     uint32_t globalBytes() const { return module_->globalBytes; }
     HeapUsage heapUsage() const { return module_->heapUsage; }
@@ -683,17 +683,17 @@ class WasmModuleObject : public NativeOb
     static const unsigned RESERVED_SLOTS = 1;
     static WasmModuleObject* create(ExclusiveContext* cx);
     MOZ_MUST_USE bool init(wasm::Module* module);
     wasm::Module& module() const;
     void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data);
     static const Class class_;
 };
 
-inline const HeapPtr<WasmModuleObject*>&
+inline const GCPtr<WasmModuleObject*>&
 wasm::Module::owner() const {
     MOZ_ASSERT(&ownerObject_->module() == this);
     return ownerObject_;
 }
 
 using WasmModuleObjectVector = GCVector<WasmModuleObject*>;
 
 } // namespace js
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -288,24 +288,30 @@ EvalKernel(JSContext* cx, HandleValue v,
             enclosing = callerScript->innermostStaticScope(pc);
         else
             enclosing = &cx->global()->lexicalScope().staticBlock();
         Rooted<StaticEvalScope*> staticScope(cx, StaticEvalScope::create(cx, enclosing));
         if (!staticScope)
             return false;
 
         CompileOptions options(cx);
-        options.setFileAndLine(filename, 1)
-               .setIsRunOnce(true)
+        options.setIsRunOnce(true)
                .setForEval(true)
                .setNoScriptRval(false)
                .setMutedErrors(mutedErrors)
-               .setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset)
                .maybeMakeStrictMode(evalType == DIRECT_EVAL && IsStrictEvalPC(pc));
 
+        if (introducerFilename) {
+            options.setFileAndLine(filename, 1);
+            options.setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset);
+        } else {
+            options.setFileAndLine("eval", 1);
+            options.setIntroductionType("eval");
+        }
+
         AutoStableStringChars linearChars(cx);
         if (!linearChars.initTwoByte(cx, linearStr))
             return false;
 
         const char16_t* chars = linearChars.twoByteRange().start().get();
         SourceBufferHolder::Ownership ownership = linearChars.maybeGiveOwnershipToCaller()
                                                   ? SourceBufferHolder::GiveOwnership
                                                   : SourceBufferHolder::NoOwnership;
@@ -370,24 +376,30 @@ js::DirectEvalStringFromIon(JSContext* c
             introducerFilename = maybeScript->scriptSource()->introducerFilename();
 
         RootedObject enclosing(cx, callerScript->innermostStaticScope(pc));
         Rooted<StaticEvalScope*> staticScope(cx, StaticEvalScope::create(cx, enclosing));
         if (!staticScope)
             return false;
 
         CompileOptions options(cx);
-        options.setFileAndLine(filename, 1)
-               .setIsRunOnce(true)
+        options.setIsRunOnce(true)
                .setForEval(true)
                .setNoScriptRval(false)
                .setMutedErrors(mutedErrors)
-               .setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset)
                .maybeMakeStrictMode(IsStrictEvalPC(pc));
 
+        if (introducerFilename) {
+            options.setFileAndLine(filename, 1);
+            options.setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset);
+        } else {
+            options.setFileAndLine("eval", 1);
+            options.setIntroductionType("eval");
+        }
+
         AutoStableStringChars linearChars(cx);
         if (!linearChars.initTwoByte(cx, linearStr))
             return false;
 
         const char16_t* chars = linearChars.twoByteRange().start().get();
         SourceBufferHolder::Ownership ownership = linearChars.maybeGiveOwnershipToCaller()
                                                   ? SourceBufferHolder::GiveOwnership
                                                   : SourceBufferHolder::NoOwnership;
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -433,17 +433,17 @@ MapObject::set(JSContext* cx, HandleObje
     ValueMap* map = obj->as<MapObject>().getData();
     if (!map)
         return false;
 
     Rooted<HashableValue> key(cx);
     if (!key.setValue(cx, k))
         return false;
 
-    RelocatableValue rval(v);
+    HeapPtr<Value> rval(v);
     if (!map->put(key, rval)) {
         ReportOutOfMemory(cx);
         return false;
     }
     WriteBarrierPost(cx->runtime(), map, key.value());
     return true;
 }
 
@@ -533,17 +533,17 @@ MapObject::construct(JSContext* cx, unsi
             RootedValue val(cx);
             if (!GetElement(cx, pairObj, pairObj, 1, &val))
                 return false;
 
             if (isOriginalAdder) {
                 if (!hkey.setValue(cx, key))
                     return false;
 
-                RelocatableValue rval(val);
+                HeapPtr<Value> rval(val);
                 if (!map->put(hkey, rval)) {
                     ReportOutOfMemory(cx);
                     return false;
                 }
                 WriteBarrierPost(cx->runtime(), map, key);
             } else {
                 if (!args2.init(2))
                     return false;
@@ -683,17 +683,17 @@ MapObject::has(JSContext* cx, unsigned a
 
 bool
 MapObject::set_impl(JSContext* cx, const CallArgs& args)
 {
     MOZ_ASSERT(MapObject::is(args.thisv()));
 
     ValueMap& map = extract(args);
     ARG0_KEY(cx, args, key);
-    RelocatableValue rval(args.get(1));
+    HeapPtr<Value> rval(args.get(1));
     if (!map.put(key, rval)) {
         ReportOutOfMemory(cx);
         return false;
     }
     WriteBarrierPost(cx->runtime(), &map, key.value());
     args.rval().set(args.thisv());
     return true;
 }
@@ -720,24 +720,24 @@ MapObject::delete_(JSContext *cx, Handle
     }
     return true;
 }
 
 bool
 MapObject::delete_impl(JSContext *cx, const CallArgs& args)
 {
     // MapObject::mark does not mark deleted entries. Incremental GC therefore
-    // requires that no RelocatableValue objects pointing to heap values be
-    // left alive in the ValueMap.
+    // requires that no HeapPtr<Value> objects pointing to heap values be left
+    // alive in the ValueMap.
     //
     // OrderedHashMap::remove() doesn't destroy the removed entry. It merely
     // calls OrderedHashMap::MapOps::makeEmpty. But that is sufficient, because
     // makeEmpty clears the value by doing e->value = Value(), and in the case
-    // of a ValueMap, Value() means RelocatableValue(), which is the same as
-    // RelocatableValue(UndefinedValue()).
+    // of a ValueMap, Value() means HeapPtr<Value>(), which is the same as
+    // HeapPtr<Value>(UndefinedValue()).
     MOZ_ASSERT(MapObject::is(args.thisv()));
 
     ValueMap& map = extract(args);
     ARG0_KEY(cx, args, key);
     bool found;
     if (!map.remove(key, &found)) {
         ReportOutOfMemory(cx);
         return false;
--- a/js/src/builtin/MapObject.h
+++ b/js/src/builtin/MapObject.h
@@ -61,17 +61,17 @@ class RootedBase<HashableValue> {
 
 template <class Key, class Value, class OrderedHashPolicy, class AllocPolicy>
 class OrderedHashMap;
 
 template <class T, class OrderedHashPolicy, class AllocPolicy>
 class OrderedHashSet;
 
 typedef OrderedHashMap<HashableValue,
-                       RelocatableValue,
+                       HeapPtr<Value>,
                        HashableValue::Hasher,
                        RuntimeAllocPolicy> ValueMap;
 
 typedef OrderedHashSet<HashableValue,
                        HashableValue::Hasher,
                        RuntimeAllocPolicy> ValueSet;
 
 class MapObject : public NativeObject {
--- a/js/src/builtin/ModuleObject.h
+++ b/js/src/builtin/ModuleObject.h
@@ -113,18 +113,18 @@ class IndirectBindingMap
         for (auto r = map_.all(); !r.empty(); r.popFront())
             func(r.front().key());
     }
 
   private:
     struct Binding
     {
         Binding(ModuleEnvironmentObject* environment, Shape* shape);
-        RelocatablePtr<ModuleEnvironmentObject*> environment;
-        RelocatablePtrShape shape;
+        HeapPtr<ModuleEnvironmentObject*> environment;
+        HeapPtr<Shape*> shape;
     };
 
     typedef HashMap<jsid, Binding, DefaultHasher<jsid>, ZoneAllocPolicy> Map;
 
     Map map_;
 };
 
 class ModuleNamespaceObject : public ProxyObject
@@ -189,18 +189,18 @@ class ModuleNamespaceObject : public Pro
 typedef Rooted<ModuleNamespaceObject*> RootedModuleNamespaceObject;
 typedef Handle<ModuleNamespaceObject*> HandleModuleNamespaceObject;
 
 struct FunctionDeclaration
 {
     FunctionDeclaration(HandleAtom name, HandleFunction fun);
     void trace(JSTracer* trc);
 
-    RelocatablePtrAtom name;
-    RelocatablePtrFunction fun;
+    HeapPtr<JSAtom*> name;
+    HeapPtr<JSFunction*> fun;
 };
 
 using FunctionDeclarationVector = GCVector<FunctionDeclaration, 0, ZoneAllocPolicy>;
 
 class ModuleObject : public NativeObject
 {
   public:
     enum
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -16,16 +16,17 @@
 #include "jsfriendapi.h"
 #include "jsgc.h"
 #include "jsobj.h"
 #include "jsprf.h"
 #include "jswrapper.h"
 
 #include "asmjs/AsmJS.h"
 #include "asmjs/Wasm.h"
+#include "asmjs/WasmBinaryToExperimentalText.h"
 #include "asmjs/WasmBinaryToText.h"
 #include "asmjs/WasmTextToBinary.h"
 #include "builtin/Promise.h"
 #include "builtin/SelfHostingDefines.h"
 #include "jit/InlinableNatives.h"
 #include "jit/JitFrameIterator.h"
 #include "js/Debug.h"
 #include "js/HashTable.h"
@@ -575,21 +576,40 @@ WasmBinaryToText(JSContext* cx, unsigned
 
     Vector<uint8_t> copy(cx);
     if (code->bufferUnshared()->hasInlineData()) {
         if (!copy.append(bytes, length))
             return false;
         bytes = copy.begin();
     }
 
+    bool experimental = false;
+    if (args.length() > 1) {
+        JSString* opt = JS::ToString(cx, args[1]);
+        if (!opt)
+            return false;
+        bool match;
+        if (!JS_StringEqualsAscii(cx, opt, "experimental", &match))
+            return false;
+        experimental = match;
+    }
+
     StringBuffer buffer(cx);
-    if (!wasm::BinaryToText(cx, bytes, length, buffer)) {
-        if (!cx->isExceptionPending())
-            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL, "print error");
-        return false;
+    if (experimental) {
+        if (!wasm::BinaryToExperimentalText(cx, bytes, length, buffer, wasm::ExperimentalTextFormatting())) {
+            if (!cx->isExceptionPending())
+                JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL, "print error");
+            return false;
+        }
+    } else {
+        if (!wasm::BinaryToText(cx, bytes, length, buffer)) {
+            if (!cx->isExceptionPending())
+                JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL, "print error");
+            return false;
+        }
     }
 
     JSString* result = buffer.finishString();
     if (!result)
         return false;
 
     args.rval().setString(result);
     return true;
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -2609,17 +2609,17 @@ js::StoreScalar##T::Func(JSContext*, uns
     double d = args[2].toNumber();                                              \
     *target = ConvertScalar<T>(d);                                              \
     args.rval().setUndefined();                                                 \
     return true;                                                                \
 }
 
 #define JS_STORE_REFERENCE_CLASS_IMPL(_constant, T, _name)                      \
 bool                                                                            \
-js::StoreReference##T::Func(JSContext* cx, unsigned argc, Value* vp)    \
+js::StoreReference##_name::Func(JSContext* cx, unsigned argc, Value* vp)        \
 {                                                                               \
     CallArgs args = CallArgsFromVp(argc, vp);                                   \
     MOZ_ASSERT(args.length() == 4);                                             \
     MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());     \
     MOZ_ASSERT(args[1].isInt32());                                              \
     MOZ_ASSERT(args[2].isString() || args[2].isNull());                         \
                                                                                 \
     TypedObject& typedObj = args[0].toObject().as<TypedObject>();               \
@@ -2636,17 +2636,17 @@ js::StoreReference##T::Func(JSContext* c
     if (!store(cx, target, args[3], &typedObj, id))                             \
         return false;                                                           \
     args.rval().setUndefined();                                                 \
     return true;                                                                \
 }
 
 #define JS_LOAD_SCALAR_CLASS_IMPL(_constant, T, _name)                                  \
 bool                                                                                    \
-js::LoadScalar##T::Func(JSContext*, unsigned argc, Value* vp)                  \
+js::LoadScalar##T::Func(JSContext*, unsigned argc, Value* vp)                           \
 {                                                                                       \
     CallArgs args = CallArgsFromVp(argc, vp);                                           \
     MOZ_ASSERT(args.length() == 2);                                                     \
     MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());             \
     MOZ_ASSERT(args[1].isInt32());                                                      \
                                                                                         \
     TypedObject& typedObj = args[0].toObject().as<TypedObject>();                       \
     int32_t offset = args[1].toInt32();                                                 \
@@ -2656,17 +2656,17 @@ js::LoadScalar##T::Func(JSContext*, unsi
                                                                                         \
     T* target = reinterpret_cast<T*>(typedObj.typedMem(offset));                        \
     args.rval().setNumber((double) *target);                                            \
     return true;                                                                        \
 }
 
 #define JS_LOAD_REFERENCE_CLASS_IMPL(_constant, T, _name)                       \
 bool                                                                            \
-js::LoadReference##T::Func(JSContext*, unsigned argc, Value* vp)       \
+js::LoadReference##_name::Func(JSContext*, unsigned argc, Value* vp)            \
 {                                                                               \
     CallArgs args = CallArgsFromVp(argc, vp);                                   \
     MOZ_ASSERT(args.length() == 2);                                             \
     MOZ_ASSERT(args[0].isObject() && args[0].toObject().is<TypedObject>());     \
     MOZ_ASSERT(args[1].isInt32());                                              \
                                                                                 \
     TypedObject& typedObj = args[0].toObject().as<TypedObject>();               \
     int32_t offset = args[1].toInt32();                                         \
@@ -2679,36 +2679,36 @@ js::LoadReference##T::Func(JSContext*, u
     return true;                                                                \
 }
 
 // Because the precise syntax for storing values/objects/strings
 // differs, we abstract it away using specialized variants of the
 // private methods `store()` and `load()`.
 
 bool
-StoreReferenceHeapValue::store(JSContext* cx, HeapValue* heap, const Value& v,
-                               TypedObject* obj, jsid id)
+StoreReferenceAny::store(JSContext* cx, GCPtrValue* heap, const Value& v,
+                         TypedObject* obj, jsid id)
 {
     // Undefined values are not included in type inference information for
     // value properties of typed objects, as these properties are always
     // considered to contain undefined.
     if (!v.isUndefined()) {
         if (cx->isJSContext())
             AddTypePropertyId(cx->asJSContext(), obj, id, v);
         else if (!HasTypePropertyId(obj, id, v))
             return false;
     }
 
     *heap = v;
     return true;
 }
 
 bool
-StoreReferenceHeapPtrObject::store(JSContext* cx, HeapPtrObject* heap, const Value& v,
-                                   TypedObject* obj, jsid id)
+StoreReferenceObject::store(JSContext* cx, GCPtrObject* heap, const Value& v,
+                            TypedObject* obj, jsid id)
 {
     MOZ_ASSERT(v.isObjectOrNull()); // or else Store_object is being misused
 
     // Null pointers are not included in type inference information for
     // object properties of typed objects, as these properties are always
     // considered to contain null.
     if (v.isObject()) {
         if (cx->isJSContext())
@@ -2717,47 +2717,44 @@ StoreReferenceHeapPtrObject::store(JSCon
             return false;
     }
 
     *heap = v.toObjectOrNull();
     return true;
 }
 
 bool
-StoreReferenceHeapPtrString::store(JSContext* cx, HeapPtrString* heap, const Value& v,
-                                   TypedObject* obj, jsid id)
+StoreReferencestring::store(JSContext* cx, GCPtrString* heap, const Value& v,
+                            TypedObject* obj, jsid id)
 {
     MOZ_ASSERT(v.isString()); // or else Store_string is being misused
 
     // Note: string references are not reflected in type information for the object.
     *heap = v.toString();
 
     return true;
 }
 
 void
-LoadReferenceHeapValue::load(HeapValue* heap,
-                             MutableHandleValue v)
+LoadReferenceAny::load(GCPtrValue* heap, MutableHandleValue v)
 {
     v.set(*heap);
 }
 
 void
-LoadReferenceHeapPtrObject::load(HeapPtrObject* heap,
-                                 MutableHandleValue v)
+LoadReferenceObject::load(GCPtrObject* heap, MutableHandleValue v)
 {
     if (*heap)
         v.setObject(**heap);
     else
         v.setNull();
 }
 
 void
-LoadReferenceHeapPtrString::load(HeapPtrString* heap,
-                                 MutableHandleValue v)
+LoadReferencestring::load(GCPtrString* heap, MutableHandleValue v)
 {
     v.setString(*heap);
 }
 
 // I was using templates for this stuff instead of macros, but ran
 // into problems with the Unagi compiler.
 JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_STORE_SCALAR_CLASS_IMPL)
 JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(JS_LOAD_SCALAR_CLASS_IMPL)
@@ -2830,33 +2827,33 @@ class MemoryInitVisitor {
 } // namespace
 
 void
 MemoryInitVisitor::visitReference(ReferenceTypeDescr& descr, uint8_t* mem)
 {
     switch (descr.type()) {
       case ReferenceTypeDescr::TYPE_ANY:
       {
-        js::HeapValue* heapValue = reinterpret_cast<js::HeapValue*>(mem);
+        js::GCPtrValue* heapValue = reinterpret_cast<js::GCPtrValue*>(mem);
         heapValue->init(UndefinedValue());
         return;
       }
 
       case ReferenceTypeDescr::TYPE_OBJECT:
       {
-        js::HeapPtrObject* objectPtr =
-            reinterpret_cast<js::HeapPtrObject*>(mem);
+        js::GCPtrObject* objectPtr =
+            reinterpret_cast<js::GCPtrObject*>(mem);
         objectPtr->init(nullptr);
         return;
       }
 
       case ReferenceTypeDescr::TYPE_STRING:
       {
-        js::HeapPtrString* stringPtr =
-            reinterpret_cast<js::HeapPtrString*>(mem);
+        js::GCPtrString* stringPtr =
+            reinterpret_cast<js::GCPtrString*>(mem);
         stringPtr->init(rt_->emptyString);
         return;
       }
     }
 
     MOZ_CRASH("Invalid kind");
 }
 
@@ -2900,31 +2897,31 @@ class MemoryTracingVisitor {
 } // namespace
 
 void
 MemoryTracingVisitor::visitReference(ReferenceTypeDescr& descr, uint8_t* mem)
 {
     switch (descr.type()) {
       case ReferenceTypeDescr::TYPE_ANY:
       {
-        HeapValue* heapValue = reinterpret_cast<js::HeapValue*>(mem);
+        GCPtrValue* heapValue = reinterpret_cast<js::GCPtrValue*>(mem);
         TraceEdge(trace_, heapValue, "reference-val");
         return;
       }
 
       case ReferenceTypeDescr::TYPE_OBJECT:
       {
-        HeapPtrObject* objectPtr = reinterpret_cast<js::HeapPtrObject*>(mem);
+        GCPtrObject* objectPtr = reinterpret_cast<js::GCPtrObject*>(mem);
         TraceNullableEdge(trace_, objectPtr, "reference-obj");
         return;
       }
 
       case ReferenceTypeDescr::TYPE_STRING:
       {
-        HeapPtrString* stringPtr = reinterpret_cast<js::HeapPtrString*>(mem);
+        GCPtrString* stringPtr = reinterpret_cast<js::GCPtrString*>(mem);
         TraceNullableEdge(trace_, stringPtr, "reference-str");
         return;
       }
     }
 
     MOZ_CRASH("Invalid kind");
 }
 
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -305,20 +305,20 @@ class ReferenceTypeDescr : public Simple
 
     const char* typeName() const {
         return typeName(type());
     }
 
     static MOZ_MUST_USE bool call(JSContext* cx, unsigned argc, Value* vp);
 };
 
-#define JS_FOR_EACH_REFERENCE_TYPE_REPR(macro_)                    \
-    macro_(ReferenceTypeDescr::TYPE_ANY,    HeapValue, Any)        \
-    macro_(ReferenceTypeDescr::TYPE_OBJECT, HeapPtrObject, Object) \
-    macro_(ReferenceTypeDescr::TYPE_STRING, HeapPtrString, string)
+#define JS_FOR_EACH_REFERENCE_TYPE_REPR(macro_) \
+    macro_(ReferenceTypeDescr::TYPE_ANY, GCPtrValue, Any) \
+    macro_(ReferenceTypeDescr::TYPE_OBJECT, GCPtrObject, Object) \
+    macro_(ReferenceTypeDescr::TYPE_STRING, GCPtrString, string)
 
 // Type descriptors whose instances are objects and hence which have
 // an associated `prototype` property.
 class ComplexTypeDescr : public TypeDescr
 {
   public:
     // Returns the prototype that instances of this type descriptor
     // will have.
@@ -493,17 +493,17 @@ class TypedObject : public JSObject
                                                  Handle<TypedObject*> typedObj,
                                                  Handle<TypeDescr*> typeDescr,
                                                  uint32_t index,
                                                  MutableHandleValue vp);
 
   protected:
     static const ObjectOps objectOps_;
 
-    HeapPtrShape shape_;
+    GCPtrShape shape_;
 
     static MOZ_MUST_USE bool obj_lookupProperty(JSContext* cx, HandleObject obj,
                                                 HandleId id, MutableHandleObject objp,
                                                 MutableHandleShape propp);
 
     static MOZ_MUST_USE bool obj_defineProperty(JSContext* cx, HandleObject obj, HandleId id,
                                                 Handle<PropertyDescriptor> desc,
                                                 ObjectOpResult& result);
@@ -580,17 +580,17 @@ class TypedObject : public JSObject
     Shape** addressOfShapeFromGC() { return shape_.unsafeUnbarrieredForTracing(); }
 };
 
 typedef Handle<TypedObject*> HandleTypedObject;
 
 class OutlineTypedObject : public TypedObject
 {
     // The object which owns the data this object points to. Because this
-    // pointer is managed in tandem with |data|, this is not a HeapPtr and
+    // pointer is managed in tandem with |data|, this is not a GCPtr and
     // barriers are managed directly.
     JSObject* owner_;
 
     // Data pointer to some offset in the owner's contents.
     uint8_t* data_;
 
     void setOwnerAndData(JSObject* owner, uint8_t* data);
 
@@ -860,17 +860,17 @@ class StoreScalar##T {                  
  * `targetDatum` at the offset `targetOffset`.
  *
  * Assumes (and asserts) that:
  * - `targetDatum` is attached
  * - `targetOffset` is a valid offset within the bounds of `targetDatum`
  * - `value` is an object or null (`Store_Object`) or string (`Store_string`).
  */
 #define JS_STORE_REFERENCE_CLASS_DEFN(_constant, T, _name)                    \
-class StoreReference##T {                                                     \
+class StoreReference##_name {                                                 \
   private:                                                                    \
     static MOZ_MUST_USE bool store(JSContext* cx, T* heap, const Value& v,    \
                                    TypedObject* obj, jsid id);                \
                                                                               \
   public:                                                                     \
     static MOZ_MUST_USE bool Func(JSContext* cx, unsigned argc, Value* vp);   \
     static const JSJitInfo JitInfo;                                           \
 };
@@ -894,17 +894,17 @@ class LoadScalar##T {                   
  * Usage: LoadReference(targetDatum, targetOffset, value)
  *
  * Intrinsic function. Stores value (which must be an int32 or uint32)
  * by `scalarTypeRepr` (which must be a type repr obj) and stores the
  * value at the memory for `targetDatum` at offset `targetOffset`.
  * `targetDatum` must be attached.
  */
 #define JS_LOAD_REFERENCE_CLASS_DEFN(_constant, T, _name)                     \
-class LoadReference##T {                                                      \
+class LoadReference##_name {                                                  \
   private:                                                                    \
     static void load(T* heap, MutableHandleValue v);                          \
                                                                               \
   public:                                                                     \
     static MOZ_MUST_USE bool Func(JSContext* cx, unsigned argc, Value* vp);   \
     static const JSJitInfo JitInfo;                                           \
 };
 
--- a/js/src/ctypes/CTypes.h
+++ b/js/src/ctypes/CTypes.h
@@ -276,18 +276,18 @@ struct FieldHashPolicy : DefaultHasher<J
 
     if (k->length() != l->length())
       return false;
 
     return EqualChars(k, l);
   }
 };
 
-using FieldInfoHash = GCHashMap<js::RelocatablePtr<JSFlatString*>,
-                                FieldInfo, FieldHashPolicy, SystemAllocPolicy>;
+using FieldInfoHash = GCHashMap<js::HeapPtr<JSFlatString*>, FieldInfo,
+                                FieldHashPolicy, SystemAllocPolicy>;
 
 // Descriptor of ABI, return type, argument types, and variadicity for a
 // FunctionType.
 struct FunctionInfo
 {
   // Initialized in NewFunctionInfo when !mIsVariadic, but only later, in
   // FunctionType::Call, when mIsVariadic. Not always consistent with
   // mFFITypes, due to lazy initialization when mIsVariadic.
--- a/js/src/devtools/automation/autospider.sh
+++ b/js/src/devtools/automation/autospider.sh
@@ -177,17 +177,18 @@ COMMAND_PREFIX=''
 # On Linux, disable ASLR to make shell builds a bit more reproducible.
 if type setarch >/dev/null 2>&1; then
     COMMAND_PREFIX="setarch $(uname -m) -R "
 fi
 
 RUN_JSTESTS=true
 RUN_JITTEST=true
 RUN_JSAPITESTS=true
-RUN_CHECK_STYLE_ONLY=false
+: ${RUN_CHECK_STYLE_ONLY:=false}
+: ${RUN_MAKE_CHECKS:=true}
 
 PARENT=$$
 
 # Spawn off a child process, detached from any of our fds, that will kill us after a timeout.
 # To report the timeout, catch the signal in the parent before exiting.
 sh -c "sleep $TIMEOUT; kill -INT $PARENT" <&- >&- 2>&- &
 KILLER=$!
 disown %1
@@ -228,20 +229,22 @@ elif [[ "$VARIANT" = "nonunified" ]]; th
     RUN_CHECK_STYLE_ONLY=true
 elif [[ "$VARIANT" = arm64* ]]; then
     # The ARM64 simulator is slow, so some tests are timing out.
     # Run a reduced set of test cases so this doesn't take hours.
     export JSTESTS_EXTRA_ARGS="--exclude-file=$ABSDIR/arm64-jstests-slow.txt"
     export JITTEST_EXTRA_ARGS="--jitflags=none --args=--baseline-eager -x ion/ -x asm.js/"
 fi
 
-if $RUN_CHECK_STYLE_ONLY; then
-    $COMMAND_PREFIX $MAKE check-style || exit 1
-else
-    $COMMAND_PREFIX $MAKE check || exit 1
+if $RUN_MAKE_CHECKS; then
+    if $RUN_CHECK_STYLE_ONLY; then
+        $COMMAND_PREFIX $MAKE check-style || exit 1
+    else
+        $COMMAND_PREFIX $MAKE check || exit 1
+    fi
 fi
 
 RESULT=0
 
 if $RUN_JITTEST; then
     $COMMAND_PREFIX $MAKE check-jit-test || RESULT=$?
 fi
 if $RUN_JSAPITESTS; then
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -9375,17 +9375,17 @@ CGObjectList::indexOf(JSObject* obj)
 }
 
 void
 CGObjectList::finish(ObjectArray* array)
 {
     MOZ_ASSERT(length <= INDEX_LIMIT);
     MOZ_ASSERT(length == array->length);
 
-    js::HeapPtrObject* cursor = array->vector + array->length;
+    js::GCPtrObject* cursor = array->vector + array->length;
     ObjectBox* objbox = lastbox;
     do {
         --cursor;
         MOZ_ASSERT(!*cursor);
         MOZ_ASSERT(objbox->object->isTenured());
         *cursor = objbox->object;
     } while ((objbox = objbox->emitLink) != nullptr);
     MOZ_ASSERT(cursor == array->vector);
--- a/js/src/frontend/ParseMaps-inl.h
+++ b/js/src/frontend/ParseMaps-inl.h
@@ -17,49 +17,49 @@ namespace frontend {
 template <class Map>
 inline bool
 AtomThingMapPtr<Map>::ensureMap(ExclusiveContext* cx)
 {
     if (map_)
         return true;
 
     AutoLockForExclusiveAccess lock(cx);
-    map_ = cx->parseMapPool().acquire<Map>();
+    map_ = cx->parseMapPool(lock).acquire<Map>();
     if (!map_)
         ReportOutOfMemory(cx);
     return !!map_;
 }
 
 template <class Map>
 inline void
 AtomThingMapPtr<Map>::releaseMap(ExclusiveContext* cx)
 {
     if (!map_)
         return;
 
     AutoLockForExclusiveAccess lock(cx);
-    cx->parseMapPool().release(map_);
+    cx->parseMapPool(lock).release(map_);
     map_ = nullptr;
 }
 
 template <typename ParseHandler>
 inline bool
 AtomDecls<ParseHandler>::init()
 {
     AutoLockForExclusiveAccess lock(cx);
-    map = cx->parseMapPool().acquire<AtomDefnListMap>();
+    map = cx->parseMapPool(lock).acquire<AtomDefnListMap>();
     return map;
 }
 
 template <typename ParseHandler>
 inline
 AtomDecls<ParseHandler>::~AtomDecls()
 {
     if (map) {
         AutoLockForExclusiveAccess lock(cx);
-        cx->parseMapPool().release(map);
+        cx->parseMapPool(lock).release(map);
     }
 }
 
 } /* namespace frontend */
 } /* namespace js */
 
 #endif /* frontend_ParseMaps_inl_h */
--- a/js/src/frontend/ParseMaps.cpp
+++ b/js/src/frontend/ParseMaps.cpp
@@ -117,17 +117,17 @@ AtomDecls<ParseHandler>::addShadowedForA
     AtomDefnListAddPtr p = map->lookupForAdd(atom);
     if (!p)
         return map->add(p, atom, DefinitionList(ParseHandler::definitionToBits(defn)));
 
     return p.value().appendBack<ParseHandler>(cx, alloc, defn);
 }
 
 void
-frontend::InitAtomMap(frontend::AtomIndexMap* indices, HeapPtrAtom* atoms)
+frontend::InitAtomMap(frontend::AtomIndexMap* indices, GCPtrAtom* atoms)
 {
     if (indices->isMap()) {
         typedef AtomIndexMap::WordMap WordMap;
         const WordMap& wm = indices->asMap();
         for (WordMap::Range r = wm.all(); !r.empty(); r.popFront()) {
             JSAtom* atom = r.front().key();
             jsatomid index = r.front().value();
             MOZ_ASSERT(index < indices->count());
--- a/js/src/frontend/ParseMaps.h
+++ b/js/src/frontend/ParseMaps.h
@@ -32,17 +32,17 @@ typedef InlineMap<JSAtom*, DefinitionSin
 typedef InlineMap<JSAtom*, DefinitionList, 24> AtomDefnListMap;
 
 /*
  * For all unmapped atoms recorded in al, add a mapping from the atom's index
  * to its address. map->length must already be set to the number of atoms in
  * the list and map->vector must point to pre-allocated memory.
  */
 void
-InitAtomMap(AtomIndexMap* indices, HeapPtrAtom* atoms);
+InitAtomMap(AtomIndexMap* indices, GCPtrAtom* atoms);
 
 /*
  * A pool that permits the reuse of the backing storage for the defn, index, or
  * defn-or-header (multi) maps.
  *
  * The pool owns all the maps that are given out, and is responsible for
  * relinquishing all resources when |purgeAll| is triggered.
  */
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -696,17 +696,17 @@ Parser<ParseHandler>::Parser(ExclusiveCo
     checkOptionsCalled(false),
 #endif
     abortedSyntaxParse(false),
     isUnexpectedEOF_(false),
     handler(cx, *alloc, tokenStream, syntaxParser, lazyOuterFunction)
 {
     {
         AutoLockForExclusiveAccess lock(cx);
-        cx->perThreadData->addActiveCompilation();
+        cx->perThreadData->addActiveCompilation(lock);
     }
 
     // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings
     // which are not generated if functions are parsed lazily. Note that the
     // standard "use strict" does not inhibit lazy parsing.
     if (options.extraWarningsOption)
         handler.disableSyntaxParser();
 
@@ -737,17 +737,17 @@ Parser<ParseHandler>::~Parser()
      * The parser can allocate enormous amounts of memory for large functions.
      * Eagerly free the memory now (which otherwise won't be freed until the
      * next GC) to avoid unnecessary OOMs.
      */
     alloc.freeAllIfHugeAndUnused();
 
     {
         AutoLockForExclusiveAccess lock(context);
-        context->perThreadData->removeActiveCompilation();
+        context->perThreadData->removeActiveCompilation(lock);
     }
 }
 
 template <typename ParseHandler>
 ObjectBox*
 Parser<ParseHandler>::newObjectBox(JSObject* obj)
 {
     MOZ_ASSERT(obj);
@@ -2903,17 +2903,17 @@ Parser<SyntaxParseHandler>::finishFuncti
         return false;
 
     LazyScript::FreeVariable* freeVariables = lazy->freeVariables();
     size_t i = 0;
     for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront())
         freeVariables[i++] = LazyScript::FreeVariable(r.front().key());
     MOZ_ASSERT(i == numFreeVariables);
 
-    HeapPtrFunction* innerFunctions = lazy->innerFunctions();
+    GCPtrFunction* innerFunctions = lazy->innerFunctions();
     for (size_t i = 0; i < numInnerFunctions; i++)
         innerFunctions[i].init(pc->innerFunctions[i]);
 
     if (pc->sc->strict())
         lazy->setStrict();
     lazy->setGeneratorKind(funbox->generatorKind());
     if (funbox->isLikelyConstructorWrapper())
         lazy->setLikelyConstructorWrapper();
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -118,39 +118,38 @@
  *                            IMPLEMENTATION DETAILS
  *
  * Since it would be awkward to change every write to memory into a function
  * call, this file contains a bunch of C++ classes and templates that use
  * operator overloading to take care of barriers automatically. In many cases,
  * all that's necessary to make some field be barriered is to replace
  *     Type* field;
  * with
- *     HeapPtr<Type> field;
- * There are also special classes HeapValue and HeapId, which barrier js::Value
- * and jsid, respectively.
+ *     GCPtr<Type> field;
  *
  * One additional note: not all object writes need to be pre-barriered. Writes
  * to newly allocated objects do not need a pre-barrier. In these cases, we use
  * the "obj->field.init(value)" method instead of "obj->field = value". We use
  * the init naming idiom in many places to signify that a field is being
  * assigned for the first time.
  *
  * This file implements four classes, illustrated here:
  *
  * BarrieredBase             base class of all barriers
  *  |  |
  *  | WriteBarrieredBase     base class which provides common write operations
  *  |  |  |  |  |
  *  |  |  |  | PreBarriered  provides pre-barriers only
  *  |  |  |  |
- *  |  |  | HeapPtr          provides pre- and post-barriers
+ *  |  |  | GCPtr            provides pre- and post-barriers
  *  |  |  |
- *  |  | RelocatablePtr      provides pre- and post-barriers and is relocatable
+ *  |  | HeapPtr             provides pre- and post-barriers; is relocatable
+ *  |  |                     and deletable for use inside C++ managed memory
  *  |  |
- *  | HeapSlot               similar to HeapPtr, but tailored to slots storage
+ *  | HeapSlot               similar to GCPtr, but tailored to slots storage
  *  |
  * ReadBarrieredBase         base class which provides common read operations
  *  |
  * ReadBarriered             provides read barriers only
  *
  *
  * The implementation of the barrier logic is implemented on T::writeBarrier.*,
  * via:
@@ -158,17 +157,17 @@
  * WriteBarrieredBase<T>::pre
  *  -> InternalBarrierMethods<T*>::preBarrier
  *      -> T::writeBarrierPre
  *  -> InternalBarrierMethods<Value>::preBarrier
  *  -> InternalBarrierMethods<jsid>::preBarrier
  *      -> InternalBarrierMethods<T*>::preBarrier
  *          -> T::writeBarrierPre
  *
- * HeapPtr<T>::post and RelocatablePtr<T>::post
+ * GCPtr<T>::post and HeapPtr<T>::post
  *  -> InternalBarrierMethods<T*>::postBarrier
  *      -> T::writeBarrierPost
  *  -> InternalBarrierMethods<Value>::postBarrier
  *      -> StoreBuffer::put
  *
  * These classes are designed to be used by the internals of the JS engine.
  * Barriers designed to be used externally are provided in js/RootingAPI.h.
  * These external barriers call into the same post-barrier implementations at
@@ -197,16 +196,17 @@ class ArgumentsObject;
 class ArrayBufferObjectMaybeShared;
 class ArrayBufferObject;
 class ArrayBufferViewObject;
 class SharedArrayBufferObject;
 class BaseShape;
 class DebugScopeObject;
 class GlobalObject;
 class LazyScript;
+class ModuleObject;
 class ModuleEnvironmentObject;
 class ModuleNamespaceObject;
 class NativeObject;
 class NestedScopeObject;
 class PlainObject;
 class PropertyName;
 class SavedFrame;
 class ScopeObject;
@@ -365,28 +365,29 @@ class WriteBarrieredBase : public Barrie
     static void writeBarrierPre(const T& v) { InternalBarrierMethods<T>::preBarrier(v); }
 
   protected:
     void pre() { InternalBarrierMethods<T>::preBarrier(this->value); }
     void post(T prev, T next) { InternalBarrierMethods<T>::postBarrier(&this->value, prev, next); }
 };
 
 /*
- * PreBarriered only automatically handles pre-barriers. Post-barriers must
- * be manually implemented when using this class. HeapPtr and RelocatablePtr
- * should be used in all cases that do not require explicit low-level control
- * of moving behavior, e.g. for HashMap keys.
+ * PreBarriered only automatically handles pre-barriers. Post-barriers must be
+ * manually implemented when using this class. GCPtr and HeapPtr should be used
+ * in all cases that do not require explicit low-level control of moving
+ * behavior, e.g. for HashMap keys.
  */
 template <class T>
 class PreBarriered : public WriteBarrieredBase<T>
 {
   public:
     PreBarriered() : WriteBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
     /*
-     * Allow implicit construction for use in generic contexts, such as DebuggerWeakMap::markKeys.
+     * Allow implicit construction for use in generic contexts, such as
+     * DebuggerWeakMap::markKeys.
      */
     MOZ_IMPLICIT PreBarriered(T v) : WriteBarrieredBase<T>(v) {}
     explicit PreBarriered(const PreBarriered<T>& v) : WriteBarrieredBase<T>(v.value) {}
     ~PreBarriered() { this->pre(); }
 
     void init(T v) {
         this->value = v;
     }
@@ -404,136 +405,133 @@ class PreBarriered : public WriteBarrier
         this->pre();
         this->value = v;
     }
 };
 
 /*
  * A pre- and post-barriered heap pointer, for use inside the JS engine.
  *
- * It must only be stored in memory that has GC lifetime. HeapPtr must not be
+ * It must only be stored in memory that has GC lifetime. GCPtr must not be
  * used in contexts where it may be implicitly moved or deleted, e.g. most
  * containers.
  *
- * Not to be confused with JS::Heap<T>. This is a different class from the
- * external interface and implements substantially different semantics.
- *
  * The post-barriers implemented by this class are faster than those
- * implemented by RelocatablePtr<T> or JS::Heap<T> at the cost of not
+ * implemented by js::HeapPtr<T> or JS::Heap<T> at the cost of not
  * automatically handling deletion or movement.
  */
 template <class T>
-class HeapPtr : public WriteBarrieredBase<T>
+class GCPtr : public WriteBarrieredBase<T>
 {
   public:
-    HeapPtr() : WriteBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
-    explicit HeapPtr(T v) : WriteBarrieredBase<T>(v) {
+    GCPtr() : WriteBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
+    explicit GCPtr(T v) : WriteBarrieredBase<T>(v) {
         this->post(JS::GCPolicy<T>::initial(), v);
     }
-    explicit HeapPtr(const HeapPtr<T>& v) : WriteBarrieredBase<T>(v) {
+    explicit GCPtr(const GCPtr<T>& v) : WriteBarrieredBase<T>(v) {
         this->post(JS::GCPolicy<T>::initial(), v);
     }
 #ifdef DEBUG
-    ~HeapPtr() {
+    ~GCPtr() {
         // No prebarrier necessary as this only happens when we are sweeping or
         // before the containing object becomes part of the GC graph.
         MOZ_ASSERT(CurrentThreadIsGCSweeping() || CurrentThreadIsHandlingInitFailure());
     }
 #endif
 
     void init(T v) {
         this->value = v;
         this->post(JS::GCPolicy<T>::initial(), v);
     }
 
-    DECLARE_POINTER_ASSIGN_OPS(HeapPtr, T);
+    DECLARE_POINTER_ASSIGN_OPS(GCPtr, T);
 
     T unbarrieredGet() const {
         return this->value;
     }
 
   private:
     void set(const T& v) {
         this->pre();
         T tmp = this->value;
         this->value = v;
         this->post(tmp, this->value);
     }
 
     /*
-     * Unlike RelocatablePtr<T>, HeapPtr<T> must be managed with GC lifetimes.
+     * Unlike HeapPtr<T>, GCPtr<T> must be managed with GC lifetimes.
      * Specifically, the memory used by the pointer itself must be live until
      * at least the next minor GC. For that reason, move semantics are invalid
      * and are deleted here. Please note that not all containers support move
      * semantics, so this does not completely prevent invalid uses.
      */
-    HeapPtr(HeapPtr<T>&&) = delete;
-    HeapPtr<T>& operator=(HeapPtr<T>&&) = delete;
+    GCPtr(GCPtr<T>&&) = delete;
+    GCPtr<T>& operator=(GCPtr<T>&&) = delete;
 };
 
 /*
  * A pre- and post-barriered heap pointer, for use inside the JS engine. These
  * heap pointers can be stored in C++ containers like GCVector and GCHashMap.
  *
  * The GC sometimes keeps pointers to pointers to GC things --- for example, to
  * track references into the nursery. However, C++ containers like GCVector and
  * GCHashMap usually reserve the right to relocate their elements any time
- * they're modified, invalidating all pointers to the elements. RelocatablePtr
+ * they're modified, invalidating all pointers to the elements. HeapPtr
  * has a move constructor which knows how to keep the GC up to date if it is
  * moved to a new location.
  *
- * However, because of this additional communication with the GC, RelocatablePtr
+ * However, because of this additional communication with the GC, HeapPtr
  * is somewhat slower, so it should only be used in contexts where this ability
  * is necessary.
  *
  * Obviously, JSObjects, JSStrings, and the like get tenured and compacted, so
  * whatever pointers they contain get relocated, in the sense used here.
  * However, since the GC itself is moving those values, it takes care of its
- * internal pointers to those pointers itself. RelocatablePtr is only necessary
+ * internal pointers to those pointers itself. HeapPtr is only necessary
  * when the relocation would otherwise occur without the GC's knowledge.
  */
 template <class T>
-class RelocatablePtr : public WriteBarrieredBase<T>
+class HeapPtr : public WriteBarrieredBase<T>
 {
   public:
-    RelocatablePtr() : WriteBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
+    HeapPtr() : WriteBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
 
     // Implicitly adding barriers is a reasonable default.
-    MOZ_IMPLICIT RelocatablePtr(const T& v) : WriteBarrieredBase<T>(v) {
+    MOZ_IMPLICIT HeapPtr(const T& v) : WriteBarrieredBase<T>(v) {
         this->post(JS::GCPolicy<T>::initial(), this->value);
     }
 
     /*
-     * For RelocatablePtr, move semantics are equivalent to copy semantics. In
+     * For HeapPtr, move semantics are equivalent to copy semantics. In
      * C++, a copy constructor taking const-ref is the way to get a single
      * function that will be used for both lvalue and rvalue copies, so we can
      * simply omit the rvalue variant.
      */
-    MOZ_IMPLICIT RelocatablePtr(const RelocatablePtr<T>& v) : WriteBarrieredBase<T>(v) {
+    MOZ_IMPLICIT HeapPtr(const HeapPtr<T>& v) : WriteBarrieredBase<T>(v) {
         this->post(JS::GCPolicy<T>::initial(), this->value);
     }
 
-    ~RelocatablePtr() {
+    ~HeapPtr() {
         this->pre();
         this->post(this->value, JS::GCPolicy<T>::initial());
     }
 
     void init(T v) {
         this->value = v;
         this->post(JS::GCPolicy<T>::initial(), this->value);
     }
 
-    DECLARE_POINTER_ASSIGN_OPS(RelocatablePtr, T);
+    DECLARE_POINTER_ASSIGN_OPS(HeapPtr, T);
 
     /* Make this friend so it can access pre() and post(). */
     template <class T1, class T2>
     friend inline void
     BarrieredSetPair(Zone* zone,
-                     RelocatablePtr<T1*>& v1, T1* val1,
-                     RelocatablePtr<T2*>& v2, T2* val2);
+                     HeapPtr<T1*>& v1, T1* val1,
+                     HeapPtr<T2*>& v2, T2* val2);
 
   protected:
     void set(const T& v) {
         this->pre();
         postBarrieredSet(v);
     }
 
     void postBarrieredSet(const T& v) {
@@ -642,17 +640,17 @@ using WeakRef = ReadBarriered<T>;
 // Add Value operations to all Barrier types. Note, this must be defined before
 // HeapSlot for HeapSlot's base to get these operations.
 template <>
 class BarrieredBaseMixins<JS::Value> : public ValueOperations<WriteBarrieredBase<JS::Value>>
 {};
 
 // A pre- and post-barriered Value that is specialized to be aware that it
 // resides in a slots or elements vector. This allows it to be relocated in
-// memory, but with substantially less overhead than a RelocatablePtr.
+// memory, but with substantially less overhead than a HeapPtr.
 class HeapSlot : public WriteBarrieredBase<Value>
 {
   public:
     enum Kind {
         Slot = 0,
         Element = 1
     };
 
@@ -721,17 +719,17 @@ class HeapSlotArray
     explicit HeapSlotArray(HeapSlot* array, bool allowWrite)
       : array(array)
 #ifdef DEBUG
       , allowWrite_(allowWrite)
 #endif
     {}
 
     operator const Value*() const {
-        JS_STATIC_ASSERT(sizeof(HeapPtr<Value>) == sizeof(Value));
+        JS_STATIC_ASSERT(sizeof(GCPtr<Value>) == sizeof(Value));
         JS_STATIC_ASSERT(sizeof(HeapSlot) == sizeof(Value));
         return reinterpret_cast<const Value*>(array);
     }
     operator HeapSlot*() const { MOZ_ASSERT(allowWrite()); return array; }
 
     HeapSlotArray operator +(int offset) const { return HeapSlotArray(array + offset, allowWrite()); }
     HeapSlotArray operator +(uint32_t offset) const { return HeapSlotArray(array + offset, allowWrite()); }
 
@@ -747,18 +745,18 @@ class HeapSlotArray
 
 /*
  * This is a hack for RegExpStatics::updateFromMatch. It allows us to do two
  * barriers with only one branch to check if we're in an incremental GC.
  */
 template <class T1, class T2>
 static inline void
 BarrieredSetPair(Zone* zone,
-                 RelocatablePtr<T1*>& v1, T1* val1,
-                 RelocatablePtr<T2*>& v2, T2* val2)
+                 HeapPtr<T1*>& v1, T1* val1,
+                 HeapPtr<T2*>& v2, T2* val2)
 {
     if (T1::needWriteBarrierPre(zone)) {
         v1.pre();
         v2.pre();
     }
     v1.postBarrieredSet(val1);
     v2.postBarrieredSet(val2);
 }
@@ -804,19 +802,19 @@ struct MovableCellHasher<PreBarriered<T>
     using Lookup = T;
 
     static HashNumber hash(const Lookup& l) { return MovableCellHasher<T>::hash(l); }
     static bool match(const Key& k, const Lookup& l) { return MovableCellHasher<T>::match(k, l); }
     static void rekey(Key& k, const Key& newKey) { k.unsafeSet(newKey); }
 };
 
 template <typename T>
-struct MovableCellHasher<RelocatablePtr<T>>
+struct MovableCellHasher<HeapPtr<T>>
 {
-    using Key = RelocatablePtr<T>;
+    using Key = HeapPtr<T>;
     using Lookup = T;
 
     static HashNumber hash(const Lookup& l) { return MovableCellHasher<T>::hash(l); }
     static bool match(const Key& k, const Lookup& l) { return MovableCellHasher<T>::match(k, l); }
     static void rekey(Key& k, const Key& newKey) { k.unsafeSet(newKey); }
 };
 
 template <typename T>
@@ -827,31 +825,31 @@ struct MovableCellHasher<ReadBarriered<T
 
     static HashNumber hash(const Lookup& l) { return MovableCellHasher<T>::hash(l); }
     static bool match(const Key& k, const Lookup& l) {
         return MovableCellHasher<T>::match(k.unbarrieredGet(), l);
     }
     static void rekey(Key& k, const Key& newKey) { k.unsafeSet(newKey); }
 };
 
-/* Useful for hashtables with a HeapPtr as key. */
+/* Useful for hashtables with a GCPtr as key. */
 template <class T>
-struct HeapPtrHasher
+struct GCPtrHasher
 {
-    typedef HeapPtr<T> Key;
+    typedef GCPtr<T> Key;
     typedef T Lookup;
 
     static HashNumber hash(Lookup obj) { return DefaultHasher<T>::hash(obj); }
     static bool match(const Key& k, Lookup l) { return k.get() == l; }
     static void rekey(Key& k, const Key& newKey) { k.unsafeSet(newKey); }
 };
 
-/* Specialized hashing policy for HeapPtrs. */
+/* Specialized hashing policy for GCPtrs. */
 template <class T>
-struct DefaultHasher<HeapPtr<T>> : HeapPtrHasher<T> { };
+struct DefaultHasher<GCPtr<T>> : GCPtrHasher<T> {};
 
 template <class T>
 struct PreBarrieredHasher
 {
     typedef PreBarriered<T> Key;
     typedef T Lookup;
 
     static HashNumber hash(Lookup obj) { return DefaultHasher<T>::hash(obj); }
@@ -892,58 +890,43 @@ class JitCode;
 } // namespace jit
 
 typedef PreBarriered<JSObject*> PreBarrieredObject;
 typedef PreBarriered<JSScript*> PreBarrieredScript;
 typedef PreBarriered<jit::JitCode*> PreBarrieredJitCode;
 typedef PreBarriered<JSString*> PreBarrieredString;
 typedef PreBarriered<JSAtom*> PreBarrieredAtom;
 
-typedef RelocatablePtr<JSObject*> RelocatablePtrObject;
-typedef RelocatablePtr<JSFunction*> RelocatablePtrFunction;
-typedef RelocatablePtr<PlainObject*> RelocatablePtrPlainObject;
-typedef RelocatablePtr<JSScript*> RelocatablePtrScript;
-typedef RelocatablePtr<NativeObject*> RelocatablePtrNativeObject;
-typedef RelocatablePtr<NestedScopeObject*> RelocatablePtrNestedScopeObject;
-typedef RelocatablePtr<Shape*> RelocatablePtrShape;
-typedef RelocatablePtr<ObjectGroup*> RelocatablePtrObjectGroup;
-typedef RelocatablePtr<jit::JitCode*> RelocatablePtrJitCode;
-typedef RelocatablePtr<JSLinearString*> RelocatablePtrLinearString;
-typedef RelocatablePtr<JSString*> RelocatablePtrString;
-typedef RelocatablePtr<JSAtom*> RelocatablePtrAtom;
-typedef RelocatablePtr<ArrayBufferObjectMaybeShared*> RelocatablePtrArrayBufferObjectMaybeShared;
-
-typedef HeapPtr<NativeObject*> HeapPtrNativeObject;
-typedef HeapPtr<ArrayObject*> HeapPtrArrayObject;
-typedef HeapPtr<ArrayBufferObjectMaybeShared*> HeapPtrArrayBufferObjectMaybeShared;
-typedef HeapPtr<ArrayBufferObject*> HeapPtrArrayBufferObject;
-typedef HeapPtr<BaseShape*> HeapPtrBaseShape;
-typedef HeapPtr<JSAtom*> HeapPtrAtom;
-typedef HeapPtr<JSFlatString*> HeapPtrFlatString;
-typedef HeapPtr<JSFunction*> HeapPtrFunction;
-typedef HeapPtr<JSLinearString*> HeapPtrLinearString;
-typedef HeapPtr<JSObject*> HeapPtrObject;
-typedef HeapPtr<JSScript*> HeapPtrScript;
-typedef HeapPtr<JSString*> HeapPtrString;
-typedef HeapPtr<ModuleEnvironmentObject*> HeapPtrModuleEnvironmentObject;
-typedef HeapPtr<ModuleNamespaceObject*> HeapPtrModuleNamespaceObject;
-typedef HeapPtr<PlainObject*> HeapPtrPlainObject;
-typedef HeapPtr<PropertyName*> HeapPtrPropertyName;
-typedef HeapPtr<Shape*> HeapPtrShape;
-typedef HeapPtr<UnownedBaseShape*> HeapPtrUnownedBaseShape;
-typedef HeapPtr<jit::JitCode*> HeapPtrJitCode;
-typedef HeapPtr<ObjectGroup*> HeapPtrObjectGroup;
+typedef GCPtr<NativeObject*> GCPtrNativeObject;
+typedef GCPtr<ArrayObject*> GCPtrArrayObject;
+typedef GCPtr<ArrayBufferObjectMaybeShared*> GCPtrArrayBufferObjectMaybeShared;
+typedef GCPtr<ArrayBufferObject*> GCPtrArrayBufferObject;
+typedef GCPtr<BaseShape*> GCPtrBaseShape;
+typedef GCPtr<JSAtom*> GCPtrAtom;
+typedef GCPtr<JSFlatString*> GCPtrFlatString;
+typedef GCPtr<JSFunction*> GCPtrFunction;
+typedef GCPtr<JSLinearString*> GCPtrLinearString;
+typedef GCPtr<JSObject*> GCPtrObject;
+typedef GCPtr<JSScript*> GCPtrScript;
+typedef GCPtr<JSString*> GCPtrString;
+typedef GCPtr<ModuleObject*> GCPtrModuleObject;
+typedef GCPtr<ModuleEnvironmentObject*> GCPtrModuleEnvironmentObject;
+typedef GCPtr<ModuleNamespaceObject*> GCPtrModuleNamespaceObject;
+typedef GCPtr<PlainObject*> GCPtrPlainObject;
+typedef GCPtr<PropertyName*> GCPtrPropertyName;
+typedef GCPtr<Shape*> GCPtrShape;
+typedef GCPtr<UnownedBaseShape*> GCPtrUnownedBaseShape;
+typedef GCPtr<jit::JitCode*> GCPtrJitCode;
+typedef GCPtr<ObjectGroup*> GCPtrObjectGroup;
 
 typedef PreBarriered<Value> PreBarrieredValue;
-typedef RelocatablePtr<Value> RelocatableValue;
-typedef HeapPtr<Value> HeapValue;
+typedef GCPtr<Value> GCPtrValue;
 
 typedef PreBarriered<jsid> PreBarrieredId;
-typedef RelocatablePtr<jsid> RelocatableId;
-typedef HeapPtr<jsid> HeapId;
+typedef GCPtr<jsid> GCPtrId;
 
 typedef ImmutableTenuredPtr<PropertyName*> ImmutablePropertyNamePtr;
 typedef ImmutableTenuredPtr<JS::Symbol*> ImmutableSymbolPtr;
 
 typedef ReadBarriered<DebugScopeObject*> ReadBarrieredDebugScopeObject;
 typedef ReadBarriered<GlobalObject*> ReadBarrieredGlobalObject;
 typedef ReadBarriered<JSObject*> ReadBarrieredObject;
 typedef ReadBarriered<JSScript*> ReadBarrieredScript;
--- a/js/src/gc/FindSCCs.h
+++ b/js/src/gc/FindSCCs.h
@@ -2,16 +2,18 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef gc_FindSCCs_h
 #define gc_FindSCCs_h
 
+#include "mozilla/Move.h"
+
 #include "jsfriendapi.h"
 #include "jsutil.h"
 
 namespace js {
 namespace gc {
 
 template<class Node>
 struct GraphNodeBase
@@ -42,30 +44,38 @@ struct GraphNodeBase
 
 /*
  * Find the strongly connected components of a graph using Tarjan's algorithm,
  * and return them in topological order.
  *
  * Nodes derive from GraphNodeBase and implement findGraphEdges, which calls
  * finder.addEdgeTo to describe the outgoing edges from that node:
  *
+ * struct MyComponentFinder;
+ *
  * struct MyGraphNode : public GraphNodeBase
  * {
- *     void findOutgoingEdges(ComponentFinder<MyGraphNode>& finder)
+ *     void findOutgoingEdges(MyComponentFinder& finder)
  *     {
  *         for edge in my_outgoing_edges:
  *             if is_relevant(edge):
  *                 finder.addEdgeTo(edge.destination)
  *     }
  * }
  *
- * ComponentFinder<MyGraphNode> finder;
+ * struct MyComponentFinder : public ComponentFinder<MyGraphNode, MyComponentFinder>
+ * {
+ *     ...
+ * };
+ *
+ * MyComponentFinder finder;
  * finder.addNode(v);
  */
-template<class Node>
+
+template <typename Node, typename Derived>
 class ComponentFinder
 {
   public:
     explicit ComponentFinder(uintptr_t sl)
       : clock(1),
         stack(nullptr),
         firstComponent(nullptr),
         cur(nullptr),
@@ -151,17 +161,17 @@ class ComponentFinder
         int stackDummy;
         if (stackFull || !JS_CHECK_STACK_SIZE(stackLimit, &stackDummy)) {
             stackFull = true;
             return;
         }
 
         Node* old = cur;
         cur = v;
-        cur->findOutgoingEdges(*this);
+        cur->findOutgoingEdges(*static_cast<Derived*>(this));
         cur = old;
 
         if (stackFull)
             return;
 
         if (v->gcLowLink == v->gcDiscoveryTime) {
             Node* nextComponent = firstComponent;
             Node* w;
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -27,33 +27,36 @@ void FinishGC(JSRuntime* rt);
  * heap in order to trace through it...
  */
 class MOZ_RAII AutoTraceSession
 {
   public:
     explicit AutoTraceSession(JSRuntime* rt, JS::HeapState state = JS::HeapState::Tracing);
     ~AutoTraceSession();
 
+    AutoLockForExclusiveAccess lock;
+
   protected:
-    AutoLockForExclusiveAccess lock;
     JSRuntime* runtime;
 
   private:
     AutoTraceSession(const AutoTraceSession&) = delete;
     void operator=(const AutoTraceSession&) = delete;
 
     JS::HeapState prevState;
     AutoSPSEntry pseudoFrame;
 };
 
-struct MOZ_RAII AutoPrepareForTracing
+class MOZ_RAII AutoPrepareForTracing
 {
-    mozilla::Maybe<AutoTraceSession> session;
+    mozilla::Maybe<AutoTraceSession> session_;
 
+  public:
     AutoPrepareForTracing(JSRuntime* rt, ZoneSelector selector);
+    AutoTraceSession& session() { return session_.ref(); }
 };
 
 class IncrementalSafety
 {
     const char* reason_;
 
     explicit IncrementalSafety(const char* reason) : reason_(reason) {}
 
@@ -116,17 +119,17 @@ class MOZ_RAII AutoStopVerifyingBarriers
 struct MOZ_RAII AutoStopVerifyingBarriers
 {
     AutoStopVerifyingBarriers(JSRuntime*, bool) {}
 };
 #endif /* JS_GC_ZEAL */
 
 #ifdef JSGC_HASH_TABLE_CHECKS
 void CheckHashTablesAfterMovingGC(JSRuntime* rt);
-void CheckHeapAfterMovingGC(JSRuntime* rt);
+void CheckHeapAfterMovingGC(JSRuntime* rt, AutoLockForExclusiveAccess& lock);
 #endif
 
 struct MovingTracer : JS::CallbackTracer
 {
     explicit MovingTracer(JSRuntime* rt) : CallbackTracer(rt, TraceWeakMapKeysValues) {}
 
     void onObjectEdge(JSObject** objp) override;
     void onShapeEdge(Shape** shapep) override;
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -633,17 +633,18 @@ class GCRuntime
 
     void runDebugGC();
     inline void poke();
 
     enum TraceOrMarkRuntime {
         TraceRuntime,
         MarkRuntime
     };
-    void markRuntime(JSTracer* trc, TraceOrMarkRuntime traceOrMark = TraceRuntime);
+    void markRuntime(JSTracer* trc, TraceOrMarkRuntime traceOrMark,
+                     AutoLockForExclusiveAccess& lock);
 
     void notifyDidPaint();
     void shrinkBuffers();
     void onOutOfMallocMemory();
     void onOutOfMallocMemory(const AutoLockGC& lock);
 
 #ifdef JS_GC_ZEAL
     const void* addressOfZealModeBits() { return &zealModeBits; }
@@ -911,85 +912,87 @@ class GCRuntime
 
     friend class BackgroundAllocTask;
     friend class AutoMaybeStartBackgroundAllocation;
     inline bool wantBackgroundAllocation(const AutoLockGC& lock) const;
     void startBackgroundAllocTaskIfIdle();
 
     void requestMajorGC(JS::gcreason::Reason reason);
     SliceBudget defaultBudget(JS::gcreason::Reason reason, int64_t millis);
-    void budgetIncrementalGC(SliceBudget& budget);
-    void resetIncrementalGC(const char* reason);
+    void budgetIncrementalGC(SliceBudget& budget, AutoLockForExclusiveAccess& lock);
+    void resetIncrementalGC(const char* reason, AutoLockForExclusiveAccess& lock);
 
     // Assert if the system state is such that we should never
     // receive a request to do GC work.
     void checkCanCallAPI();
 
     // Check if the system state is such that GC has been supressed
     // or otherwise delayed.
     MOZ_MUST_USE bool checkIfGCAllowedInCurrentState(JS::gcreason::Reason reason);
 
     gcstats::ZoneGCStats scanZonesBeforeGC();
     void collect(bool nonincrementalByAPI, SliceBudget budget, JS::gcreason::Reason reason) JS_HAZ_GC_CALL;
     MOZ_MUST_USE bool gcCycle(bool nonincrementalByAPI, SliceBudget& budget,
                               JS::gcreason::Reason reason);
-    void incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason reason);
+    void incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason reason,
+                                 AutoLockForExclusiveAccess& lock);
 
     void pushZealSelectedObjects();
-    void purgeRuntime();
-    MOZ_MUST_USE bool beginMarkPhase(JS::gcreason::Reason reason);
+    void purgeRuntime(AutoLockForExclusiveAccess& lock);
+    MOZ_MUST_USE bool beginMarkPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAccess& lock);
     bool shouldPreserveJITCode(JSCompartment* comp, int64_t currentTime,
                                JS::gcreason::Reason reason);
     void bufferGrayRoots();
     void markCompartments();
     IncrementalProgress drainMarkStack(SliceBudget& sliceBudget, gcstats::Phase phase);
     template <class CompartmentIterT> void markWeakReferences(gcstats::Phase phase);
     void markWeakReferencesInCurrentGroup(gcstats::Phase phase);
     template <class ZoneIterT, class CompartmentIterT> void markGrayReferences(gcstats::Phase phase);
     void markBufferedGrayRoots(JS::Zone* zone);
     void markGrayReferencesInCurrentGroup(gcstats::Phase phase);
     void markAllWeakReferences(gcstats::Phase phase);
     void markAllGrayReferences(gcstats::Phase phase);
 
-    void beginSweepPhase(bool lastGC);
-    void findZoneGroups();
+    void beginSweepPhase(bool lastGC, AutoLockForExclusiveAccess& lock);
+    void findZoneGroups(AutoLockForExclusiveAccess& lock);
     MOZ_MUST_USE bool findZoneEdgesForWeakMaps();
     void getNextZoneGroup();
     void endMarkingZoneGroup();
-    void beginSweepingZoneGroup();
+    void beginSweepingZoneGroup(AutoLockForExclusiveAccess& lock);
     bool shouldReleaseObservedTypes();
     void endSweepingZoneGroup();
-    IncrementalProgress sweepPhase(SliceBudget& sliceBudget);
-    void endSweepPhase(bool lastGC);
+    IncrementalProgress sweepPhase(SliceBudget& sliceBudget, AutoLockForExclusiveAccess& lock);
+    void endSweepPhase(bool lastGC, AutoLockForExclusiveAccess& lock);
     void sweepZones(FreeOp* fop, bool lastGC);
     void decommitAllWithoutUnlocking(const AutoLockGC& lock);
     void decommitArenas(AutoLockGC& lock);
     void expireChunksAndArenas(bool shouldShrink, AutoLockGC& lock);
     void queueZonesForBackgroundSweep(ZoneList& zones);
     void sweepBackgroundThings(ZoneList& zones, LifoAlloc& freeBlocks, ThreadType threadType);
     void assertBackgroundSweepingFinished();
     bool shouldCompact();
     void beginCompactPhase();
-    IncrementalProgress compactPhase(JS::gcreason::Reason reason, SliceBudget& sliceBudget);
+    IncrementalProgress compactPhase(JS::gcreason::Reason reason, SliceBudget& sliceBudget,
+                                     AutoLockForExclusiveAccess& lock);
     void endCompactPhase(JS::gcreason::Reason reason);
     void sweepTypesAfterCompacting(Zone* zone);
     void sweepZoneAfterCompacting(Zone* zone);
     MOZ_MUST_USE bool relocateArenas(Zone* zone, JS::gcreason::Reason reason,
                                      Arena*& relocatedListOut, SliceBudget& sliceBudget);
     void updateTypeDescrObjects(MovingTracer* trc, Zone* zone);
     void updateCellPointers(MovingTracer* trc, Zone* zone, AllocKinds kinds, size_t bgTaskCount);
     void updateAllCellPointers(MovingTracer* trc, Zone* zone);
-    void updatePointersToRelocatedCells(Zone* zone);
+    void updatePointersToRelocatedCells(Zone* zone, AutoLockForExclusiveAccess& lock);
     void protectAndHoldArenas(Arena* arenaList);
     void unprotectHeldRelocatedArenas();
     void releaseRelocatedArenas(Arena* arenaList);
     void releaseRelocatedArenasWithoutUnlocking(Arena* arenaList, const AutoLockGC& lock);
     void finishCollection(JS::gcreason::Reason reason);
 
-    void computeNonIncrementalMarkingForValidation();
+    void computeNonIncrementalMarkingForValidation(AutoLockForExclusiveAccess& lock);
     void validateIncrementalMarking();
     void finishMarkingValidation();
 
 #ifdef DEBUG
     void checkForCompartmentMismatches();
 #endif
 
     void callFinalizeCallbacks(FreeOp* fop, JSFinalizeStatus status) const;
--- a/js/src/gc/Iteration.cpp
+++ b/js/src/gc/Iteration.cpp
@@ -21,17 +21,17 @@ void
 js::TraceRuntime(JSTracer* trc)
 {
     MOZ_ASSERT(!trc->isMarkingTracer());
 
     JSRuntime* rt = trc->runtime();
     rt->gc.evictNursery();
     AutoPrepareForTracing prep(rt, WithAtoms);
     gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_TRACE_HEAP);
-    rt->gc.markRuntime(trc);
+    rt->gc.markRuntime(trc, GCRuntime::TraceRuntime, prep.session().lock);
 }
 
 static void
 IterateCompartmentsArenasCells(JSRuntime* rt, Zone* zone, void* data,
                                JSIterateCompartmentCallback compartmentCallback,
                                IterateArenaCallback arenaCallback,
                                IterateCellCallback cellCallback)
 {
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -977,17 +977,17 @@ LazyScript::traceChildren(JSTracer* trc)
 
     // We rely on the fact that atoms are always tenured.
     FreeVariable* freeVariables = this->freeVariables();
     for (auto i : MakeRange(numFreeVariables())) {
         JSAtom* atom = freeVariables[i].atom();
         TraceManuallyBarrieredEdge(trc, &atom, "lazyScriptFreeVariable");
     }
 
-    HeapPtrFunction* innerFunctions = this->innerFunctions();
+    GCPtrFunction* innerFunctions = this->innerFunctions();
     for (auto i : MakeRange(numInnerFunctions()))
         TraceEdge(trc, &innerFunctions[i], "lazyScriptInnerFunction");
 }
 inline void
 js::GCMarker::eagerlyMarkChildren(LazyScript *thing)
 {
     if (thing->script_)
         noteWeakEdge(thing->script_.unsafeUnbarrieredForTracing());
@@ -1001,17 +1001,17 @@ js::GCMarker::eagerlyMarkChildren(LazySc
     if (thing->enclosingScope_)
         traverseEdge(thing, static_cast<JSObject*>(thing->enclosingScope_));
 
     // We rely on the fact that atoms are always tenured.
     LazyScript::FreeVariable* freeVariables = thing->freeVariables();
     for (auto i : MakeRange(thing->numFreeVariables()))
         traverseEdge(thing, static_cast<JSString*>(freeVariables[i].atom()));
 
-    HeapPtrFunction* innerFunctions = thing->innerFunctions();
+    GCPtrFunction* innerFunctions = thing->innerFunctions();
     for (auto i : MakeRange(thing->numInnerFunctions()))
         traverseEdge(thing, static_cast<JSObject*>(innerFunctions[i]));
 }
 
 void
 Shape::traceChildren(JSTracer* trc)
 {
     TraceEdge(trc, &base_, "base");
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -435,17 +435,17 @@ js::Nursery::collect(JSRuntime* rt, JS::
     sb.traceWholeCells(mover);
     TIME_END(traceWholeCells);
 
     TIME_START(traceGenericEntries);
     sb.traceGenericEntries(&mover);
     TIME_END(traceGenericEntries);
 
     TIME_START(markRuntime);
-    rt->gc.markRuntime(&mover);
+    rt->gc.markRuntime(&mover, GCRuntime::TraceRuntime, session.lock);
     TIME_END(markRuntime);
 
     TIME_START(markDebugger);
     {
         gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_MARK_ROOTS);
         Debugger::markAll(&mover);
     }
     TIME_END(markDebugger);
@@ -498,17 +498,17 @@ js::Nursery::collect(JSRuntime* rt, JS::
     if (rt->hasZealMode(ZealMode::CheckHashTablesOnMinorGC))
         CheckHashTablesAfterMovingGC(rt);
 #endif
     TIME_END(checkHashTables);
 
     TIME_START(checkHeap);
 #ifdef JS_GC_ZEAL
     if (rt->hasZealMode(ZealMode::CheckHeapOnMovingGC))
-        CheckHeapAfterMovingGC(rt);
+        CheckHeapAfterMovingGC(rt, session.lock);
 #endif
     TIME_END(checkHeap);
 
     // Resize the nursery.
     TIME_START(resize);
     double promotionRate = mover.tenuredSize / double(allocationEnd() - start());
     if (promotionRate > 0.05)
         growAllocableSpace();
--- a/js/src/gc/Policy.h
+++ b/js/src/gc/Policy.h
@@ -140,22 +140,22 @@ struct InternalGCPointerPolicy {
 namespace JS {
 
 #define DEFINE_INTERNAL_GC_POLICY(type) \
     template <> struct GCPolicy<type> : public js::InternalGCPointerPolicy<type> {};
 FOR_EACH_INTERNAL_GC_POINTER_TYPE(DEFINE_INTERNAL_GC_POLICY)
 #undef DEFINE_INTERNAL_GC_POLICY
 
 template <typename T>
-struct GCPolicy<js::RelocatablePtr<T>>
+struct GCPolicy<js::HeapPtr<T>>
 {
-    static void trace(JSTracer* trc, js::RelocatablePtr<T>* thingp, const char* name) {
+    static void trace(JSTracer* trc, js::HeapPtr<T>* thingp, const char* name) {
         js::TraceEdge(trc, thingp, name);
     }
-    static bool needsSweep(js::RelocatablePtr<T>* thingp) {
+    static bool needsSweep(js::HeapPtr<T>* thingp) {
         return js::gc::IsAboutToBeFinalized(thingp);
     }
 };
 
 template <typename T>
 struct GCPolicy<js::ReadBarriered<T>>
 {
     static void trace(JSTracer* trc, js::ReadBarriered<T>* thingp, const char* name) {
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -267,17 +267,18 @@ PropertyDescriptor::trace(JSTracer* trc)
     if ((attrs & JSPROP_SETTER) && setter) {
         JSObject* tmp = JS_FUNC_TO_DATA_PTR(JSObject*, setter);
         TraceRoot(trc, &tmp, "Descriptor::set");
         setter = JS_DATA_TO_FUNC_PTR(JSSetterOp, tmp);
     }
 }
 
 void
-js::gc::GCRuntime::markRuntime(JSTracer* trc, TraceOrMarkRuntime traceOrMark)
+js::gc::GCRuntime::markRuntime(JSTracer* trc, TraceOrMarkRuntime traceOrMark,
+                               AutoLockForExclusiveAccess& lock)
 {
     gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_ROOTS);
 
     MOZ_ASSERT(traceOrMark == TraceRuntime || traceOrMark == MarkRuntime);
 
     MOZ_ASSERT(!rt->mainThread.suppressGC);
 
     if (traceOrMark == MarkRuntime) {
@@ -301,21 +302,21 @@ js::gc::GCRuntime::markRuntime(JSTracer*
         }
 
         MarkPersistentRooted(rt, trc);
     }
 
     if (!rt->isBeingDestroyed() && !rt->isHeapMinorCollecting()) {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_MARK_RUNTIME_DATA);
 
-        if (traceOrMark == TraceRuntime || rt->atomsCompartment()->zone()->isCollecting()) {
+        if (traceOrMark == TraceRuntime || rt->atomsCompartment(lock)->zone()->isCollecting()) {
             MarkPermanentAtoms(trc);
-            MarkAtoms(trc);
+            MarkAtoms(trc, lock);
             MarkWellKnownSymbols(trc);
-            jit::JitRuntime::Mark(trc);
+            jit::JitRuntime::Mark(trc, lock);
         }
     }
 
     if (rt->isHeapMinorCollecting())
         jit::JitRuntime::MarkJitcodeGlobalTableUnconditionally(trc);
 
     for (ContextIter acx(rt); !acx.done(); acx.next())
         acx->mark(trc);
--- a/js/src/gc/StoreBuffer.h
+++ b/js/src/gc/StoreBuffer.h
@@ -59,17 +59,17 @@ class StoreBuffer
     struct MonoTypeBuffer
     {
         /* The canonical set of stores. */
         typedef HashSet<T, typename T::Hasher, SystemAllocPolicy> StoreSet;
         StoreSet stores_;
 
         /*
          * A one element cache in front of the canonical set to speed up
-         * temporary instances of RelocatablePtr.
+         * temporary instances of HeapPtr.
          */
         T last_;
 
         /* Maximum number of entries before we request a minor GC. */
         const static size_t MaxEntries = 48 * 1024 / sizeof(T);
 
         explicit MonoTypeBuffer() : last_(T()) {}
         ~MonoTypeBuffer() { stores_.finish(); }
@@ -343,19 +343,16 @@ class StoreBuffer
             MOZ_ASSERT(edge->isTenured());
         }
 
         bool operator==(const WholeCellEdges& other) const { return edge == other.edge; }
         bool operator!=(const WholeCellEdges& other) const { return edge != other.edge; }
 
         bool maybeInRememberedSet(const Nursery&) const { return true; }
 
-        static bool supportsDeduplication() { return true; }
-        void* deduplicationKey() const { return (void*)edge; }
-
         void trace(TenuringTracer& mover) const;
 
         explicit operator bool() const { return edge != nullptr; }
 
         typedef PointerEdgeHasher<WholeCellEdges> Hasher;
     };
 
     template <typename Buffer, typename Edge>
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -201,17 +201,17 @@ gc::GCRuntime::startVerifyPreBarriers()
         goto oom;
 
     /* Create the root node. */
     trc->curnode = MakeNode(trc, nullptr, JS::TraceKind(0));
 
     incrementalState = MARK_ROOTS;
 
     /* Make all the roots be edges emanating from the root node. */
-    markRuntime(trc);
+    markRuntime(trc, TraceRuntime, prep.session().lock);
 
     VerifyNode* node;
     node = trc->curnode;
     if (trc->edgeptr == trc->term)
         goto oom;
 
     /* For each edge, make a node for it if one doesn't already exist. */
     while ((char*)node < trc->edgeptr) {
@@ -416,17 +416,17 @@ js::gc::GCRuntime::finishVerifier()
 
 #ifdef JSGC_HASH_TABLE_CHECKS
 
 class CheckHeapTracer : public JS::CallbackTracer
 {
   public:
     explicit CheckHeapTracer(JSRuntime* rt);
     bool init();
-    bool check();
+    bool check(AutoLockForExclusiveAccess& lock);
 
   private:
     void onChild(const JS::GCCellPtr& thing) override;
 
     struct WorkItem {
         WorkItem(JS::GCCellPtr thing, const char* name, int parentIndex)
           : thing(thing), name(name), parentIndex(parentIndex), processed(false)
         {}
@@ -491,21 +491,21 @@ CheckHeapTracer::onChild(const JS::GCCel
     }
 
     WorkItem item(thing, contextName(), parentIndex);
     if (!stack.append(item))
         oom = true;
 }
 
 bool
-CheckHeapTracer::check()
+CheckHeapTracer::check(AutoLockForExclusiveAccess& lock)
 {
     // The analysis thinks that markRuntime might GC by calling a GC callback.
     JS::AutoSuppressGCAnalysis nogc(rt);
-    rt->gc.markRuntime(this, GCRuntime::TraceRuntime);
+    rt->gc.markRuntime(this, GCRuntime::TraceRuntime, lock);
 
     while (!stack.empty()) {
         WorkItem item = stack.back();
         if (item.processed) {
             stack.popBack();
         } else {
             parentIndex = stack.length() - 1;
             TraceChildren(this, item.thing);
@@ -521,17 +521,17 @@ CheckHeapTracer::check()
                 failures, visited.count());
     }
     MOZ_RELEASE_ASSERT(failures == 0);
 
     return true;
 }
 
 void
-js::gc::CheckHeapAfterMovingGC(JSRuntime* rt)
+js::gc::CheckHeapAfterMovingGC(JSRuntime* rt, AutoLockForExclusiveAccess& lock)
 {
     MOZ_ASSERT(rt->isHeapCollecting());
     CheckHeapTracer tracer(rt);
-    if (!tracer.init() || !tracer.check())
+    if (!tracer.init() || !tracer.check(lock))
         fprintf(stderr, "OOM checking heap\n");
 }
 
 #endif /* JSGC_HASH_TABLE_CHECKS */
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -163,17 +163,17 @@ Zone::sweepBreakpoints(FreeOp* fop)
         for (unsigned i = 0; i < script->length(); i++) {
             BreakpointSite* site = script->getBreakpointSite(script->offsetToPC(i));
             if (!site)
                 continue;
 
             Breakpoint* nextbp;
             for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = nextbp) {
                 nextbp = bp->nextInSite();
-                HeapPtrNativeObject& dbgobj = bp->debugger->toJSObjectRef();
+                GCPtrNativeObject& dbgobj = bp->debugger->toJSObjectRef();
 
                 // If we are sweeping, then we expect the script and the
                 // debugger object to be swept in the same zone group, except if
                 // the breakpoint was added after we computed the zone
                 // groups. In this case both script and debugger object must be
                 // live.
                 MOZ_ASSERT_IF(isGCSweeping() && dbgobj->zone()->isCollecting(),
                               dbgobj->zone()->isGCSweeping() ||
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -57,16 +57,25 @@ class ZoneHeapThreshold
                                                          const GCSchedulingTunables& tunables,
                                                          const GCSchedulingState& state);
     static size_t computeZoneTriggerBytes(double growthFactor, size_t lastBytes,
                                           JSGCInvocationKind gckind,
                                           const GCSchedulingTunables& tunables,
                                           const AutoLockGC& lock);
 };
 
+struct ZoneComponentFinder : public ComponentFinder<JS::Zone, ZoneComponentFinder>
+{
+    ZoneComponentFinder(uintptr_t sl, AutoLockForExclusiveAccess& lock)
+      : ComponentFinder<JS::Zone, ZoneComponentFinder>(sl), lock(lock)
+    {}
+
+    AutoLockForExclusiveAccess& lock;
+};
+
 struct UniqueIdGCPolicy {
     static bool needsSweep(Cell** cell, uint64_t* value);
 };
 
 // Maps a Cell* to a unique, 64bit id.
 using UniqueIdMap = GCHashMap<Cell*,
                               uint64_t,
                               PointerHasher<Cell*, 3>,
@@ -124,17 +133,17 @@ namespace JS {
 struct Zone : public JS::shadow::Zone,
               public js::gc::GraphNodeBase<JS::Zone>,
               public js::MallocProvider<JS::Zone>
 {
     explicit Zone(JSRuntime* rt);
     ~Zone();
     MOZ_MUST_USE bool init(bool isSystem);
 
-    void findOutgoingEdges(js::gc::ComponentFinder<JS::Zone>& finder);
+    void findOutgoingEdges(js::gc::ZoneComponentFinder& finder);
 
     void discardJitCode(js::FreeOp* fop);
 
     void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
                                 size_t* typePool,
                                 size_t* baselineStubsOptimized,
                                 size_t* uniqueIdMap);
 
@@ -325,18 +334,18 @@ struct Zone : public JS::shadow::Zone,
     //
     // This is used during GC while calculating zone groups to record edges that
     // can't be determined by examining this zone by itself.
     ZoneSet gcZoneGroupEdges;
 
     // Keep track of all TypeDescr and related objects in this compartment.
     // This is used by the GC to trace them all first when compacting, since the
     // TypedObject trace hook may access these objects.
-    using TypeDescrObjectSet = js::GCHashSet<js::RelocatablePtrObject,
-                                             js::MovableCellHasher<js::RelocatablePtrObject>,
+    using TypeDescrObjectSet = js::GCHashSet<js::HeapPtr<JSObject*>,
+                                             js::MovableCellHasher<js::HeapPtr<JSObject*>>,
                                              js::SystemAllocPolicy>;
     JS::WeakCache<TypeDescrObjectSet> typeDescrObjects;
 
 
     // Malloc counter to measure memory pressure for GC scheduling. It runs from
     // gcMaxMallocBytes down to zero. This counter should be used only when it's
     // not possible to know the size of a free.
     mozilla::Atomic<ptrdiff_t, mozilla::ReleaseAcquire> gcMallocBytes;
--- a/js/src/gdb/tests/test-Root.cpp
+++ b/js/src/gdb/tests/test-Root.cpp
@@ -35,23 +35,23 @@ FRAGMENT(Root, HeapSlot) {
 
   (void) plinth;
   (void) array;
 }
 
 FRAGMENT(Root, barriers) {
   JSObject* obj = JS_NewPlainObject(cx);
   js::PreBarriered<JSObject*> prebarriered(obj);
-  js::HeapPtr<JSObject*> heapptr(obj);
-  js::RelocatablePtr<JSObject*> relocatable(obj);
+  js::GCPtrObject heapptr(obj);
+  js::HeapPtr<JSObject*> relocatable(obj);
 
   JS::Value val = JS::ObjectValue(*obj);
   js::PreBarrieredValue prebarrieredValue(JS::ObjectValue(*obj));
-  js::HeapValue heapValue(JS::ObjectValue(*obj));
-  js::RelocatableValue relocatableValue(JS::ObjectValue(*obj));
+  js::GCPtrValue heapValue(JS::ObjectValue(*obj));
+  js::HeapPtr<JS::Value> relocatableValue(JS::ObjectValue(*obj));
 
   breakpoint();
 
   (void) prebarriered;
   (void) heapptr;
   (void) relocatable;
   (void) val;
   (void) prebarrieredValue;
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/eval-introduction-principals.js
@@ -0,0 +1,16 @@
+// Ensure the introduction info for eval scripts respects principal checks.
+
+function myAPI(f) { return f(); }
+
+var contentGlobal = newGlobal({principal: 0x1});
+contentGlobal.chrome = this;
+contentGlobal.eval("\n" +
+		   "function contentTest() { chrome.myAPI(eval.bind(undefined, 'chrome.stack = Error().stack;')) };\n" +
+		   "contentTest();");
+
+// Note that the stack below does not include the current filename or file
+// line numbers, and there's no trace of the myAPI call between the two
+// evals.
+assertEq(stack, "@eval line 2 > eval:1:16\n" +
+                "contentTest@eval:2:26\n" +
+                "@eval:3:1\n");
--- a/js/src/jit-test/tests/wasm/totext1.js
+++ b/js/src/jit-test/tests/wasm/totext1.js
@@ -145,8 +145,29 @@ runTest(`
   (func $itrunc_u_f64 (param $x f64) (result i32) (i32.trunc_u/f64 (get_local $x)))
   (func $fconvert_s_i32 (param $x i32) (result f32) (f32.convert_s/i32 (get_local $x)))
   (func $dconvert_s_i32 (param $x i32) (result f64) (f64.convert_s/i32 (get_local $x)))
   (func $fconvert_u_i32 (param $x i32) (result f32) (f32.convert_u/i32 (get_local $x)))
   (func $dconvert_u_i32 (param $x i32) (result f64) (f64.convert_u/i32 (get_local $x)))
   (func $dpromote_f32 (param $x f32) (result f64) (f64.promote/f32 (get_local $x)))
   (func $fdemote_f64 (param $x f64) (result f32) (f32.demote/f64 (get_local $x)))
 (memory 0))`);
+
+// function calls
+runTest(`
+(module
+  (type $type1 (func (param i32) (result i32)))
+  (import $import1 "mod" "test" (param f32) (result f32))
+  (table $func1 $func2)
+  (func $func1 (param i32) (param f32) (nop))
+  (func $func2 (param i32) (result i32) (get_local 0))
+  (func $test
+    (call $func1
+      (call_indirect $type1 (i32.const 1) (i32.const 2))
+      (call_import $import1 (f32.const 1.0))
+    )
+  )
+  (export "test" $test)
+  (memory 1)
+)`);
+
+// default memory export from binaryen
+runTest(`(module (func (nop)) (memory 0 65535))`);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/totext2.js
@@ -0,0 +1,72 @@
+if (!wasmIsSupported())
+     quit();
+
+load(libdir + "asserts.js");
+
+function runTest(code, expected) {
+  var binary = wasmTextToBinary(code);
+  var s = wasmBinaryToText(binary, "experimental");
+  s = s.replace(/\s+/g, ' ');
+  print("TEXT: " + s);
+  assertEq(expected, s);
+}
+
+// Smoke test
+runTest(`
+(module
+  (func (param i32) (result f64)
+     (local $l f32)
+     (block
+        (set_local $l (f32.const 0.0))
+        (loop $exit $cont
+           (br_if $exit (get_local 0))
+           (br 2)
+        )
+        (if (i32.const 1)
+           (f64.min (f64.neg (f64.const 1)) (f64.const 0))
+           (f64.add (f64.const 0.5) (f64.load offset=0 (i32.const 0)) )
+        )
+     )
+     (i32.store16 (i32.const 8) (i32.const 128))
+
+     (return (f64.const 0))
+  )
+  (export "test" 0)
+  (memory 1 10)
+)`,
+"type $type$0 of function (i32) : (f64) " +
+"export $func$0 as \"test\" " +
+"function $func$0($var$0:i32) : (f64) {" +
+" var $var$1:f32 { $var$1 = 0.0f loop { br_if $var$0,$label$0 br $label$1 $label$0: }" +
+" if (1) { f64.min -1.0 0.0 } else { 0.5 + f64.load [0] } $label$1: }" +
+" i32.store16 [8],128 return 0.0 "+
+"} memory 1,10 {} ");
+
+// function calls
+runTest(`
+(module
+  (type $type1 (func (param i32) (result i32)))
+  (import $import1 "mod" "test" (param f32) (result f32))
+  (table $func1 $func2)
+  (func $func1 (param i32) (param f32) (nop))
+  (func $func2 (param i32) (result i32) (get_local 0))
+  (func $test
+    (call $func1
+      (call_indirect $type1 (i32.const 1) (i32.const 2))
+      (call_import $import1 (f32.const 1.0))
+    )
+  )
+  (export "test" $test)
+  (memory 1 65535)
+)`,
+"type $type$0 of function (i32) : (i32) " +
+"type $type$1 of function (f32) : (f32) " +
+"type $type$2 of function (i32,f32) : () " +
+"type $type$3 of function () : () " +
+"import \"test\" as $import$0 from \"mod\" typeof function (f32) : (f32) " +
+"table [$func$0,$func$1] export $func$2 as \"test\" " +
+"function $func$0($var$0:i32,$var$1:f32) : () { nop } " +
+"function $func$1($var$0:i32) : (i32) { $var$0 } " +
+"function $func$2() : () {" +
+" call $func$0 (call_indirect $type$0 [1] (2),call_import $import$0 (1.0f)) " +
+"} memory 1,65535 {} ");
--- a/js/src/jit/BaselineCacheIR.cpp
+++ b/js/src/jit/BaselineCacheIR.cpp
@@ -1054,61 +1054,61 @@ BaselineCacheIRCompiler::init(CacheKind 
 
     MOZ_ASSERT(numInputs == 1);
     allocator.initInputLocation(0, R0);
 
     return true;
 }
 
 template <typename T>
-static HeapPtr<T>*
-AsHeapPtr(uintptr_t* ptr)
+static GCPtr<T>*
+AsGCPtr(uintptr_t* ptr)
 {
-    return reinterpret_cast<HeapPtr<T>*>(ptr);
+    return reinterpret_cast<GCPtr<T>*>(ptr);
 }
 
 template<class T>
-HeapPtr<T>&
+GCPtr<T>&
 CacheIRStubInfo::getStubField(ICStub* stub, uint32_t field) const
 {
     uint8_t* stubData = (uint8_t*)stub + stubDataOffset_;
     MOZ_ASSERT(uintptr_t(stubData) % sizeof(uintptr_t) == 0);
 
-    return *AsHeapPtr<T>((uintptr_t*)stubData + field);
+    return *AsGCPtr<T>((uintptr_t*)stubData + field);
 }
 
-template HeapPtr<Shape*>& CacheIRStubInfo::getStubField(ICStub* stub, uint32_t offset) const;
-template HeapPtr<ObjectGroup*>& CacheIRStubInfo::getStubField(ICStub* stub, uint32_t offset) const;
-template HeapPtr<JSObject*>& CacheIRStubInfo::getStubField(ICStub* stub, uint32_t offset) const;
+template GCPtr<Shape*>& CacheIRStubInfo::getStubField(ICStub* stub, uint32_t offset) const;
+template GCPtr<ObjectGroup*>& CacheIRStubInfo::getStubField(ICStub* stub, uint32_t offset) const;
+template GCPtr<JSObject*>& CacheIRStubInfo::getStubField(ICStub* stub, uint32_t offset) const;
 
 template <typename T>
 static void
-InitHeapPtr(uintptr_t* ptr, uintptr_t val)
+InitGCPtr(uintptr_t* ptr, uintptr_t val)
 {
-    AsHeapPtr<T*>(ptr)->init((T*)val);
+    AsGCPtr<T*>(ptr)->init((T*)val);
 }
 
 void
 CacheIRWriter::copyStubData(uint8_t* dest) const
 {
     uintptr_t* destWords = reinterpret_cast<uintptr_t*>(dest);
 
     for (size_t i = 0; i < stubFields_.length(); i++) {
         switch (stubFields_[i].gcType) {
           case StubField::GCType::NoGCThing:
             destWords[i] = stubFields_[i].word;
             continue;
           case StubField::GCType::Shape:
-            InitHeapPtr<Shape>(destWords + i, stubFields_[i].word);
+            InitGCPtr<Shape>(destWords + i, stubFields_[i].word);
             continue;
           case StubField::GCType::JSObject:
-            InitHeapPtr<JSObject>(destWords + i, stubFields_[i].word);
+            InitGCPtr<JSObject>(destWords + i, stubFields_[i].word);
             continue;
           case StubField::GCType::ObjectGroup:
-            InitHeapPtr<ObjectGroup>(destWords + i, stubFields_[i].word);
+            InitGCPtr<ObjectGroup>(destWords + i, stubFields_[i].word);
             continue;
           case StubField::GCType::Limit:
             break;
         }
         MOZ_CRASH();
     }
 }
 
--- a/js/src/jit/BaselineCacheIR.h
+++ b/js/src/jit/BaselineCacheIR.h
@@ -48,17 +48,17 @@ class CacheIRStubInfo
     uint32_t stubDataOffset() const { return stubDataOffset_; }
 
     StubField::GCType gcType(uint32_t i) const { return (StubField::GCType)gcTypes_[i]; }
 
     static CacheIRStubInfo* New(CacheKind kind, uint32_t stubDataOffset,
                                 const CacheIRWriter& writer);
 
     template <class T>
-    js::HeapPtr<T>& getStubField(ICStub* stub, uint32_t field) const;
+    js::GCPtr<T>& getStubField(ICStub* stub, uint32_t field) const;
 };
 
 void TraceBaselineCacheIRStub(JSTracer* trc, ICStub* stub, const CacheIRStubInfo* stubInfo);
 
 ICStub* AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer, CacheKind kind,
                                   ICFallbackStub* stub);
 
 } // namespace jit
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -1047,17 +1047,17 @@ BaselineFrame::deleteDebugModeOSRInfo()
     flags_ &= ~HAS_DEBUG_MODE_OSR_INFO;
 }
 
 JitCode*
 JitRuntime::getBaselineDebugModeOSRHandler(JSContext* cx)
 {
     if (!baselineDebugModeOSRHandler_) {
         AutoLockForExclusiveAccess lock(cx);
-        AutoCompartment ac(cx, cx->runtime()->atomsCompartment());
+        AutoCompartment ac(cx, cx->runtime()->atomsCompartment(lock));
         uint32_t offset;
         if (JitCode* code = generateBaselineDebugModeOSRHandler(cx, &offset)) {
             baselineDebugModeOSRHandler_ = code;
             baselineDebugModeOSRHandlerNoFrameRegPopAddr_ = code->raw() + offset;
         }
     }
 
     return baselineDebugModeOSRHandler_;
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -10,16 +10,17 @@
 #include "mozilla/Assertions.h"
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsgc.h"
 #include "jsopcode.h"
 
 #include "builtin/TypedObject.h"
+#include "gc/Barrier.h"
 #include "jit/BaselineICList.h"
 #include "jit/BaselineJIT.h"
 #include "jit/SharedIC.h"
 #include "jit/SharedICRegisters.h"
 #include "js/GCVector.h"
 #include "vm/ArrayObject.h"
 #include "vm/UnboxedObject.h"
 
@@ -120,22 +121,22 @@ class ICTypeUpdate_PrimitiveSet : public
     };
 };
 
 // Type update stub to handle a singleton object.
 class ICTypeUpdate_SingleObject : public ICStub
 {
     friend class ICStubSpace;
 
-    HeapPtrObject obj_;
+    GCPtrObject obj_;
 
     ICTypeUpdate_SingleObject(JitCode* stubCode, JSObject* obj);
 
   public:
-    HeapPtrObject& object() {
+    GCPtrObject& object() {
         return obj_;
     }
 
     static size_t offsetOfObject() {
         return offsetof(ICTypeUpdate_SingleObject, obj_);
     }
 
     class Compiler : public ICStubCompiler {
@@ -155,22 +156,22 @@ class ICTypeUpdate_SingleObject : public
     };
 };
 
 // Type update stub to handle a single ObjectGroup.
 class ICTypeUpdate_ObjectGroup : public ICStub
 {
     friend class ICStubSpace;
 
-    HeapPtrObjectGroup group_;
+    GCPtrObjectGroup group_;
 
     ICTypeUpdate_ObjectGroup(JitCode* stubCode, ObjectGroup* group);
 
   public:
-    HeapPtrObjectGroup& group() {
+    GCPtrObjectGroup& group() {
         return group_;
     }
 
     static size_t offsetOfGroup() {
         return offsetof(ICTypeUpdate_ObjectGroup, group_);
     }
 
     class Compiler : public ICStubCompiler {
@@ -462,27 +463,27 @@ class ICGetElemNativeStub : public ICMon
         return (extra_ >> ISSYMBOL_SHIFT) & ISSYMBOL_MASK;
     }
 };
 
 template <class T>
 class ICGetElemNativeStubImpl : public ICGetElemNativeStub
 {
   protected:
-    HeapPtr<T> key_;
+    GCPtr<T> key_;
 
     ICGetElemNativeStubImpl(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
                             ReceiverGuard guard, const T* key, AccessType acctype, bool needsAtomize)
       : ICGetElemNativeStub(kind, stubCode, firstMonitorStub, guard, acctype, needsAtomize,
                             mozilla::IsSame<T, JS::Symbol*>::value),
         key_(*key)
     {}
 
   public:
-    HeapPtr<T>& key() {
+    GCPtr<T>& key() {
         return key_;
     }
     static size_t offsetOfKey() {
         return offsetof(ICGetElemNativeStubImpl, key_);
     }
 };
 
 typedef ICGetElemNativeStub::AccessType AccType;
@@ -518,25 +519,25 @@ class ICGetElemNativeSlotStub : public I
         return offsetof(ICGetElemNativeSlotStub, offset_);
     }
 };
 
 template <class T>
 class ICGetElemNativeGetterStub : public ICGetElemNativeStubImpl<T>
 {
   protected:
-    HeapPtrFunction getter_;
+    GCPtrFunction getter_;
     uint32_t pcOffset_;
 
     ICGetElemNativeGetterStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
                               ReceiverGuard guard, const T* key, AccType acctype, bool needsAtomize,
                               JSFunction* getter, uint32_t pcOffset);
 
   public:
-    HeapPtrFunction& getter() {
+    GCPtrFunction& getter() {
         return getter_;
     }
     static size_t offsetOfGetter() {
         return offsetof(ICGetElemNativeGetterStub, getter_);
     }
 
     static size_t offsetOfPCOffset() {
         return offsetof(ICGetElemNativeGetterStub, pcOffset_);
@@ -588,32 +589,32 @@ class ICGetElem_UnboxedProperty : public
 class ICGetElem_UnboxedPropertyName :
       public ICGetElem_UnboxedProperty<PropertyName*>
 {};
 
 template <class T>
 class ICGetElem_NativePrototypeSlot : public ICGetElemNativeSlotStub<T>
 {
     friend class ICStubSpace;
-    HeapPtrObject holder_;
-    HeapPtrShape holderShape_;
+    GCPtrObject holder_;
+    GCPtrShape holderShape_;
 
     ICGetElem_NativePrototypeSlot(JitCode* stubCode, ICStub* firstMonitorStub, ReceiverGuard guard,
                                   const T* key, AccType acctype, bool needsAtomize, uint32_t offset,
                                   JSObject* holder, Shape* holderShape);
 
   public:
-    HeapPtrObject& holder() {
+    GCPtrObject& holder() {
         return holder_;
     }
     static size_t offsetOfHolder() {
         return offsetof(ICGetElem_NativePrototypeSlot, holder_);
     }
 
-    HeapPtrShape& holderShape() {
+    GCPtrShape& holderShape() {
         return holderShape_;
     }
     static size_t offsetOfHolderShape() {
         return offsetof(ICGetElem_NativePrototypeSlot, holderShape_);
     }
 };
 
 class ICGetElem_NativePrototypeSlotName :
@@ -622,34 +623,34 @@ class ICGetElem_NativePrototypeSlotName 
 class ICGetElem_NativePrototypeSlotSymbol :
       public ICGetElem_NativePrototypeSlot<JS::Symbol*>
 {};
 
 template <class T>
 class ICGetElemNativePrototypeCallStub : public ICGetElemNativeGetterStub<T>
 {
     friend class ICStubSpace;
-    HeapPtrObject holder_;
-    HeapPtrShape holderShape_;
+    GCPtrObject holder_;
+    GCPtrShape holderShape_;
 
   protected:
     ICGetElemNativePrototypeCallStub(ICStub::Kind kind, JitCode* stubCode, ICStub* firstMonitorStub,
                                      ReceiverGuard guard, const T* key, AccType acctype,
                                      bool needsAtomize, JSFunction* getter, uint32_t pcOffset,
                                      JSObject* holder, Shape* holderShape);
 
   public:
-    HeapPtrObject& holder() {
+    GCPtrObject& holder() {
         return holder_;
     }
     static size_t offsetOfHolder() {
         return offsetof(ICGetElemNativePrototypeCallStub, holder_);
     }
 
-    HeapPtrShape& holderShape() {
+    GCPtrShape& holderShape() {
         return holderShape_;
     }
     static size_t offsetOfHolderShape() {
         return offsetof(ICGetElemNativePrototypeCallStub, holderShape_);
     }
 };
 
 template <class T>
@@ -845,29 +846,29 @@ class ICGetElem_String : public ICStub
         }
     };
 };
 
 class ICGetElem_Dense : public ICMonitoredStub
 {
     friend class ICStubSpace;
 
-    HeapPtrShape shape_;
+    GCPtrShape shape_;
 
     ICGetElem_Dense(JitCode* stubCode, ICStub* firstMonitorStub, Shape* shape);
 
   public:
     static ICGetElem_Dense* Clone(JSContext* cx, ICStubSpace* space, ICStub* firstMonitorStub,
                                   ICGetElem_Dense& other);
 
     static size_t offsetOfShape() {
         return offsetof(ICGetElem_Dense, shape_);
     }
 
-    HeapPtrShape& shape() {
+    GCPtrShape& shape() {
         return shape_;
     }
 
     class Compiler : public ICStubCompiler {
       ICStub* firstMonitorStub_;
       RootedShape shape_;
 
       protected:
@@ -890,29 +891,29 @@ class ICGetElem_Dense : public ICMonitor
         }
     };
 };
 
 class ICGetElem_UnboxedArray : public ICMonitoredStub
 {
     friend class ICStubSpace;
 
-    HeapPtrObjectGroup group_;
+    GCPtrObjectGroup group_;
 
     ICGetElem_UnboxedArray(JitCode* stubCode, ICStub* firstMonitorStub, ObjectGroup* group);
 
   public:
     static ICGetElem_UnboxedArray* Clone(JSContext* cx, ICStubSpace* space,
                                          ICStub* firstMonitorStub, ICGetElem_UnboxedArray& other);
 
     static size_t offsetOfGroup() {
         return offsetof(ICGetElem_UnboxedArray, group_);
     }
 
-    HeapPtrObjectGroup& group() {
+    GCPtrObjectGroup& group() {
         return group_;
     }
 
     class Compiler : public ICStubCompiler {
       ICStub* firstMonitorStub_;
       RootedObjectGroup group_;
       JSValueType elementType_;
 
@@ -940,26 +941,26 @@ class ICGetElem_UnboxedArray : public IC
 };
 
 // Accesses scalar elements of a typed array or typed object.
 class ICGetElem_TypedArray : public ICStub
 {
     friend class ICStubSpace;
 
   protected: // Protected to silence Clang warning.
-    HeapPtrShape shape_;
+    GCPtrShape shape_;
 
     ICGetElem_TypedArray(JitCode* stubCode, Shape* shape, Scalar::Type type);
 
   public:
     static size_t offsetOfShape() {
         return offsetof(ICGetElem_TypedArray, shape_);
     }
 
-    HeapPtrShape& shape() {
+    GCPtrShape& shape() {
         return shape_;
     }
 
     class Compiler : public ICStubCompiler {
       RootedShape shape_;
       Scalar::Type type_;
       TypedThingLayout layout_;
 
@@ -1071,33 +1072,33 @@ class ICSetElem_Fallback : public ICFall
         }
     };
 };
 
 class ICSetElem_DenseOrUnboxedArray : public ICUpdatedStub
 {
     friend class ICStubSpace;
 
-    HeapPtrShape shape_; // null for unboxed arrays
-    HeapPtrObjectGroup group_;
+    GCPtrShape shape_; // null for unboxed arrays
+    GCPtrObjectGroup group_;
 
     ICSetElem_DenseOrUnboxedArray(JitCode* stubCode, Shape* shape, ObjectGroup* group);
 
   public:
     static size_t offsetOfShape() {
         return offsetof(ICSetElem_DenseOrUnboxedArray, shape_);
     }
     static size_t offsetOfGroup() {
         return offsetof(ICSetElem_DenseOrUnboxedArray, group_);
     }
 
-    HeapPtrShape& shape() {
+    GCPtrShape& shape() {
         return shape_;
     }
-    HeapPtrObjectGroup& group() {
+    GCPtrObjectGroup& group() {
         return group_;
     }
 
     class Compiler : public ICStubCompiler {
         RootedShape shape_;
         RootedObjectGroup group_;
         JSValueType unboxedType_;
 
@@ -1138,26 +1139,26 @@ template <size_t ProtoChainDepth> class 
 class ICSetElem_DenseOrUnboxedArrayAdd : public ICUpdatedStub
 {
     friend class ICStubSpace;
 
   public:
     static const size_t MAX_PROTO_CHAIN_DEPTH = 4;
 
   protected:
-    HeapPtrObjectGroup group_;
+    GCPtrObjectGroup group_;
 
     ICSetElem_DenseOrUnboxedArrayAdd(JitCode* stubCode, ObjectGroup* group, size_t protoChainDepth);
 
   public:
     static size_t offsetOfGroup() {
         return offsetof(ICSetElem_DenseOrUnboxedArrayAdd, group_);
     }
 
-    HeapPtrObjectGroup& group() {
+    GCPtrObjectGroup& group() {
         return group_;
     }
     size_t protoChainDepth() const {
         MOZ_ASSERT(extra_ <= MAX_PROTO_CHAIN_DEPTH);
         return extra_;
     }
 
     template <size_t ProtoChainDepth>
@@ -1174,17 +1175,17 @@ class ICSetElem_DenseOrUnboxedArrayAdd :
 
 template <size_t ProtoChainDepth>
 class ICSetElem_DenseOrUnboxedArrayAddImpl : public ICSetElem_DenseOrUnboxedArrayAdd
 {
     friend class ICStubSpace;
 
     // Note: for unboxed arrays, the first shape is null.
     static const size_t NumShapes = ProtoChainDepth + 1;
-    mozilla::Array<HeapPtrShape, NumShapes> shapes_;
+    mozilla::Array<GCPtrShape, NumShapes> shapes_;
 
     ICSetElem_DenseOrUnboxedArrayAddImpl(JitCode* stubCode, ObjectGroup* group,
                                          Handle<ShapeVector> shapes)
       : ICSetElem_DenseOrUnboxedArrayAdd(stubCode, group, ProtoChainDepth)
     {
         MOZ_ASSERT(shapes.length() == NumShapes);
         for (size_t i = 0; i < NumShapes; i++)
             shapes_[i].init(shapes[i]);
@@ -1195,17 +1196,17 @@ class ICSetElem_DenseOrUnboxedArrayAddIm
         for (size_t i = 0; i < NumShapes; i++)
             TraceNullableEdge(trc, &shapes_[i], "baseline-setelem-denseadd-stub-shape");
     }
     Shape* shape(size_t i) const {
         MOZ_ASSERT(i < NumShapes);
         return shapes_[i];
     }
     static size_t offsetOfShape(size_t idx) {
-        return offsetof(ICSetElem_DenseOrUnboxedArrayAddImpl, shapes_) + idx * sizeof(HeapPtrShape);
+        return offsetof(ICSetElem_DenseOrUnboxedArrayAddImpl, shapes_) + idx * sizeof(GCPtrShape);
     }
 };
 
 class ICSetElemDenseOrUnboxedArrayAddCompiler : public ICStubCompiler {
     RootedObject obj_;
     size_t protoChainDepth_;
     JSValueType unboxedType_;
 
@@ -1240,17 +1241,17 @@ class ICSetElemDenseOrUnboxedArrayAddCom
 };
 
 // Accesses scalar elements of a typed array or typed object.
 class ICSetElem_TypedArray : public ICStub
 {
     friend class ICStubSpace;
 
   protected: // Protected to silence Clang warning.
-    HeapPtrShape shape_;
+    GCPtrShape shape_;
 
     ICSetElem_TypedArray(JitCode* stubCode, Shape* shape, Scalar::Type type,
                          bool expectOutOfBounds);
 
   public:
     Scalar::Type type() const {
         return (Scalar::Type)