merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 05 May 2015 12:01:27 +0200
changeset 273703 754579ec0e68068d32be534d553d5b191d918d84
parent 273649 4c93d46ab92fe880f1be7afe6114d55b9972cdea (current diff)
parent 273702 54333627915b2bf94331c86dd68486bb2340c90d (diff)
child 273714 50608a9c7b091eb8ba2b59105c7175954a572def
child 273735 ae783e4a68eb2c1852aeec061eeb7822ad97b073
child 273752 cca08f3dc531b36c296ccf3fd45fa8deae828f8d
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone40.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
browser/extensions/Makefile.in
mobile/android/extensions/Makefile.in
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,10 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1159082 - Renaming *Readonly animation interfaces to *ReadOnly causes
-build bustage on case-insensitive filesystems.
+Bug 1128037 needed a clobber to properly build on OS X
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -8,18 +8,17 @@ var gPluginHandler = {
   PREF_PERSISTENT_DAYS: "plugin.persistentPermissionAlways.intervalInDays",
   MESSAGES: [
     "PluginContent:ShowClickToPlayNotification",
     "PluginContent:RemoveNotification",
     "PluginContent:UpdateHiddenPluginUI",
     "PluginContent:HideNotificationBar",
     "PluginContent:ShowInstallNotification",
     "PluginContent:InstallSinglePlugin",
-    "PluginContent:ShowNPAPIPluginCrashedNotification",
-    "PluginContent:ShowGMPCrashedNotification",
+    "PluginContent:ShowPluginCrashedNotification",
     "PluginContent:SubmitReport",
     "PluginContent:LinkClickCallback",
   ],
 
   init: function () {
     const mm = window.messageManager;
     for (let msg of this.MESSAGES) {
       mm.addMessageListener(msg, this);
@@ -57,25 +56,19 @@ var gPluginHandler = {
       case "PluginContent:HideNotificationBar":
         this.hideNotificationBar(msg.target, msg.data.name);
         break;
       case "PluginContent:ShowInstallNotification":
         return this.showInstallNotification(msg.target, msg.data.pluginInfo);
       case "PluginContent:InstallSinglePlugin":
         this.installSinglePlugin(msg.data.pluginInfo);
         break;
-      case "PluginContent:ShowNPAPIPluginCrashedNotification":
-        this.showNPAPIPluginCrashedNotification(msg.target, msg.data.message,
-                                                msg.data.runID);
-        break;
-      case "PluginContent:ShowGMPCrashedNotification":
-        this.showGMPCrashedNotification(msg.target,
-                                        msg.data.messageString,
-                                        msg.data.pluginDumpID,
-                                        msg.data.browserDumpID);
+      case "PluginContent:ShowPluginCrashedNotification":
+        this.showPluginCrashedNotification(msg.target, msg.data.messageString,
+                                           msg.data.pluginID);
         break;
       case "PluginContent:SubmitReport":
         if (AppConstants.MOZ_CRASHREPORTER) {
           this.submitReport(msg.data.runID, msg.data.keyVals, msg.data.submitURLOptIn);
         }
         break;
       case "PluginContent:LinkClickCallback":
         switch (msg.data.name) {
@@ -493,103 +486,75 @@ var gPluginHandler = {
       state = "please";
     }
 
     let mm = window.getGroupMessageManager("browsers");
     mm.broadcastAsyncMessage("BrowserPlugins:NPAPIPluginProcessCrashed",
                              { pluginName, runID, state });
   },
 
-  showNPAPIPluginCrashedNotification: function (browser, messageString, runID) {
-    let crashReportCallback;
-
-    if (AppConstants.MOZ_CRASHREPORTER &&
-        PluginCrashReporter.hasCrashReport(runID)) {
-      crashReportCallback = () => {
-        PluginCrashReporter.submitCrashReport(runID);
-      };
-    }
-
-    this._showPluginCrashedNotification(browser, messageString, crashReportCallback);
-  },
-
   /**
-   * For now, GMP crashes are handled separately from NPAPI plugin crashes,
-   * since the latter are not yet working for e10s. These will be unified
-   * once e10s support is added for GMP crash reporting in bug 1146955.
-   */
-  showGMPCrashedNotification: function (browser, messageString,
-                                        pluginDumpID, browserDumpID) {
-    let crashReportCallback;
-
-    if (AppConstants.MOZ_CRASHREPORTER && pluginDumpID) {
-      crashReportCallback = () => {
-        PluginCrashReporter.submitGMPCrashReport(pluginDumpID, browserDumpID);
-      };
-    }
-
-    this._showPluginCrashedNotification(browser, messageString, crashReportCallback);
-  },
-
-  /**
-   * A helper function for showing the plugin crashed notification bar.
+   * Shows a plugin-crashed notification bar for a browser that has had an
+   * invisiable NPAPI plugin crash, or a GMP plugin crash.
    *
    * @param browser
-   *        The browser that contains the crashing plugin.
+   *        The browser to show the notification for.
    * @param messageString
-   *        The message to display in the notification.
-   * @param crashReportCallback
-   *        Optional. Pass a function to submit a crash report for this plugin
-   *        crash if a report exists. If no function is passed, the Submit Report
-   *        button will not be added.
+   *        The string to put in the notification bar
+   * @param pluginID
+   *        The unique-per-process identifier for the NPAPI plugin or GMP.
+   *        For a GMP, this is the pluginID. For NPAPI plugins (where "pluginID"
+   *        means something different), this is the runID.
    */
-  _showPluginCrashedNotification: function (browser, messageString, crashReportCallback) {
+  showPluginCrashedNotification: function (browser, messageString, pluginID) {
     // If there's already an existing notification bar, don't do anything.
     let notificationBox = gBrowser.getNotificationBox(browser);
     let notification = notificationBox.getNotificationWithValue("plugin-crashed");
-    if (notification)
+    if (notification) {
       return;
+    }
 
     // Configure the notification bar
     let priority = notificationBox.PRIORITY_WARNING_MEDIUM;
     let iconURL = "chrome://mozapps/skin/plugins/notifyPluginCrashed.png";
     let reloadLabel = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.label");
     let reloadKey   = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.accesskey");
-    let submitLabel = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.label");
-    let submitKey   = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.accesskey");
 
     let buttons = [{
       label: reloadLabel,
       accessKey: reloadKey,
       popup: null,
       callback: function() { browser.reload(); },
     }];
 
-    if (AppConstants.MOZ_CRASHREPORTER && crashReportCallback) {
+    if (AppConstants.MOZ_CRASHREPORTER &&
+        PluginCrashReporter.hasCrashReport(pluginID)) {
+      let submitLabel = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.label");
+      let submitKey   = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.accesskey");
       let submitButton = {
         label: submitLabel,
         accessKey: submitKey,
         popup: null,
-        callback: crashReportCallback,
+        callback: () => {
+          PluginCrashReporter.submitCrashReport(pluginID);
+        },
       };
 
       buttons.push(submitButton);
     }
 
     notification = notificationBox.appendNotification(messageString, "plugin-crashed",
                                                       iconURL, priority, buttons);
 
     // Add the "learn more" link.
     let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
     let link = notification.ownerDocument.createElementNS(XULNS, "label");
     link.className = "text-link";
     link.setAttribute("value", gNavigatorBundle.getString("crashedpluginsMessage.learnMore"));
     let crashurl = formatURL("app.support.baseURL", true);
     crashurl += "plugin-crashed-notificationbar";
     link.href = crashurl;
-
     let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
     description.appendChild(link);
   },
 };
 
 gPluginHandler.init();
-
--- a/browser/base/content/test/plugins/browser_globalplugin_crashinfobar.js
+++ b/browser/base/content/test/plugins/browser_globalplugin_crashinfobar.js
@@ -1,22 +1,20 @@
 /**
- * Test that plugin crash submissions still work properly after
- * click-to-play activation.
+ * Test that the notification bar for crashed GMPs works.
  */
 add_task(function*() {
   yield BrowserTestUtils.withNewTab({
     gBrowser,
     url: "about:blank",
   }, function* (browser) {
     yield ContentTask.spawn(browser, null, function* () {
       const GMP_CRASH_EVENT = {
+        pluginID: 1,
         pluginName: "GlobalTestPlugin",
-        pluginDumpID: "1234",
-        browserDumpID: "5678",
         submittedCrashReport: false,
         bubbles: true,
         cancelable: true,
         gmpPlugin: true,
       };
 
       let crashEvent = new content.PluginCrashedEvent("PluginCrashed",
                                                       GMP_CRASH_EVENT);
deleted file mode 100644
--- a/browser/extensions/Makefile.in
+++ /dev/null
@@ -1,32 +0,0 @@
-# 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 $(topsrcdir)/config/rules.mk
-
-exclude_files = \
-  test \
-  README.mozilla \
-  $(NULL)
-
-$(FINAL_TARGET)/chrome/pdfjs.manifest: $(GLOBAL_DEPS)
-	printf 'manifest pdfjs/chrome.manifest' > $@
-
-libs:: $(FINAL_TARGET)/chrome/pdfjs.manifest
-	$(PYTHON) $(topsrcdir)/config/nsinstall.py \
-	  $(srcdir)/pdfjs \
-          $(foreach exclude,$(exclude_files), -X $(srcdir)/pdfjs/$(exclude)) \
-          $(FINAL_TARGET)/chrome
-	$(call py_action,buildlist,$(FINAL_TARGET)/chrome.manifest 'manifest chrome/pdfjs.manifest')
-
-ifdef NIGHTLY_BUILD
-$(FINAL_TARGET)/chrome/shumway.manifest: $(GLOBAL_DEPS)
-	printf 'manifest shumway/chrome.manifest' > $@
-
-libs:: $(FINAL_TARGET)/chrome/shumway.manifest
-	$(PYTHON) $(topsrcdir)/config/nsinstall.py \
-	  $(srcdir)/shumway \
-          $(foreach exclude,$(exclude_files), -X $(srcdir)/shumway/$(exclude)) \
-          $(FINAL_TARGET)/chrome
-	$(call py_action,buildlist,$(FINAL_TARGET)/chrome.manifest 'manifest chrome/shumway.manifest')
-endif
--- a/browser/extensions/moz.build
+++ b/browser/extensions/moz.build
@@ -1,7 +1,10 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-BROWSER_CHROME_MANIFESTS += ['pdfjs/test/browser.ini']
+DIRS += [
+    'pdfjs',
+    'shumway',
+]
new file mode 100644
--- /dev/null
+++ b/browser/extensions/pdfjs/jar.mn
@@ -0,0 +1,3 @@
+pdfjs.jar:
+% resource pdf.js %content/
+ content/	(content/*)
new file mode 100644
--- /dev/null
+++ b/browser/extensions/pdfjs/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
+
+JAR_MANIFESTS += ['jar.mn']
new file mode 100644
--- /dev/null
+++ b/browser/extensions/shumway/jar.mn
@@ -0,0 +1,5 @@
+shumway.jar:
+% content shumway %chrome/
+% resource shumway %content/
+ chrome/	(chrome/*)
+ content/	(content/*)
new file mode 100644
--- /dev/null
+++ b/browser/extensions/shumway/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+JAR_MANIFESTS += ['jar.mn']
--- a/browser/installer/windows/nsis/installer.nsi
+++ b/browser/installer/windows/nsis/installer.nsi
@@ -790,43 +790,34 @@ Function LaunchApp
 !ifndef DEV_EDITION
   ${ManualCloseAppPrompt} "${WindowClass}" "$(WARN_MANUALLY_CLOSE_APP_LAUNCH)"
 !endif
 
   ClearErrors
   ${GetParameters} $0
   ${GetOptions} "$0" "/UAC:" $1
   ${If} ${Errors}
-    StrCpy $1 "0"
-    StrCpy $2 "0"
-    ${If} $1 == "1"
-      Exec "$\"$INSTDIR\${FileMainEXE}$\""
-    ${EndIf}
+    Exec "$\"$INSTDIR\${FileMainEXE}$\""
   ${Else}
     GetFunctionAddress $0 LaunchAppFromElevatedProcess
     UAC::ExecCodeSegment $0
   ${EndIf}
 FunctionEnd
 
 Function LaunchAppFromElevatedProcess
   ; Find the installation directory when launching using GetFunctionAddress
   ; from an elevated installer since $INSTDIR will not be set in this installer
   ${StrFilter} "${FileMainEXE}" "+" "" "" $R9
   ReadRegStr $0 HKLM "Software\Clients\StartMenuInternet\$R9\DefaultIcon" ""
   ${GetPathFromString} "$0" $0
   ${GetParent} "$0" $1
   ; Set our current working directory to the application's install directory
   ; otherwise the 7-Zip temp directory will be in use and won't be deleted.
   SetOutPath "$1"
-  StrCpy $2 "0"
-  StrCpy $3 "0"
-  ${If} $2 == "1"
-    ; Launch into desktop
-    Exec "$\"$0$\""
-  ${EndIf}
+  Exec "$\"$0$\""
 FunctionEnd
 
 ################################################################################
 # Language
 
 !insertmacro MOZ_MUI_LANGUAGE 'baseLocale'
 !verbose push
 !verbose 3
--- a/browser/modules/ContentCrashReporters.jsm
+++ b/browser/modules/ContentCrashReporters.jsm
@@ -109,37 +109,78 @@ this.PluginCrashReporter = {
     if (this.initialized) {
       return;
     }
 
     this.initialized = true;
     this.crashReports = new Map();
 
     Services.obs.addObserver(this, "plugin-crashed", false);
+    Services.obs.addObserver(this, "gmp-plugin-crash", false);
+    Services.obs.addObserver(this, "profile-after-change", false);
+  },
+
+  uninit() {
+    Services.obs.removeObserver(this, "plugin-crashed", false);
+    Services.obs.removeObserver(this, "gmp-plugin-crash", false);
+    Services.obs.removeObserver(this, "profile-after-change", false);
+    this.initialized = false;
   },
 
   observe(subject, topic, data) {
-    if (topic != "plugin-crashed") {
-      return;
-    }
+    switch(topic) {
+      case "plugin-crashed": {
+        let propertyBag = subject;
+        if (!(propertyBag instanceof Ci.nsIPropertyBag2) ||
+            !(propertyBag instanceof Ci.nsIWritablePropertyBag2) ||
+            !propertyBag.hasKey("runID") ||
+            !propertyBag.hasKey("pluginDumpID")) {
+          Cu.reportError("PluginCrashReporter can not read plugin information.");
+          return;
+        }
 
-    let propertyBag = subject;
-    if (!(propertyBag instanceof Ci.nsIPropertyBag2) ||
-        !(propertyBag instanceof Ci.nsIWritablePropertyBag2) ||
-        !propertyBag.hasKey("runID") ||
-        !propertyBag.hasKey("pluginName")) {
-      Cu.reportError("PluginCrashReporter can not read plugin information.");
-      return;
-    }
+        let runID = propertyBag.getPropertyAsUint32("runID");
+        let pluginDumpID = propertyBag.getPropertyAsAString("pluginDumpID");
+        let browserDumpID = propertyBag.getPropertyAsAString("browserDumpID");
+        if (pluginDumpID) {
+          this.crashReports.set(runID, { pluginDumpID, browserDumpID });
+        }
+        break;
+      }
+      case "gmp-plugin-crash": {
+        let propertyBag = subject;
+        if (!(propertyBag instanceof Ci.nsIWritablePropertyBag2) ||
+            !propertyBag.hasKey("pluginID") ||
+            !propertyBag.hasKey("pluginDumpID") ||
+            !propertyBag.hasKey("pluginName")) {
+          Cu.reportError("PluginCrashReporter can not read plugin information.");
+          return;
+        }
 
-    let runID = propertyBag.getPropertyAsUint32("runID");
-    let pluginDumpID = propertyBag.getPropertyAsAString("pluginDumpID");
-    let browserDumpID = propertyBag.getPropertyAsAString("browserDumpID");
-    if (pluginDumpID) {
-      this.crashReports.set(runID, { pluginDumpID, browserDumpID });
+        let pluginID = propertyBag.getPropertyAsUint32("pluginID");
+        let pluginDumpID = propertyBag.getPropertyAsAString("pluginDumpID");
+        if (pluginDumpID) {
+          this.crashReports.set(pluginID, { pluginDumpID });
+        }
+
+        // Only the parent process gets the gmp-plugin-crash observer
+        // notification, so we need to inform any content processes that
+        // the GMP has crashed.
+        if (Cc["@mozilla.org/parentprocessmessagemanager;1"]) {
+          let pluginName = propertyBag.getPropertyAsAString("pluginName");
+          let mm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
+            .getService(Ci.nsIMessageListenerManager);
+          mm.broadcastAsyncMessage("gmp-plugin-crash",
+                                   { pluginName, pluginID });
+        }
+        break;
+      }
+      case "profile-after-change":
+        this.uninit();
+        break;
     }
   },
 
   /**
    * Submit a crash report for a crashed NPAPI plugin.
    *
    * @param runID
    *        The runID of the plugin that crashed. A run ID is a unique
@@ -189,19 +230,9 @@ this.PluginCrashReporter = {
       mm.broadcastAsyncMessage("BrowserPlugins:CrashReportSubmitted",
                                { runID, state });
     }
   },
 
   hasCrashReport(runID) {
     return this.crashReports.has(runID);
   },
-
-  /**
-   * Deprecated mechanism for sending crash reports for GMPs. This
-   * should be removed when bug 1146955 is fixed.
-   */
-  submitGMPCrashReport(pluginDumpID, browserDumpID) {
-    CrashSubmit.submit(pluginDumpID, { recordSubmission: true });
-    if (browserDumpID)
-      CrashSubmit.submit(browserDumpID);
-  },
 };
--- a/browser/modules/PluginContent.jsm
+++ b/browser/modules/PluginContent.jsm
@@ -1026,18 +1026,18 @@ PluginContent.prototype = {
       // notification bar, then remove it because this plugin instance it big
       // enough to serve as in-content notification.
       this.hideNotificationBar("plugin-crashed");
       doc.mozNoPluginCrashedNotification = true;
     } else {
       // If another plugin on the page was large enough to show our UI, we don't
       // want to show a notification bar.
       if (!doc.mozNoPluginCrashedNotification) {
-        this.global.sendAsyncMessage("PluginContent:ShowNPAPIPluginCrashedNotification",
-                                     { message, runID });
+        this.global.sendAsyncMessage("PluginContent:ShowPluginCrashedNotification",
+                                     { messageString: message, pluginID: runID });
         // Remove the notification when the page is reloaded.
         doc.defaultView.top.addEventListener("unload", event => {
           this.hideNotificationBar("plugin-crashed");
         }, false);
       }
     }
   },
 
@@ -1052,42 +1052,33 @@ PluginContent.prototype = {
       if (plugin instanceof Ci.nsIObjectLoadingContent &&
           plugin.runID == runID) {
         let statusDiv = this.getPluginUI(plugin, "submitStatus");
         statusDiv.setAttribute("status", state);
       }
     }
   },
 
-  /**
-   * Currently, GMP crash events are only handled in the non-e10s case.
-   * e10s support for GMP crash events is being tracked in bug 1146955.
-   */
   GMPCrashed: function(aEvent) {
     let target          = aEvent.target;
-    let submittedReport = aEvent.submittedCrashReport;
     let pluginName      = aEvent.pluginName;
-    let pluginDumpID    = aEvent.pluginDumpID;
-    let browserDumpID   = aEvent.browserDumpID;
     let gmpPlugin       = aEvent.gmpPlugin;
+    let pluginID        = aEvent.pluginID;
     let doc             = target.document;
 
     if (!gmpPlugin || !doc) {
       // TODO: Throw exception? How did we get here?
       return;
     }
 
     let messageString =
       gNavigatorBundle.formatStringFromName("crashedpluginsMessage.title",
                                             [pluginName], 1);
 
-    this.global.sendAsyncMessage("PluginContent:ShowGMPCrashedNotification", {
-      messageString: messageString,
-      pluginDumpID: pluginDumpID,
-      browserDumpID: browserDumpID,
-    });
+    this.global.sendAsyncMessage("PluginContent:ShowPluginCrashedNotification",
+                                 { messageString, pluginID });
 
     // Remove the notification when the page is reloaded.
     doc.defaultView.top.addEventListener("unload", event => {
       this.hideNotificationBar("plugin-crashed");
     }, false);
   },
 };
--- a/build/docs/jar-manifests.rst
+++ b/build/docs/jar-manifests.rst
@@ -54,16 +54,25 @@ equivalent.
 There is a special source-directory format for localized files (note the
 percent sign in the source file location): this format reads ``localized.dtd``
 from the ``en-US`` directory if building an English version, and reads the
 file from the alternate localization source tree
 ``/l10n/<locale>/path/localized.dtd`` if building a localized version::
 
    locale/path/localized.dtd     (%localized/path/localized.dtd)
 
+The source tree location can also use wildcards, in which case the path in
+jar is expected to be a base directory. Paths before the wildcard are not
+made part of the destination path::
+
+     path/in/jar/                (source/tree/location/*.xul)
+
+The above will install all xul files under ``source/tree/location`` as
+``path/in/jar/*.xul``.
+
 Register Chrome
 ===============
 
 `Chrome Registration <https://developer.mozilla.org/en-US/docs/Chrome_Registration>`_
 instructions are marked with a percent sign (``%``) at the beginning of the
 line, and must be part of the definition of a JAR file. Any additional percents
 signs are replaced with an appropriate relative URL of the JAR file being
 packaged::
--- a/build/mobile/remoteautomation.py
+++ b/build/mobile/remoteautomation.py
@@ -274,18 +274,19 @@ class RemoteAutomation(Automation):
                     self.proc = stdout
                 else:
                     raise Exception("unable to launch process")
             self.procName = cmd[0].split('/')[-1]
             if cmd[0] == 'am' and cmd[1] == "instrument":
                 self.procName = app
                 print "Robocop process name: "+self.procName
 
-            # Setting timeout at 1 hour since on a remote device this takes much longer
-            self.timeout = 3600
+            # Setting timeout at 1 hour since on a remote device this takes much longer.
+            # Temporarily increased to 75 minutes because no more chunks can be created.
+            self.timeout = 4500
             # The benefit of the following sleep is unclear; it was formerly 15 seconds
             time.sleep(1)
 
             # Used to buffer log messages until we meet a line break
             self.logBuffer = ""
 
         @property
         def pid(self):
--- a/configure.in
+++ b/configure.in
@@ -3572,17 +3572,17 @@ dnl = If NSS was not detected in the sys
 dnl = use the one in the source tree (mozilla/security/nss)
 dnl ========================================================
 
 MOZ_ARG_WITH_BOOL(system-nss,
 [  --with-system-nss       Use system installed NSS],
     _USE_SYSTEM_NSS=1 )
 
 if test -n "$_USE_SYSTEM_NSS"; then
-    AM_PATH_NSS(3.18, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
+    AM_PATH_NSS(3.19, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
 fi
 
 if test -n "$MOZ_NATIVE_NSS"; then
    NSS_LIBS="$NSS_LIBS -lcrmf"
 else
    NSS_CFLAGS='-I$(LIBXUL_DIST)/include/nss'
 
    if test -z "$GNU_CC" -a "$OS_ARCH" = "WINNT"; then
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -134,16 +134,17 @@
 #include "nsITextControlElement.h"
 #include "nsITextControlFrame.h"
 #include "nsISupportsImpl.h"
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "mozilla/dom/ElementBinding.h"
 #include "mozilla/dom/VRDevice.h"
+#include "nsComputedDOMStyle.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 nsIAtom*
 nsIContent::DoGetID() const
 {
   MOZ_ASSERT(HasID(), "Unexpected call");
@@ -380,29 +381,26 @@ Element::GetBindingURL(nsIDocument *aDoc
   // If we have a frame the frame has already loaded the binding.  And
   // otherwise, don't do anything else here unless we're dealing with
   // XUL or an HTML element that may have a plugin-related overlay
   // (i.e. object, embed, or applet).
   bool isXULorPluginElement = (IsXULElement() ||
                                IsHTMLElement(nsGkAtoms::object) ||
                                IsHTMLElement(nsGkAtoms::embed) ||
                                IsHTMLElement(nsGkAtoms::applet));
-  nsIPresShell *shell = aDocument->GetShell();
+  nsCOMPtr<nsIPresShell> shell = aDocument->GetShell();
   if (!shell || GetPrimaryFrame() || !isXULorPluginElement) {
     *aResult = nullptr;
 
     return true;
   }
 
   // Get the computed -moz-binding directly from the style context
-  nsPresContext *pctx = shell->GetPresContext();
-  NS_ENSURE_TRUE(pctx, false);
-
-  nsRefPtr<nsStyleContext> sc = pctx->StyleSet()->ResolveStyleFor(this,
-                                                                  nullptr);
+  nsRefPtr<nsStyleContext> sc =
+    nsComputedDOMStyle::GetStyleContextForElementNoFlush(this, nullptr, shell);
   NS_ENSURE_TRUE(sc, false);
 
   *aResult = sc->StyleDisplay()->mBinding;
 
   return true;
 }
 
 JSObject*
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -10393,17 +10393,17 @@ class CGDOMJSProxyHandler_getOwnPropDesc
             namedGet = ""
 
         return fill(
             """
             bool isXray = xpc::WrapperFactory::IsXrayWrapper(proxy);
             $*{getIndexed}
             JS::Rooted<JSObject*> expando(cx);
             if (!isXray && (expando = GetExpandoObject(proxy))) {
-              if (!JS_GetPropertyDescriptorById(cx, expando, id, desc)) {
+              if (!JS_GetOwnPropertyDescriptorById(cx, expando, id, desc)) {
                 return false;
               }
               if (desc.object()) {
                 // Pretend the property lives on the wrapper.
                 desc.object().set(proxy);
                 return true;
               }
             }
--- a/dom/bindings/DOMJSProxyHandler.cpp
+++ b/dom/bindings/DOMJSProxyHandler.cpp
@@ -205,35 +205,22 @@ DOMProxyHandler::set(JSContext *cx, Hand
     return false;
   }
   if (done) {
     return result.succeed();
   }
 
   // Make sure to ignore our named properties when checking for own
   // property descriptors for a set.
-  JS::Rooted<JSPropertyDescriptor> desc(cx);
+  JS::Rooted<JSPropertyDescriptor> ownDesc(cx);
   if (!getOwnPropDescriptor(cx, proxy, id, /* ignoreNamedProps = */ true,
-                            &desc)) {
+                            &ownDesc)) {
     return false;
   }
-  if (!desc.object()) {
-    // Don't just use getPropertyDescriptor, unlike BaseProxyHandler::set,
-    // because that would call getOwnPropertyDescriptor on ourselves.  Instead,
-    // directly delegate to the proto, if any.
-    JS::Rooted<JSObject*> proto(cx);
-    if (!js::GetObjectProto(cx, proxy, &proto)) {
-      return false;
-    }
-    if (proto && !JS_GetPropertyDescriptorById(cx, proto, id, &desc)) {
-      return false;
-    }
-  }
-
-  return js::SetPropertyIgnoringNamedGetter(cx, proxy, id, v, receiver, desc, result);
+  return js::SetPropertyIgnoringNamedGetter(cx, proxy, id, v, receiver, ownDesc, result);
 }
 
 bool
 DOMProxyHandler::delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
                          JS::Handle<jsid> id, JS::ObjectOpResult &result) const
 {
   JS::Rooted<JSObject*> expando(cx);
   if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) {
--- a/dom/cache/TypeUtils.cpp
+++ b/dom/cache/TypeUtils.cpp
@@ -395,17 +395,18 @@ TypeUtils::ProcessURL(nsAString& aUrl, b
   aRv = urlParser->ParseURL(url, flatURL.Length(), &schemePos, &schemeLen,
                             nullptr, nullptr,       // ignore authority
                             &pathPos, &pathLen);
   if (NS_WARN_IF(aRv.Failed())) { return; }
 
   if (aSchemeValidOut) {
     nsAutoCString scheme(Substring(flatURL, schemePos, schemeLen));
     *aSchemeValidOut = scheme.LowerCaseEqualsLiteral("http") ||
-                       scheme.LowerCaseEqualsLiteral("https");
+                       scheme.LowerCaseEqualsLiteral("https") ||
+                       scheme.LowerCaseEqualsLiteral("app");
   }
 
   uint32_t queryPos;
   int32_t queryLen;
   uint32_t refPos;
   int32_t refLen;
 
   aRv = urlParser->ParsePath(url + pathPos, flatURL.Length() - pathPos,
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -285,20 +285,17 @@ void MediaDecoder::Pause()
   }
 
   ChangeState(PLAY_STATE_PAUSED);
 }
 
 void MediaDecoder::SetVolume(double aVolume)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  mInitialVolume = aVolume;
-  if (mDecoderStateMachine) {
-    mDecoderStateMachine->SetVolume(aVolume);
-  }
+  mVolume = aVolume;
 }
 
 void MediaDecoder::ConnectDecodedStreamToOutputStream(OutputStreamData* aStream)
 {
   NS_ASSERTION(!aStream->mPort, "Already connected?");
 
   // The output stream must stay in sync with the decoded stream, so if
   // either stream is blocked, we block the other.
@@ -533,17 +530,17 @@ void MediaDecoder::AddOutputStream(Proce
                                    bool aFinishWhenEnded)
 {
   MOZ_ASSERT(NS_IsMainThread());
   DECODER_LOG("AddOutputStream aStream=%p!", aStream);
 
   {
     ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
     if (mDecoderStateMachine) {
-      mDecoderStateMachine->SetAudioCaptured();
+      mDecoderStateMachine->DispatchAudioCaptured();
     }
     if (!GetDecodedStream()) {
       int64_t t = mDecoderStateMachine ?
                   mDecoderStateMachine->GetCurrentTimeUs() : 0;
       RecreateDecodedStream(t, aStream->Graph());
     }
     OutputStreamData* os = mOutputStreams.AppendElement();
     os->Init(this, aStream);
@@ -597,19 +594,19 @@ bool MediaDecoder::IsInfinite()
 MediaDecoder::MediaDecoder() :
   mWatchManager(this, AbstractThread::MainThread()),
   mNextFrameStatus(AbstractThread::MainThread(),
                    MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED,
                    "MediaDecoder::mNextFrameStatus (Mirror)"),
   mDecoderPosition(0),
   mPlaybackPosition(0),
   mCurrentTime(0.0),
-  mInitialVolume(0.0),
-  mInitialPlaybackRate(1.0),
-  mInitialPreservesPitch(true),
+  mVolume(AbstractThread::MainThread(), 0.0, "MediaDecoder::mVolume (Canonical)"),
+  mPlaybackRate(AbstractThread::MainThread(), 1.0, "MediaDecoder::mPlaybackRate (Canonical)"),
+  mPreservesPitch(AbstractThread::MainThread(), true, "MediaDecoder::mPreservesPitch (Canonical)"),
   mDuration(-1),
   mMediaSeekable(true),
   mSameOriginMedia(false),
   mReentrantMonitor("media.decoder"),
   mPlayState(AbstractThread::MainThread(), PLAY_STATE_LOADING,
              "MediaDecoder::mPlayState (Canonical)"),
   mNextState(AbstractThread::MainThread(), PLAY_STATE_PAUSED,
              "MediaDecoder::mNextState (Canonical)"),
@@ -745,31 +742,33 @@ nsresult MediaDecoder::InitializeStateMa
 
   return ScheduleStateMachineThread();
 }
 
 void MediaDecoder::SetStateMachineParameters()
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   mDecoderStateMachine->SetDuration(mDuration);
-  mDecoderStateMachine->SetVolume(mInitialVolume);
   if (GetDecodedStream()) {
-    mDecoderStateMachine->SetAudioCaptured();
+    mDecoderStateMachine->DispatchAudioCaptured();
   }
-  SetPlaybackRate(mInitialPlaybackRate);
-  mDecoderStateMachine->SetPreservesPitch(mInitialPreservesPitch);
   if (mMinimizePreroll) {
-    mDecoderStateMachine->SetMinimizePrerollUntilPlaybackStarts();
+    mDecoderStateMachine->DispatchMinimizePrerollUntilPlaybackStarts();
   }
 }
 
 void MediaDecoder::SetMinimizePrerollUntilPlaybackStarts()
 {
+  DECODER_LOG("SetMinimizePrerollUntilPlaybackStarts()");
   MOZ_ASSERT(NS_IsMainThread());
   mMinimizePreroll = true;
+
+  // This needs to be called before we init the state machine, otherwise it will
+  // have no effect.
+  MOZ_DIAGNOSTIC_ASSERT(!mDecoderStateMachine);
 }
 
 nsresult MediaDecoder::ScheduleStateMachineThread()
 {
   MOZ_ASSERT(NS_IsMainThread());
   NS_ASSERTION(mDecoderStateMachine,
                "Must have state machine to start state machine thread");
   NS_ENSURE_STATE(mDecoderStateMachine);
@@ -1545,45 +1544,34 @@ void MediaDecoder::UpdatePlaybackOffset(
 
 bool MediaDecoder::OnStateMachineTaskQueue() const
 {
   return mDecoderStateMachine->OnTaskQueue();
 }
 
 void MediaDecoder::SetPlaybackRate(double aPlaybackRate)
 {
-  if (aPlaybackRate == 0.0) {
+  mPlaybackRate = aPlaybackRate;
+  if (mPlaybackRate == 0.0) {
     mPausedForPlaybackRateNull = true;
-    mInitialPlaybackRate = aPlaybackRate;
     Pause();
-    return;
   } else if (mPausedForPlaybackRateNull) {
     // Play() uses mPausedForPlaybackRateNull value, so must reset it first
     mPausedForPlaybackRateNull = false;
     // If the playbackRate is no longer null, restart the playback, iff the
     // media was playing.
     if (mOwner && !mOwner->GetPaused()) {
       Play();
     }
   }
-
-  if (mDecoderStateMachine) {
-    mDecoderStateMachine->SetPlaybackRate(aPlaybackRate);
-  } else {
-    mInitialPlaybackRate = aPlaybackRate;
-  }
 }
 
 void MediaDecoder::SetPreservesPitch(bool aPreservesPitch)
 {
-  if (mDecoderStateMachine) {
-    mDecoderStateMachine->SetPreservesPitch(aPreservesPitch);
-  } else {
-    mInitialPreservesPitch = aPreservesPitch;
-  }
+  mPreservesPitch = aPreservesPitch;
 }
 
 bool MediaDecoder::OnDecodeTaskQueue() const {
   NS_WARN_IF_FALSE(mDecoderStateMachine, "mDecoderStateMachine is null");
   return mDecoderStateMachine ? mDecoderStateMachine->OnDecodeTaskQueue() : false;
 }
 
 void
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -1074,24 +1074,32 @@ protected:
   int64_t mPlaybackPosition;
 
   // The current playback position of the media resource in units of
   // seconds. This is updated approximately at the framerate of the
   // video (if it is a video) or the callback period of the audio.
   // It is read and written from the main thread only.
   double mCurrentTime;
 
-  // Volume that playback should start at.  0.0 = muted. 1.0 = full
-  // volume.  Readable/Writeable from the main thread.
-  double mInitialVolume;
+  // Volume of playback.  0.0 = muted. 1.0 = full volume.
+  Canonical<double> mVolume;
+public:
+  AbstractCanonical<double>* CanonicalVolume() { return &mVolume; }
+protected:
 
   // PlaybackRate and pitch preservation status we should start at.
-  // Readable/Writeable from the main thread.
-  double mInitialPlaybackRate;
-  bool mInitialPreservesPitch;
+  Canonical<double> mPlaybackRate;
+public:
+  AbstractCanonical<double>* CanonicalPlaybackRate() { return &mPlaybackRate; }
+protected:
+
+  Canonical<bool> mPreservesPitch;
+public:
+  AbstractCanonical<bool>* CanonicalPreservesPitch() { return &mPreservesPitch; }
+protected:
 
   // Duration of the media resource. Set to -1 if unknown.
   // Set when the metadata is loaded. Accessed on the main thread
   // only.
   int64_t mDuration;
 
   // True if the media is seekable (i.e. supports random access).
   bool mMediaSeekable;
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -221,19 +221,20 @@ MediaDecoderStateMachine::MediaDecoderSt
   mFragmentEndTime(-1),
   mReader(aReader),
   mCurrentFrameTime(0),
   mAudioStartTime(-1),
   mAudioEndTime(-1),
   mDecodedAudioEndTime(-1),
   mVideoFrameEndTime(-1),
   mDecodedVideoEndTime(-1),
-  mVolume(1.0),
+  mVolume(mTaskQueue, 1.0, "MediaDecoderStateMachine::mVolume (Mirror)"),
   mPlaybackRate(1.0),
-  mPreservesPitch(true),
+  mLogicalPlaybackRate(mTaskQueue, 1.0, "MediaDecoderStateMachine::mLogicalPlaybackRate (Mirror)"),
+  mPreservesPitch(mTaskQueue, true, "MediaDecoderStateMachine::mPreservesPitch (Mirror)"),
   mLowAudioThresholdUsecs(detail::LOW_AUDIO_USECS),
   mAmpleAudioThresholdUsecs(detail::AMPLE_AUDIO_USECS),
   mQuickBufferingLowDataThresholdUsecs(detail::QUICK_BUFFERING_LOW_DATA_USECS),
   mIsAudioPrerolling(false),
   mIsVideoPrerolling(false),
   mAudioCaptured(false),
   mPositionChangeQueued(false),
   mAudioCompleted(false, "MediaDecoderStateMachine::mAudioCompleted"),
@@ -302,37 +303,46 @@ MediaDecoderStateMachine::~MediaDecoderS
 void
 MediaDecoderStateMachine::InitializationTask()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   // Connect mirrors.
   mPlayState.Connect(mDecoder->CanonicalPlayState());
   mNextPlayState.Connect(mDecoder->CanonicalNextPlayState());
+  mVolume.Connect(mDecoder->CanonicalVolume());
+  mLogicalPlaybackRate.Connect(mDecoder->CanonicalPlaybackRate());
+  mPreservesPitch.Connect(mDecoder->CanonicalPreservesPitch());
 
   // Initialize watchers.
   mWatchManager.Watch(mState, &MediaDecoderStateMachine::UpdateNextFrameStatus);
   mWatchManager.Watch(mAudioCompleted, &MediaDecoderStateMachine::UpdateNextFrameStatus);
+  mWatchManager.Watch(mVolume, &MediaDecoderStateMachine::VolumeChanged);
+  mWatchManager.Watch(mLogicalPlaybackRate, &MediaDecoderStateMachine::LogicalPlaybackRateChanged);
+  mWatchManager.Watch(mPreservesPitch, &MediaDecoderStateMachine::PreservesPitchChanged);
+
 }
 
 bool MediaDecoderStateMachine::HasFutureAudio() {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   NS_ASSERTION(HasAudio(), "Should only call HasFutureAudio() when we have audio");
   // We've got audio ready to play if:
   // 1. We've not completed playback of audio, and
   // 2. we either have more than the threshold of decoded audio available, or
   //    we've completely decoded all audio (but not finished playing it yet
   //    as per 1).
   return !mAudioCompleted &&
          (AudioDecodedUsecs() >
             mLowAudioThresholdUsecs * mPlaybackRate ||
           AudioQueue().IsFinished());
 }
 
 bool MediaDecoderStateMachine::HaveNextFrameData() {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   return (!HasAudio() || HasFutureAudio()) &&
          (!HasVideo() || VideoQueue().GetSize() > 0);
 }
 
 int64_t MediaDecoderStateMachine::GetDecodedAudioDuration() {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
@@ -562,26 +572,28 @@ void MediaDecoderStateMachine::SendStrea
   if (finished && AudioQueue().GetSize() == 0) {
     mAudioCompleted = true;
   }
 }
 
 MediaDecoderStateMachine::WakeDecoderRunnable*
 MediaDecoderStateMachine::GetWakeDecoderRunnable()
 {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
 
   if (!mPendingWakeDecoder.get()) {
     mPendingWakeDecoder = new WakeDecoderRunnable(this);
   }
   return mPendingWakeDecoder.get();
 }
 
 bool MediaDecoderStateMachine::HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs)
 {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
 
   if (AudioQueue().GetSize() == 0 ||
       GetDecodedAudioDuration() < aAmpleAudioUSecs) {
     return false;
   }
   if (!mAudioCaptured) {
     return true;
@@ -599,16 +611,17 @@ bool MediaDecoderStateMachine::HaveEnoug
         TaskQueue(), GetWakeDecoderRunnable());
   }
 
   return true;
 }
 
 bool MediaDecoderStateMachine::HaveEnoughDecodedVideo()
 {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
 
   if (static_cast<uint32_t>(VideoQueue().GetSize()) < GetAmpleVideoFrames() * mPlaybackRate) {
     return false;
   }
 
   DecodedStreamData* stream = mDecoder->GetDecodedStream();
 
@@ -623,27 +636,29 @@ bool MediaDecoderStateMachine::HaveEnoug
   }
 
   return true;
 }
 
 bool
 MediaDecoderStateMachine::NeedToDecodeVideo()
 {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   return IsVideoDecoding() &&
          ((mState == DECODER_STATE_SEEKING && mDecodeToSeekTarget) ||
           (mState == DECODER_STATE_DECODING_FIRSTFRAME &&
            IsVideoDecoding() && VideoQueue().GetSize() == 0) ||
           (!mMinimizePreroll && !HaveEnoughDecodedVideo()));
 }
 
 bool
 MediaDecoderStateMachine::NeedToSkipToNextKeyframe()
 {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   if (mState == DECODER_STATE_DECODING_FIRSTFRAME) {
     return false;
   }
   MOZ_ASSERT(mState == DECODER_STATE_DECODING ||
              mState == DECODER_STATE_BUFFERING ||
              mState == DECODER_STATE_SEEKING);
 
@@ -683,16 +698,17 @@ MediaDecoderStateMachine::NeedToSkipToNe
   }
 
   return false;
 }
 
 bool
 MediaDecoderStateMachine::NeedToDecodeAudio()
 {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   SAMPLE_LOG("NeedToDecodeAudio() isDec=%d decToTar=%d minPrl=%d seek=%d enufAud=%d",
              IsAudioDecoding(), mDecodeToSeekTarget, mMinimizePreroll,
              mState == DECODER_STATE_SEEKING,
              HaveEnoughDecodedAudio(mAmpleAudioThresholdUsecs * mPlaybackRate));
 
   return IsAudioDecoding() &&
          ((mState == DECODER_STATE_SEEKING && mDecodeToSeekTarget) ||
@@ -701,29 +717,31 @@ MediaDecoderStateMachine::NeedToDecodeAu
           (!mMinimizePreroll &&
           !HaveEnoughDecodedAudio(mAmpleAudioThresholdUsecs * mPlaybackRate) &&
           (mState != DECODER_STATE_SEEKING || mDecodeToSeekTarget)));
 }
 
 bool
 MediaDecoderStateMachine::IsAudioSeekComplete()
 {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   SAMPLE_LOG("IsAudioSeekComplete() curTarVal=%d mAudDis=%d aqFin=%d aqSz=%d",
     mCurrentSeek.Exists(), mDropAudioUntilNextDiscontinuity, AudioQueue().IsFinished(), AudioQueue().GetSize());
   return
     !HasAudio() ||
     (mCurrentSeek.Exists() &&
      !mDropAudioUntilNextDiscontinuity &&
      (AudioQueue().IsFinished() || AudioQueue().GetSize() > 0));
 }
 
 bool
 MediaDecoderStateMachine::IsVideoSeekComplete()
 {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   SAMPLE_LOG("IsVideoSeekComplete() curTarVal=%d mVidDis=%d vqFin=%d vqSz=%d",
     mCurrentSeek.Exists(), mDropVideoUntilNextDiscontinuity, VideoQueue().IsFinished(), VideoQueue().GetSize());
   return
     !HasVideo() ||
     (mCurrentSeek.Exists() &&
      !mDropVideoUntilNextDiscontinuity &&
      (VideoQueue().IsFinished() || VideoQueue().GetSize() > 0));
@@ -1138,23 +1156,25 @@ MediaDecoderStateMachine::CheckIfSeekCom
     mDecodeToSeekTarget = false;
     SeekCompleted();
   }
 }
 
 bool
 MediaDecoderStateMachine::IsAudioDecoding()
 {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   return HasAudio() && !AudioQueue().IsFinished();
 }
 
 bool
 MediaDecoderStateMachine::IsVideoDecoding()
 {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   return HasVideo() && !VideoQueue().IsFinished();
 }
 
 void
 MediaDecoderStateMachine::CheckIfDecodeComplete()
 {
   MOZ_ASSERT(OnTaskQueue());
@@ -1338,37 +1358,25 @@ void MediaDecoderStateMachine::SetState(
               gMachineStateStr[mState], gMachineStateStr[aState]);
 
   mState = aState;
 
   // Clear state-scoped state.
   mSentPlaybackEndedEvent = false;
 }
 
-void MediaDecoderStateMachine::SetVolume(double volume)
+void MediaDecoderStateMachine::VolumeChanged()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  mVolume = volume;
   if (mAudioSink) {
     mAudioSink->SetVolume(mVolume);
   }
 }
 
-void MediaDecoderStateMachine::SetAudioCaptured()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
-  AssertCurrentThreadInMonitor();
-  if (!mAudioCaptured) {
-    mAudioCaptured = true;
-    // Schedule the state machine to send stream data as soon as possible.
-    ScheduleStateMachine();
-  }
-}
-
 double MediaDecoderStateMachine::GetCurrentTime() const
 {
   return static_cast<double>(mCurrentFrameTime) / static_cast<double>(USECS_PER_S);
 }
 
 int64_t MediaDecoderStateMachine::GetCurrentTimeUs() const
 {
   return mCurrentFrameTime;
@@ -1748,16 +1756,17 @@ void MediaDecoderStateMachine::StopAudio
   }
   // Wake up those waiting for audio sink to finish.
   mDecoder->GetReentrantMonitor().NotifyAll();
 }
 
 nsresult
 MediaDecoderStateMachine::EnqueueDecodeFirstFrameTask()
 {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   MOZ_ASSERT(mState == DECODER_STATE_DECODING_FIRSTFRAME);
 
   nsCOMPtr<nsIRunnable> task(
     NS_NewRunnableMethod(this, &MediaDecoderStateMachine::CallDecodeFirstFrame));
   TaskQueue()->Dispatch(task.forget());
   return NS_OK;
 }
@@ -2036,16 +2045,17 @@ MediaDecoderStateMachine::StartAudioThre
     mAudioSink->SetPlaybackRate(mPlaybackRate);
     mAudioSink->SetPreservesPitch(mPreservesPitch);
   }
   return NS_OK;
 }
 
 int64_t MediaDecoderStateMachine::AudioDecodedUsecs()
 {
+  MOZ_ASSERT(OnTaskQueue());
   NS_ASSERTION(HasAudio(),
                "Should only call AudioDecodedUsecs() when we have audio");
   // The amount of audio we have decoded is the amount of audio data we've
   // already decoded and pushed to the hardware, plus the amount of audio
   // data waiting to be pushed to the hardware.
   int64_t pushed = (mAudioEndTime != -1) ? (mAudioEndTime - GetMediaTime()) : 0;
 
   // Currently for real time streams, AudioQueue().Duration() produce
@@ -2053,41 +2063,45 @@ int64_t MediaDecoderStateMachine::AudioD
   if (IsRealTime()) {
     return pushed + FramesToUsecs(AudioQueue().FrameCount(), mInfo.mAudio.mRate).value();
   }
   return pushed + AudioQueue().Duration();
 }
 
 bool MediaDecoderStateMachine::HasLowDecodedData(int64_t aAudioUsecs)
 {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   MOZ_ASSERT(mReader->UseBufferingHeuristics());
   // We consider ourselves low on decoded data if we're low on audio,
   // provided we've not decoded to the end of the audio stream, or
   // if we're low on video frames, provided
   // we've not decoded to the end of the video stream.
   return ((IsAudioDecoding() && AudioDecodedUsecs() < aAudioUsecs) ||
          (IsVideoDecoding() &&
           static_cast<uint32_t>(VideoQueue().GetSize()) < LOW_VIDEO_FRAMES));
 }
 
 bool MediaDecoderStateMachine::OutOfDecodedAudio()
 {
+    MOZ_ASSERT(OnTaskQueue());
     return IsAudioDecoding() && !AudioQueue().IsFinished() &&
            AudioQueue().GetSize() == 0 &&
            (!mAudioSink || !mAudioSink->HasUnplayedFrames());
 }
 
 bool MediaDecoderStateMachine::HasLowUndecodedData()
 {
+  MOZ_ASSERT(OnTaskQueue());
   return HasLowUndecodedData(mLowDataThresholdUsecs);
 }
 
 bool MediaDecoderStateMachine::HasLowUndecodedData(int64_t aUsecs)
 {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   NS_ASSERTION(mState > DECODER_STATE_DECODING_FIRSTFRAME,
                "Must have loaded first frame for GetBuffered() to work");
 
   // If we don't have a duration, GetBuffered is probably not going to produce
   // a useful buffered range. Return false here so that we don't get stuck in
   // buffering mode for live streams.
   if (GetDuration() < 0) {
@@ -2141,19 +2155,19 @@ MediaDecoderStateMachine::DecodeError()
   nsCOMPtr<nsIRunnable> event =
     NS_NewRunnableMethod(mDecoder, &MediaDecoder::DecodeError);
   AbstractThread::MainThread()->Dispatch(event.forget());
 }
 
 void
 MediaDecoderStateMachine::OnMetadataRead(MetadataHolder* aMetadata)
 {
-  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(mState == DECODER_STATE_DECODING_METADATA);
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   mMetadataRequest.Complete();
 
   mDecoder->SetMediaSeekable(mReader->IsMediaSeekable());
   mInfo = aMetadata->mInfo;
   mMetadataTags = aMetadata->mTags.forget();
 
   if (HasVideo()) {
     DECODER_LOG("Video decode isAsync=%d HWAccel=%d videoQueueSize=%d",
@@ -2181,19 +2195,19 @@ MediaDecoderStateMachine::OnMetadataRead
   SetState(DECODER_STATE_DECODING_FIRSTFRAME);
   EnqueueDecodeFirstFrameTask();
   ScheduleStateMachine();
 }
 
 void
 MediaDecoderStateMachine::OnMetadataNotRead(ReadMetadataFailureReason aReason)
 {
-  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(mState == DECODER_STATE_DECODING_METADATA);
+  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   mMetadataRequest.Complete();
 
   if (aReason == ReadMetadataFailureReason::WAITING_FOR_RESOURCES) {
     SetState(DECODER_STATE_WAIT_FOR_RESOURCES);
   } else {
     MOZ_ASSERT(aReason == ReadMetadataFailureReason::METADATA_ERROR);
     DECODER_WARN("Decode metadata failed, shutting down decoder");
     DecodeError();
@@ -2294,18 +2308,18 @@ MediaDecoderStateMachine::DecodeFirstFra
   }
 
   return NS_OK;
 }
 
 nsresult
 MediaDecoderStateMachine::FinishDecodeFirstFrame()
 {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
-  MOZ_ASSERT(OnTaskQueue());
   DECODER_LOG("FinishDecodeFirstFrame");
 
   if (IsShutdown()) {
     return NS_ERROR_FAILURE;
   }
 
   if (!IsRealTime() && !mSentFirstFrameLoadedEvent) {
     const VideoData* v = VideoQueue().PeekFront();
@@ -2370,32 +2384,32 @@ MediaDecoderStateMachine::FinishDecodeFi
   }
 
   return NS_OK;
 }
 
 void
 MediaDecoderStateMachine::OnSeekCompleted(int64_t aTime)
 {
+  MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  MOZ_ASSERT(OnTaskQueue());
   mSeekRequest.Complete();
 
   // We must decode the first samples of active streams, so we can determine
   // the new stream time. So dispatch tasks to do that.
   mDecodeToSeekTarget = true;
 
   DispatchDecodeTasksIfNeeded();
 }
 
 void
 MediaDecoderStateMachine::OnSeekFailed(nsresult aResult)
 {
+  MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  MOZ_ASSERT(OnTaskQueue());
   mSeekRequest.Complete();
   MOZ_ASSERT(NS_FAILED(aResult), "Cancels should also disconnect mSeekRequest");
   DecodeError();
 }
 
 void
 MediaDecoderStateMachine::SeekCompleted()
 {
@@ -2522,16 +2536,19 @@ MediaDecoderStateMachine::FinishShutdown
 
   // Now that those threads are stopped, there's no possibility of
   // mPendingWakeDecoder being needed again. Revoke it.
   mPendingWakeDecoder = nullptr;
 
   // Disconnect canonicals and mirrors before shutting down our task queue.
   mPlayState.DisconnectIfConnected();
   mNextPlayState.DisconnectIfConnected();
+  mVolume.DisconnectIfConnected();
+  mLogicalPlaybackRate.DisconnectIfConnected();
+  mPreservesPitch.DisconnectIfConnected();
   mNextFrameStatus.DisconnectAll();
 
   // Shut down the watch manager before shutting down our task queue.
   mWatchManager.Shutdown();
 
   MOZ_ASSERT(mState == DECODER_STATE_SHUTDOWN,
              "How did we escape from the shutdown state?");
   // We must daisy-chain these events to destroy the decoder. We must
@@ -2833,26 +2850,28 @@ void MediaDecoderStateMachine::RenderVid
     }
     container->SetCurrentFrame(aData->mDisplay, aData->mImage, aTarget);
     MOZ_ASSERT(container->GetFrameDelay() >= 0 || IsRealTime());
   }
 }
 
 void MediaDecoderStateMachine::ResyncAudioClock()
 {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   if (IsPlaying()) {
     SetPlayStartTime(TimeStamp::Now());
     mPlayDuration = GetAudioClock() - mStartTime;
   }
 }
 
 int64_t
 MediaDecoderStateMachine::GetAudioClock() const
 {
+  MOZ_ASSERT(OnTaskQueue());
   // We must hold the decoder monitor while using the audio stream off the
   // audio sink to ensure that it doesn't get destroyed on the audio sink
   // while we're using it.
   AssertCurrentThreadInMonitor();
   MOZ_ASSERT(HasAudio() && !mAudioCompleted);
   return mAudioStartTime +
          (mAudioSink ? mAudioSink->GetPosition() : 0);
 }
@@ -2869,16 +2888,17 @@ int64_t MediaDecoderStateMachine::GetVid
   int64_t delta = DurationToUsecs(TimeStamp::Now() - mPlayStartTime);
   // Take playback rate into account.
   delta *= mPlaybackRate;
   return mStartTime + mPlayDuration + delta;
 }
 
 int64_t MediaDecoderStateMachine::GetClock() const
 {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
 
   // Determine the clock time. If we've got audio, and we've not reached
   // the end of the audio, use the audio clock. However if we've finished
   // audio, or don't have audio, use the system clock. If our output is being
   // fed to a MediaStream, use that stream as the source of the clock.
   int64_t clock_time = -1;
   if (!IsPlaying()) {
@@ -3048,16 +3068,17 @@ void MediaDecoderStateMachine::AdvanceFr
   } else {
     ScheduleStateMachine();
   }
 }
 
 nsresult
 MediaDecoderStateMachine::DropVideoUpToSeekTarget(VideoData* aSample)
 {
+  MOZ_ASSERT(OnTaskQueue());
   nsRefPtr<VideoData> video(aSample);
   MOZ_ASSERT(video);
   DECODER_LOG("DropVideoUpToSeekTarget() frame [%lld, %lld] dup=%d",
               video->mTime, video->GetEndTime(), video->mDuplicate);
   MOZ_ASSERT(mCurrentSeek.Exists());
   const int64_t target = mCurrentSeek.mTarget.mTime;
 
   // Duplicate handling: if we're dropping frames up the seek target, we must
@@ -3099,16 +3120,17 @@ MediaDecoderStateMachine::DropVideoUpToS
   }
 
   return NS_OK;
 }
 
 nsresult
 MediaDecoderStateMachine::DropAudioUpToSeekTarget(AudioData* aSample)
 {
+  MOZ_ASSERT(OnTaskQueue());
   nsRefPtr<AudioData> audio(aSample);
   MOZ_ASSERT(audio &&
              mCurrentSeek.Exists() &&
              mCurrentSeek.mTarget.mType == SeekTarget::Accurate);
 
   CheckedInt64 startFrame = UsecsToFrames(audio->mTime,
                                           mInfo.mAudio.mRate);
   CheckedInt64 targetFrame = UsecsToFrames(mCurrentSeek.mTarget.mTime,
@@ -3194,17 +3216,18 @@ void MediaDecoderStateMachine::SetStartT
 
   // Set the audio start time to be start of media. If this lies before the
   // first actual audio frame we have, we'll inject silence during playback
   // to ensure the audio starts at the correct time.
   mAudioStartTime = mStartTime;
   DECODER_LOG("Set media start time to %lld", mStartTime);
 }
 
-void MediaDecoderStateMachine::UpdateNextFrameStatus() {
+void MediaDecoderStateMachine::UpdateNextFrameStatus()
+{
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
   MediaDecoderOwner::NextFrameStatus status;
   const char* statusString;
   if (IsBuffering()) {
     status = MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING;
     statusString = "NEXT_FRAME_UNAVAILABLE_BUFFERING";
@@ -3223,16 +3246,17 @@ void MediaDecoderStateMachine::UpdateNex
     DECODER_LOG("Changed mNextFrameStatus to %s", statusString);
   }
 
   mNextFrameStatus = status;
 }
 
 bool MediaDecoderStateMachine::JustExitedQuickBuffering()
 {
+  MOZ_ASSERT(OnTaskQueue());
   return !mDecodeStartTime.IsNull() &&
     mQuickBuffering &&
     (TimeStamp::Now() - mDecodeStartTime) < TimeDuration::FromMicroseconds(QUICK_BUFFER_THRESHOLD_USECS);
 }
 
 void MediaDecoderStateMachine::StartBuffering()
 {
   MOZ_ASSERT(OnTaskQueue());
@@ -3280,17 +3304,19 @@ void MediaDecoderStateMachine::SetPlaySt
   }
   if (!mPlayStartTime.IsNull()) {
     mAudioSink->StartPlayback();
   } else {
     mAudioSink->StopPlayback();
   }
 }
 
-void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder() {
+void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder()
+{
+  MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   DispatchAudioDecodeTaskIfNeeded();
   DispatchVideoDecodeTaskIfNeeded();
 }
 
 void
 MediaDecoderStateMachine::ScheduleStateMachine() {
   AssertCurrentThreadInMonitor();
@@ -3348,62 +3374,53 @@ bool MediaDecoderStateMachine::OnTaskQue
   return TaskQueue()->IsCurrentThreadIn();
 }
 
 bool MediaDecoderStateMachine::IsStateMachineScheduled() const
 {
   return mDispatchedStateMachine || mDelayedScheduler.IsScheduled();
 }
 
-void MediaDecoderStateMachine::SetPlaybackRate(double aPlaybackRate)
+void
+MediaDecoderStateMachine::LogicalPlaybackRateChanged()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
-  NS_ASSERTION(aPlaybackRate != 0,
-      "PlaybackRate == 0 should be handled before this function.");
+  MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
-  if (mPlaybackRate == aPlaybackRate) {
+  if (mLogicalPlaybackRate == 0) {
+    // This case is handled in MediaDecoder by pausing playback.
     return;
   }
 
   // AudioStream will handle playback rate change when we have audio.
   // Do nothing while we are not playing. Change in playback rate will
   // take effect next time we start playing again.
   if (!HasAudio() && IsPlaying()) {
     // Remember how much time we've spent in playing the media
     // for playback rate will change from now on.
     mPlayDuration = GetVideoStreamPosition() - mStartTime;
     SetPlayStartTime(TimeStamp::Now());
   }
 
-  mPlaybackRate = aPlaybackRate;
+  mPlaybackRate = mLogicalPlaybackRate;
   if (mAudioSink) {
     mAudioSink->SetPlaybackRate(mPlaybackRate);
   }
 }
 
-void MediaDecoderStateMachine::SetPreservesPitch(bool aPreservesPitch)
+void MediaDecoderStateMachine::PreservesPitchChanged()
 {
-  NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
+  MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
-  mPreservesPitch = aPreservesPitch;
   if (mAudioSink) {
     mAudioSink->SetPreservesPitch(mPreservesPitch);
   }
 }
 
-void
-MediaDecoderStateMachine::SetMinimizePrerollUntilPlaybackStarts()
-{
-  AssertCurrentThreadInMonitor();
-  DECODER_LOG("SetMinimizePrerollUntilPlaybackStarts()");
-  mMinimizePreroll = true;
-}
-
 bool MediaDecoderStateMachine::IsShutdown()
 {
   AssertCurrentThreadInMonitor();
   return mState == DECODER_STATE_ERROR ||
          mState == DECODER_STATE_SHUTDOWN;
 }
 
 void MediaDecoderStateMachine::QueueMetadata(int64_t aPublishTime,
@@ -3466,16 +3483,17 @@ void MediaDecoderStateMachine::OnAudioSi
 
   // Otherwise notify media decoder/element about this error for it makes
   // no sense to play an audio-only file without sound output.
   DecodeError();
 }
 
 uint32_t MediaDecoderStateMachine::GetAmpleVideoFrames() const
 {
+  MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   return (mReader->IsAsync() && mReader->VideoIsHardwareAccelerated())
     ? std::max<uint32_t>(sVideoQueueHWAccelSize, MIN_VIDEO_QUEUE_SIZE)
     : std::max<uint32_t>(sVideoQueueDefaultSize, MIN_VIDEO_QUEUE_SIZE);
 }
 
 } // namespace mozilla
 
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -147,20 +147,30 @@ public:
     DECODER_STATE_ERROR
   };
 
   State GetState() {
     AssertCurrentThreadInMonitor();
     return mState;
   }
 
-  // Set the audio volume. The decoder monitor must be obtained before
-  // calling this.
-  void SetVolume(double aVolume);
-  void SetAudioCaptured();
+  void DispatchAudioCaptured()
+  {
+    nsRefPtr<MediaDecoderStateMachine> self = this;
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () -> void
+    {
+      MOZ_ASSERT(self->OnTaskQueue());
+      ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
+      if (!self->mAudioCaptured) {
+        self->mAudioCaptured = true;
+        self->ScheduleStateMachine();
+      }
+    });
+    TaskQueue()->Dispatch(r.forget());
+  }
 
   // Check if the decoder needs to become dormant state.
   bool IsDormantNeeded();
   // Set/Unset dormant state.
   void SetDormant(bool aDormant);
 
 private:
   // Initialization that needs to happen on the task queue. This is the first
@@ -304,19 +314,16 @@ public:
     ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
     if (mStartTime < 0) {
       return NS_OK;
     }
 
     return mReader->GetBuffered(aBuffered);
   }
 
-  void SetPlaybackRate(double aPlaybackRate);
-  void SetPreservesPitch(bool aPreservesPitch);
-
   size_t SizeOfVideoQueue() {
     if (mReader) {
       return mReader->SizeOfVideoQueueInBytes();
     }
     return 0;
   }
 
   size_t SizeOfAudioQueue() {
@@ -387,17 +394,31 @@ public:
   // to begin decoding.
   void NotifyWaitingForResourcesStatusChanged();
 
   // Notifies the state machine that should minimize the number of samples
   // decoded we preroll, until playback starts. The first time playback starts
   // the state machine is free to return to prerolling normally. Note
   // "prerolling" in this context refers to when we decode and buffer decoded
   // samples in advance of when they're needed for playback.
-  void SetMinimizePrerollUntilPlaybackStarts();
+  void DispatchMinimizePrerollUntilPlaybackStarts()
+  {
+    nsRefPtr<MediaDecoderStateMachine> self = this;
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () -> void
+    {
+      MOZ_ASSERT(self->OnTaskQueue());
+      ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
+      self->mMinimizePreroll = true;
+
+      // Make sure that this arrives before playback starts, otherwise this won't
+      // have the intended effect.
+      MOZ_DIAGNOSTIC_ASSERT(self->mPlayState == MediaDecoder::PLAY_STATE_LOADING);
+    });
+    TaskQueue()->Dispatch(r.forget());
+  }
 
   void OnAudioDecoded(AudioData* aSample);
   void OnVideoDecoded(VideoData* aSample);
   void OnNotDecoded(MediaData::Type aType, MediaDecoderReader::NotDecodedReason aReason);
   void OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
   {
     OnNotDecoded(MediaData::AUDIO_DATA, aReason);
   }
@@ -442,16 +463,19 @@ protected:
 
   // Pops MediaData* samples from their respective MediaQueues.
   // Note that the audio queue is also drained on the audio thread,
   // which we can't easily react to - This should be fixed when we
   // remove the audio thread in bug 750596.
   already_AddRefed<AudioData> PopAudio();
   already_AddRefed<VideoData> PopVideo();
 
+  void VolumeChanged();
+  void LogicalPlaybackRateChanged();
+  void PreservesPitchChanged();
 
   class WakeDecoderRunnable : public nsRunnable {
   public:
     explicit WakeDecoderRunnable(MediaDecoderStateMachine* aSM)
       : mMutex("WakeDecoderRunnable"), mStateMachine(aSM) {}
     NS_IMETHOD Run() override
     {
       nsRefPtr<MediaDecoderStateMachine> stateMachine;
@@ -1004,27 +1028,29 @@ protected:
   // The presentation end time of the last video frame which has been displayed
   // in microseconds. Accessed from the state machine thread.
   int64_t mVideoFrameEndTime;
 
   // The end time of the last decoded video frame. Used to check if we are low
   // on decoded video data.
   int64_t mDecodedVideoEndTime;
 
-  // Volume of playback. 0.0 = muted. 1.0 = full volume. Read/Written
-  // from the state machine and main threads. Synchronised via decoder
-  // monitor.
-  double mVolume;
+  // Volume of playback. 0.0 = muted. 1.0 = full volume.
+  Mirror<double> mVolume;
 
-  // Playback rate. 1.0 : normal speed, 0.5 : two times slower. Synchronized via
-  // decoder monitor.
+  // Playback rate. 1.0 : normal speed, 0.5 : two times slower.
+  //
+  // The separation between mPlaybackRate and mLogicalPlaybackRate is a kludge
+  // to preserve existing fragile logic while converting this setup to state-
+  // mirroring. Some hero should clean this up.
   double mPlaybackRate;
+  Mirror<double> mLogicalPlaybackRate;
 
-  // Pitch preservation for the playback rate. Synchronized via decoder monitor.
-  bool mPreservesPitch;
+  // Pitch preservation for the playback rate.
+  Mirror<bool> mPreservesPitch;
 
   // Time at which we started decoding. Synchronised via decoder monitor.
   TimeStamp mDecodeStartTime;
 
   // The maximum number of second we spend buffering when we are short on
   // unbuffered data.
   uint32_t mBufferingWait;
   int64_t  mLowDataThresholdUsecs;
@@ -1075,22 +1101,24 @@ protected:
 
   uint32_t VideoPrerollFrames() const
   {
     return IsRealTime() ? 0 : GetAmpleVideoFrames() / 2;
   }
 
   bool DonePrerollingAudio()
   {
+    MOZ_ASSERT(OnTaskQueue());
     AssertCurrentThreadInMonitor();
     return !IsAudioDecoding() || GetDecodedAudioDuration() >= AudioPrerollUsecs() * mPlaybackRate;
   }
 
   bool DonePrerollingVideo()
   {
+    MOZ_ASSERT(OnTaskQueue());
     AssertCurrentThreadInMonitor();
     return !IsVideoDecoding() ||
            static_cast<uint32_t>(VideoQueue().GetSize()) >= VideoPrerollFrames() * mPlaybackRate;
   }
 
   void StopPrerollingAudio()
   {
     AssertCurrentThreadInMonitor();
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -40,19 +40,24 @@ function GlobalPCList() {
   this._list = {};
   this._networkdown = false; // XXX Need to query current state somehow
   this._lifecycleobservers = {};
   Services.obs.addObserver(this, "inner-window-destroyed", true);
   Services.obs.addObserver(this, "profile-change-net-teardown", true);
   Services.obs.addObserver(this, "network:offline-about-to-go-offline", true);
   Services.obs.addObserver(this, "network:offline-status-changed", true);
   Services.obs.addObserver(this, "gmp-plugin-crash", true);
+  if (Cc["@mozilla.org/childprocessmessagemanager;1"]) {
+    let mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsIMessageListenerManager);
+    mm.addMessageListener("gmp-plugin-crash", this);
+  }
 }
 GlobalPCList.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
+                                         Ci.nsIMessageListener,
                                          Ci.nsISupportsWeakReference,
                                          Ci.IPeerConnectionManager]),
   classID: PC_MANAGER_CID,
   _xpcom_factory: {
     createInstance: function(outer, iid) {
       if (outer) {
         throw Cr.NS_ERROR_NO_AGGREGATION;
       }
@@ -88,16 +93,41 @@ GlobalPCList.prototype = {
     }
   },
 
   hasActivePeerConnection: function(winID) {
     this.removeNullRefs(winID);
     return this._list[winID] ? true : false;
   },
 
+  handleGMPCrash: function(data) {
+    let broadcastPluginCrash = function(list, winID, pluginID, pluginName) {
+      if (list.hasOwnProperty(winID)) {
+        list[winID].forEach(function(pcref) {
+          let pc = pcref.get();
+          if (pc) {
+            pc._pc.pluginCrash(pluginID, pluginName);
+          }
+        });
+      }
+    };
+
+    // a plugin crashed; if it's associated with any of our PCs, fire an
+    // event to the DOM window
+    for (let winId in this._list) {
+      broadcastPluginCrash(this._list, winId, data.pluginID, data.pluginName);
+    }
+  },
+
+  receiveMessage: function(message) {
+    if (message.name == "gmp-plugin-crash") {
+      this.handleGMPCrash(message.data);
+    }
+  },
+
   observe: function(subject, topic, data) {
     let cleanupPcRef = function(pcref) {
       let pc = pcref.get();
       if (pc) {
         pc._pc.close();
         delete pc._observer;
         pc._pc = null;
       }
@@ -105,27 +135,16 @@ GlobalPCList.prototype = {
 
     let cleanupWinId = function(list, winID) {
       if (list.hasOwnProperty(winID)) {
         list[winID].forEach(cleanupPcRef);
         delete list[winID];
       }
     };
 
-    let broadcastPluginCrash = function(list, winID, pluginID, name, crashReportID) {
-      if (list.hasOwnProperty(winID)) {
-        list[winID].forEach(function(pcref) {
-          let pc = pcref.get();
-          if (pc) {
-            pc._pc.pluginCrash(pluginID, name, crashReportID);
-          }
-        });
-      }
-    };
-
     if (topic == "inner-window-destroyed") {
       let winID = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
       cleanupWinId(this._list, winID);
 
       if (this._lifecycleobservers.hasOwnProperty(winID)) {
         delete this._lifecycleobservers[winID];
       }
     } else if (topic == "profile-change-net-teardown" ||
@@ -157,27 +176,21 @@ GlobalPCList.prototype = {
         if (appId != this._list[winId]._appId) {
           continue;
         }
         if (ios.isAppOffline(appId)) {
           cleanupWinId(this._list, winId);
         }
       }
     } else if (topic == "gmp-plugin-crash") {
-      // a plugin crashed; if it's associated with any of our PCs, fire an
-      // event to the DOM window
-      let sep = data.indexOf(' ');
-      let pluginId = data.slice(0, sep);
-      let rest = data.slice(sep+1);
-      // This presumes no spaces in the name!
-      sep = rest.indexOf(' ');
-      let name = rest.slice(0, sep);
-      let crashId = rest.slice(sep+1);
-      for (let winId in this._list) {
-        broadcastPluginCrash(this._list, winId, pluginId, name, crashId);
+      if (subject instanceof Ci.nsIWritablePropertyBag2) {
+        let pluginID = subject.getPropertyAsUint32("pluginID");
+        let pluginName = subject.getPropertyAsAString("pluginName");
+        let data = { pluginID, pluginName };
+        this.handleGMPCrash(data);
       }
     }
   },
 
   _registerPeerConnectionLifecycleCallback: function(winID, cb) {
     this._lifecycleobservers[winID] = cb;
   },
 };
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -372,32 +372,33 @@ MediaKeys::Init(ErrorResult& aRv)
                inPrivateBrowsing);
 
   return promise.forget();
 }
 
 class CrashHandler : public gmp::GeckoMediaPluginService::PluginCrashCallback
 {
 public:
-  CrashHandler(const nsACString& aPluginId,
+  CrashHandler(const uint32_t aPluginId,
                nsPIDOMWindow* aParentWindow,
                nsIDocument* aDocument)
     : gmp::GeckoMediaPluginService::PluginCrashCallback(aPluginId)
+    , mPluginId(aPluginId)
     , mParentWindowWeakPtr(do_GetWeakReference(aParentWindow))
     , mDocumentWeakPtr(do_GetWeakReference(aDocument))
   {
   }
 
-  virtual void Run(const nsACString& aPluginName, const nsAString& aPluginDumpId) override
+  virtual void Run(const nsACString& aPluginName) override
   {
     PluginCrashedEventInit init;
+    init.mPluginID = mPluginId;
     init.mBubbles = true;
     init.mCancelable = true;
     init.mGmpPlugin = true;
-    init.mPluginDumpID = aPluginDumpId;
     CopyUTF8toUTF16(aPluginName, init.mPluginName);
     init.mSubmittedCrashReport = false;
 
     // The following PluginCrashedEvent fields stay empty:
     // init.mBrowserDumpID
     // init.mPluginFilename
     // TODO: Can/should we fill them?
 
@@ -440,22 +441,23 @@ private:
     }
     nsCOMPtr<nsIDocument> parentWindowDocument = parentWindow->GetExtantDoc();
     if (!parentWindowDocument || document.get() != parentWindowDocument.get()) {
       return false;
     }
     return true;
   }
 
+  uint32_t mPluginId;
   nsWeakPtr mParentWindowWeakPtr;
   nsWeakPtr mDocumentWeakPtr;
 };
 
 void
-MediaKeys::OnCDMCreated(PromiseId aId, const nsACString& aNodeId, const nsACString& aPluginId)
+MediaKeys::OnCDMCreated(PromiseId aId, const nsACString& aNodeId, const uint32_t aPluginId)
 {
   nsRefPtr<Promise> promise(RetrievePromise(aId));
   if (!promise) {
     return;
   }
   mNodeId = aNodeId;
   nsRefPtr<MediaKeys> keys(this);
   EME_LOG("MediaKeys[%p]::OnCDMCreated() resolve promise id=%d", this, aId);
@@ -463,33 +465,33 @@ MediaKeys::OnCDMCreated(PromiseId aId, c
   if (mCreatePromiseId == aId) {
     Release();
   }
 
   MediaKeySystemAccess::NotifyObservers(mParent,
                                         mKeySystem,
                                         MediaKeySystemStatus::Cdm_created);
 
-  if (!aPluginId.IsEmpty()) {
+  if (aPluginId) {
     // Prepare plugin crash reporter.
     nsRefPtr<gmp::GeckoMediaPluginService> service =
       gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
     if (NS_WARN_IF(!service)) {
       return;
     }
     if (NS_WARN_IF(!mParent)) {
       return;
     }
     nsCOMPtr<nsIDocument> doc = mParent->GetExtantDoc();
     if (NS_WARN_IF(!doc)) {
       return;
     }
     service->AddPluginCrashCallback(new CrashHandler(aPluginId, mParent, doc));
-    EME_LOG("MediaKeys[%p]::OnCDMCreated() registered crash handler for pluginId '%s'",
-            this, aPluginId.Data());
+    EME_LOG("MediaKeys[%p]::OnCDMCreated() registered crash handler for pluginId '%i'",
+            this, aPluginId);
   }
 }
 
 already_AddRefed<MediaKeySession>
 MediaKeys::CreateSession(JSContext* aCx,
                          SessionType aSessionType,
                          ErrorResult& aRv)
 {
--- a/dom/media/eme/MediaKeys.h
+++ b/dom/media/eme/MediaKeys.h
@@ -76,17 +76,17 @@ public:
   already_AddRefed<MediaKeySession> GetSession(const nsAString& aSessionId);
 
   // Removes and returns MediaKeySession from the set of sessions awaiting
   // their sessionId to be assigned.
   already_AddRefed<MediaKeySession> GetPendingSession(uint32_t aToken);
 
   // Called once a Init() operation succeeds.
   void OnCDMCreated(PromiseId aId,
-                    const nsACString& aNodeId, const nsACString& aPluginId);
+                    const nsACString& aNodeId, const uint32_t aPluginId);
 
   // Called once the CDM generates a sessionId while servicing a
   // MediaKeySession.generateRequest() or MediaKeySession.load() call,
   // once the sessionId of a MediaKeySession is known.
   void OnSessionIdReady(MediaKeySession* aSession);
 
   // Called once a LoadSession succeeds.
   void OnSessionLoaded(PromiseId aId, bool aSuccess);
--- a/dom/media/gmp/GMPContentParent.h
+++ b/dom/media/gmp/GMPContentParent.h
@@ -46,21 +46,21 @@ public:
   void SetDisplayName(const nsCString& aDisplayName)
   {
     mDisplayName = aDisplayName;
   }
   const nsCString& GetDisplayName()
   {
     return mDisplayName;
   }
-  void SetPluginId(const nsCString& aPluginId)
+  void SetPluginId(const uint32_t aPluginId)
   {
     mPluginId = aPluginId;
   }
-  const nsCString& GetPluginId()
+  const uint32_t GetPluginId()
   {
     return mPluginId;
   }
 
 private:
   ~GMPContentParent();
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
@@ -87,15 +87,15 @@ private:
 
   nsTArray<nsRefPtr<GMPVideoDecoderParent>> mVideoDecoders;
   nsTArray<nsRefPtr<GMPVideoEncoderParent>> mVideoEncoders;
   nsTArray<nsRefPtr<GMPDecryptorParent>> mDecryptors;
   nsTArray<nsRefPtr<GMPAudioDecoderParent>> mAudioDecoders;
   nsCOMPtr<nsIThread> mGMPThread;
   nsRefPtr<GMPParent> mParent;
   nsCString mDisplayName;
-  nsCString mPluginId;
+  uint32_t mPluginId;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPParent_h_
--- a/dom/media/gmp/GMPDecryptorParent.cpp
+++ b/dom/media/gmp/GMPDecryptorParent.cpp
@@ -24,17 +24,17 @@ GMPDecryptorParent::GMPDecryptorParent(G
   MOZ_ASSERT(mPlugin && mGMPThread);
   mPluginId = aPlugin->GetPluginId();
 }
 
 GMPDecryptorParent::~GMPDecryptorParent()
 {
 }
 
-const nsACString&
+const uint32_t
 GMPDecryptorParent::GetPluginId() const
 {
   return mPluginId;
 }
 
 nsresult
 GMPDecryptorParent::Init(GMPDecryptorProxyCallback* aCallback)
 {
--- a/dom/media/gmp/GMPDecryptorParent.h
+++ b/dom/media/gmp/GMPDecryptorParent.h
@@ -24,17 +24,17 @@ class GMPDecryptorParent final : public 
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(GMPDecryptorParent)
 
   explicit GMPDecryptorParent(GMPContentParent *aPlugin);
 
   // GMPDecryptorProxy
 
-  virtual const nsACString& GetPluginId() const override;
+  virtual const uint32_t GetPluginId() const override;
 
   virtual nsresult Init(GMPDecryptorProxyCallback* aCallback) override;
 
   virtual void CreateSession(uint32_t aCreateSessionToken,
                              uint32_t aPromiseId,
                              const nsCString& aInitDataType,
                              const nsTArray<uint8_t>& aInitData,
                              GMPSessionType aSessionType) override;
@@ -108,17 +108,17 @@ private:
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
   virtual bool Recv__delete__() override;
 
   bool mIsOpen;
   bool mShuttingDown;
   bool mActorDestroyed;
   nsRefPtr<GMPContentParent> mPlugin;
-  nsCString mPluginId;
+  uint32_t mPluginId;
   GMPDecryptorProxyCallback* mCallback;
 #ifdef DEBUG
   nsIThread* const mGMPThread;
 #endif
 };
 
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/gmp/GMPDecryptorProxy.h
+++ b/dom/media/gmp/GMPDecryptorProxy.h
@@ -54,17 +54,17 @@ public:
                          GMPErr aResult,
                          const nsTArray<uint8_t>& aDecryptedData) = 0;
 };
 
 class GMPDecryptorProxy {
 public:
   ~GMPDecryptorProxy() {}
 
-  virtual const nsACString& GetPluginId() const = 0;
+  virtual const uint32_t GetPluginId() const = 0;
 
   virtual nsresult Init(GMPDecryptorProxyCallback* aCallback) = 0;
 
   virtual void CreateSession(uint32_t aCreateSessionToken,
                              uint32_t aPromiseId,
                              const nsCString& aInitDataType,
                              const nsTArray<uint8_t>& aInitData,
                              GMPSessionType aSessionType) = 0;
--- a/dom/media/gmp/GMPParent.cpp
+++ b/dom/media/gmp/GMPParent.cpp
@@ -8,28 +8,31 @@
 #include "nsComponentManagerUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIInputStream.h"
 #include "nsILineInputStream.h"
 #include "nsNetUtil.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsThreadUtils.h"
 #include "nsIRunnable.h"
+#include "nsIWritablePropertyBag2.h"
 #include "mozIGeckoMediaPluginService.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/SyncRunnable.h"
 #include "mozilla/unused.h"
 #include "nsIObserverService.h"
 #include "GMPTimerParent.h"
 #include "runnable_utils.h"
 #if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
 #include "mozilla/SandboxInfo.h"
 #endif
 
 #include "mozilla/dom/CrashReporterParent.h"
 using mozilla::dom::CrashReporterParent;
+using mozilla::ipc::GeckoChildProcessHost;
 
 #ifdef MOZ_CRASHREPORTER
 using CrashReporter::AnnotationTable;
 using CrashReporter::GetIDFromMinidump;
 #endif
 
 #include "mozilla/Telemetry.h"
 
@@ -59,19 +62,17 @@ GMPParent::GMPParent()
   , mGMPContentChildCount(0)
   , mAsyncShutdownRequired(false)
   , mAsyncShutdownInProgress(false)
 #ifdef PR_LOGGING
   , mChildPid(0)
 #endif
 {
   LOGD("GMPParent ctor");
-  // Use the parent address to identify it.
-  // We could use any unique-to-the-parent value.
-  mPluginId.AppendInt(reinterpret_cast<uint64_t>(this));
+  mPluginId = GeckoChildProcessHost::GetUniqueID();
 }
 
 GMPParent::~GMPParent()
 {
   // Can't Close or Destroy the process here, since destruction is MainThread only
   MOZ_ASSERT(NS_IsMainThread());
   LOGD("GMPParent dtor");
 }
@@ -494,33 +495,32 @@ GMPParent::GetCrashID(nsString& aResult)
     AppendUTF8toUTF16(mVersion, aResult);
     return;
   }
   GetIDFromMinidump(dumpFile, aResult);
   cr->GenerateCrashReportForMinidump(dumpFile, &notes);
 }
 
 static void
-GMPNotifyObservers(const nsACString& aPluginId, const nsACString& aPluginName, const nsAString& aPluginDumpId)
+GMPNotifyObservers(const uint32_t aPluginID, const nsACString& aPluginName, const nsAString& aPluginDumpID)
 {
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
-  if (obs) {
-    nsString id;
-    AppendUTF8toUTF16(aPluginId, id);
-    id.Append(NS_LITERAL_STRING(" "));
-    AppendUTF8toUTF16(aPluginName, id);
-    id.Append(NS_LITERAL_STRING(" "));
-    id.Append(aPluginDumpId);
-    obs->NotifyObservers(nullptr, "gmp-plugin-crash", id.Data());
+  nsCOMPtr<nsIWritablePropertyBag2> propbag =
+    do_CreateInstance("@mozilla.org/hash-property-bag;1");
+  if (obs && propbag) {
+    propbag->SetPropertyAsUint32(NS_LITERAL_STRING("pluginID"), aPluginID);
+    propbag->SetPropertyAsACString(NS_LITERAL_STRING("pluginName"), aPluginName);
+    propbag->SetPropertyAsAString(NS_LITERAL_STRING("pluginDumpID"), aPluginDumpID);
+    obs->NotifyObservers(propbag, "gmp-plugin-crash", nullptr);
   }
 
   nsRefPtr<gmp::GeckoMediaPluginService> service =
     gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
   if (service) {
-    service->RunPluginCrashCallbacks(aPluginId, aPluginName, aPluginDumpId);
+    service->RunPluginCrashCallbacks(aPluginID, aPluginName);
   }
 }
 #endif
 void
 GMPParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   LOGD("%s: (%d)", __FUNCTION__, (int)aWhy);
 #ifdef MOZ_CRASHREPORTER
@@ -815,17 +815,17 @@ GMPParent::GetDisplayName() const
 }
 
 const nsCString&
 GMPParent::GetVersion() const
 {
   return mVersion;
 }
 
-const nsCString&
+const uint32_t
 GMPParent::GetPluginId() const
 {
   return mPluginId;
 }
 
 bool
 GMPParent::RecvAsyncShutdownRequired()
 {
--- a/dom/media/gmp/GMPParent.h
+++ b/dom/media/gmp/GMPParent.h
@@ -118,17 +118,17 @@ public:
   // be shared across NodeIds.
 
   // Specifies that a GMP can only work with the specified NodeIds.
   void SetNodeId(const nsACString& aNodeId);
   const nsACString& GetNodeId() const { return mNodeId; }
 
   const nsCString& GetDisplayName() const;
   const nsCString& GetVersion() const;
-  const nsCString& GetPluginId() const;
+  const uint32_t GetPluginId() const;
 
   // Returns true if a plugin can be or is being used across multiple NodeIds.
   bool CanBeSharedCrossNodeIds() const;
 
   // A GMP can be used from a NodeId if it's already been set to work with
   // that NodeId, or if it's not been set to work with any NodeId and has
   // not yet been loaded (i.e. it's not shared across NodeIds).
   bool CanBeUsedFrom(const nsACString& aNodeId) const;
@@ -187,17 +187,17 @@ private:
   nsresult EnsureAsyncShutdownTimeoutSet();
 
   GMPState mState;
   nsCOMPtr<nsIFile> mDirectory; // plugin directory on disk
   nsString mName; // base name of plugin on disk, UTF-16 because used for paths
   nsCString mDisplayName; // name of plugin displayed to users
   nsCString mDescription; // description of plugin for display to users
   nsCString mVersion;
-  nsCString mPluginId;
+  uint32_t mPluginId;
   nsTArray<nsAutoPtr<GMPCapability>> mCapabilities;
   GMPProcessParent* mProcess;
   bool mDeleteProcessOnlyOnUnload;
   bool mAbnormalShutdownInProgress;
   bool mIsBlockingDeletion;
 
   bool mCanDecrypt;
 
--- a/dom/media/gmp/GMPService.cpp
+++ b/dom/media/gmp/GMPService.cpp
@@ -160,63 +160,60 @@ GeckoMediaPluginService::~GeckoMediaPlug
 
 void
 GeckoMediaPluginService::RemoveObsoletePluginCrashCallbacks()
 {
   MOZ_ASSERT(NS_IsMainThread());
   for (size_t i = mPluginCrashCallbacks.Length(); i != 0; --i) {
     nsRefPtr<PluginCrashCallback>& callback = mPluginCrashCallbacks[i - 1];
     if (!callback->IsStillValid()) {
-      LOGD(("%s::%s - Removing obsolete callback for pluginId %s",
-            __CLASS__, __FUNCTION__,
-            PromiseFlatCString(callback->PluginId()).get()));
+      LOGD(("%s::%s - Removing obsolete callback for pluginId %i",
+            __CLASS__, __FUNCTION__, callback->PluginId()));
       mPluginCrashCallbacks.RemoveElementAt(i - 1);
     }
   }
 }
 
 void
 GeckoMediaPluginService::AddPluginCrashCallback(
   nsRefPtr<PluginCrashCallback> aPluginCrashCallback)
 {
   RemoveObsoletePluginCrashCallbacks();
   mPluginCrashCallbacks.AppendElement(aPluginCrashCallback);
 }
 
 void
-GeckoMediaPluginService::RemovePluginCrashCallbacks(const nsACString& aPluginId)
+GeckoMediaPluginService::RemovePluginCrashCallbacks(const uint32_t aPluginId)
 {
   RemoveObsoletePluginCrashCallbacks();
   for (size_t i = mPluginCrashCallbacks.Length(); i != 0; --i) {
     nsRefPtr<PluginCrashCallback>& callback = mPluginCrashCallbacks[i - 1];
     if (callback->PluginId() == aPluginId) {
       mPluginCrashCallbacks.RemoveElementAt(i - 1);
     }
   }
 }
 
 void
-GeckoMediaPluginService::RunPluginCrashCallbacks(const nsACString& aPluginId,
-                                                 const nsACString& aPluginName,
-                                                 const nsAString& aPluginDumpId)
+GeckoMediaPluginService::RunPluginCrashCallbacks(const uint32_t aPluginId,
+                                                 const nsACString& aPluginName)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  LOGD(("%s::%s(%s)", __CLASS__, __FUNCTION__, aPluginId.Data()));
+  LOGD(("%s::%s(%i)", __CLASS__, __FUNCTION__, aPluginId));
   for (size_t i = mPluginCrashCallbacks.Length(); i != 0; --i) {
     nsRefPtr<PluginCrashCallback>& callback = mPluginCrashCallbacks[i - 1];
-    const nsACString& callbackPluginId = callback->PluginId();
+    const uint32_t callbackPluginId = callback->PluginId();
     if (!callback->IsStillValid()) {
-      LOGD(("%s::%s(%s) - Removing obsolete callback for pluginId %s",
-            __CLASS__, __FUNCTION__, aPluginId.Data(),
-            PromiseFlatCString(callback->PluginId()).get()));
+      LOGD(("%s::%s(%i) - Removing obsolete callback for pluginId %i",
+            __CLASS__, __FUNCTION__, aPluginId, callback->PluginId()));
       mPluginCrashCallbacks.RemoveElementAt(i - 1);
     } else if (callbackPluginId == aPluginId) {
-      LOGD(("%s::%s(%s) - Running #%u",
-          __CLASS__, __FUNCTION__, aPluginId.Data(), i - 1));
-      callback->Run(aPluginName, aPluginDumpId);
+      LOGD(("%s::%s(%i) - Running #%u",
+          __CLASS__, __FUNCTION__, aPluginId, i - 1));
+      callback->Run(aPluginName);
       mPluginCrashCallbacks.RemoveElementAt(i - 1);
     }
   }
 }
 
 nsresult
 GeckoMediaPluginService::Init()
 {
--- a/dom/media/gmp/GMPService.h
+++ b/dom/media/gmp/GMPService.h
@@ -61,38 +61,37 @@ public:
 
   int32_t AsyncShutdownTimeoutMs();
 
   class PluginCrashCallback
   {
   public:
     NS_INLINE_DECL_REFCOUNTING(PluginCrashCallback)
 
-    PluginCrashCallback(const nsACString& aPluginId)
+    PluginCrashCallback(const uint32_t aPluginId)
       : mPluginId(aPluginId)
     {
       MOZ_ASSERT(NS_IsMainThread());
     }
-    const nsACString& PluginId() const { return mPluginId; }
-    virtual void Run(const nsACString& aPluginName, const nsAString& aPluginDumpId) = 0;
+    const uint32_t PluginId() const { return mPluginId; }
+    virtual void Run(const nsACString& aPluginName) = 0;
     virtual bool IsStillValid() = 0; // False if callback has become useless.
   protected:
     virtual ~PluginCrashCallback()
     {
       MOZ_ASSERT(NS_IsMainThread());
     }
   private:
-    const nsCString mPluginId;
+    const uint32_t mPluginId;
   };
   void RemoveObsoletePluginCrashCallbacks(); // Called from add/remove/run.
   void AddPluginCrashCallback(nsRefPtr<PluginCrashCallback> aPluginCrashCallback);
-  void RemovePluginCrashCallbacks(const nsACString& aPluginId);
-  void RunPluginCrashCallbacks(const nsACString& aPluginId,
-                               const nsACString& aPluginName,
-                               const nsAString& aPluginDumpId);
+  void RemovePluginCrashCallbacks(const uint32_t aPluginId);
+  void RunPluginCrashCallbacks(const uint32_t aPluginId,
+                               const nsACString& aPluginName);
 
 protected:
   GeckoMediaPluginService();
   virtual ~GeckoMediaPluginService();
 
   virtual void InitializePlugins() = 0;
   virtual bool GetContentParentFrom(const nsACString& aNodeId,
                                     const nsCString& aAPI,
--- a/dom/media/gmp/GMPServiceChild.cpp
+++ b/dom/media/gmp/GMPServiceChild.cpp
@@ -77,17 +77,17 @@ public:
       return;
     }
 
     nsTArray<base::ProcessId> alreadyBridgedTo;
     aGMPServiceChild->GetAlreadyBridgedTo(alreadyBridgedTo);
 
     base::ProcessId otherProcess;
     nsCString displayName;
-    nsCString pluginId;
+    uint32_t pluginId;
     bool ok = aGMPServiceChild->SendLoadGMP(mNodeId, mAPI, mTags,
                                             alreadyBridgedTo, &otherProcess,
                                             &displayName, &pluginId);
     if (!ok) {
       mCallback->Done(nullptr);
       return;
     }
 
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -1430,17 +1430,17 @@ GMPServiceParent::~GMPServiceParent()
 
 bool
 GMPServiceParent::RecvLoadGMP(const nsCString& aNodeId,
                               const nsCString& aAPI,
                               nsTArray<nsCString>&& aTags,
                               nsTArray<ProcessId>&& aAlreadyBridgedTo,
                               ProcessId* aId,
                               nsCString* aDisplayName,
-                              nsCString* aPluginId)
+                              uint32_t* aPluginId)
 {
   nsRefPtr<GMPParent> gmp = mService->SelectPluginForAPI(aNodeId, aAPI, aTags);
 
 #ifdef PR_LOGGING
   nsCString api = aTags[0];
   LOGD(("%s: %p returning %p for api %s", __FUNCTION__, (void *)this, (void *)gmp, api.get()));
 #endif
 
--- a/dom/media/gmp/GMPServiceParent.h
+++ b/dom/media/gmp/GMPServiceParent.h
@@ -188,17 +188,17 @@ public:
   virtual ~GMPServiceParent();
 
   virtual bool RecvLoadGMP(const nsCString& aNodeId,
                            const nsCString& aApi,
                            nsTArray<nsCString>&& aTags,
                            nsTArray<ProcessId>&& aAlreadyBridgedTo,
                            base::ProcessId* aID,
                            nsCString* aDisplayName,
-                           nsCString* aPluginId) override;
+                           uint32_t* aPluginId) override;
   virtual bool RecvGetGMPNodeId(const nsString& aOrigin,
                                 const nsString& aTopLevelOrigin,
                                 const bool& aInPrivateBrowsing,
                                 nsCString* aID) override;
   static bool RecvGetGMPPluginVersionForAPI(const nsCString& aAPI,
                                             nsTArray<nsCString>&& aTags,
                                             bool* aHasPlugin,
                                             nsCString* aVersion);
--- a/dom/media/gmp/GMPVideoEncoderParent.cpp
+++ b/dom/media/gmp/GMPVideoEncoderParent.cpp
@@ -211,16 +211,22 @@ GMPVideoEncoderParent::SetPeriodicKeyFra
   if (!SendSetPeriodicKeyFrames(aEnable)) {
     return GMPGenericErr;
   }
 
   // Async IPC, we don't have access to a return value.
   return GMPNoErr;
 }
 
+const uint32_t
+GMPVideoEncoderParent::ParentID()
+{
+  return mPlugin ? mPlugin->GetPluginId() : 0;
+}
+
 // Note: Consider keeping ActorDestroy sync'd up when making changes here.
 void
 GMPVideoEncoderParent::Shutdown()
 {
   LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
   MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
 
   if (mShuttingDown) {
--- a/dom/media/gmp/GMPVideoEncoderParent.h
+++ b/dom/media/gmp/GMPVideoEncoderParent.h
@@ -40,17 +40,17 @@ public:
                             int32_t aNumberOfCores,
                             uint32_t aMaxPayloadSize) override;
   virtual GMPErr Encode(GMPUniquePtr<GMPVideoi420Frame> aInputFrame,
                         const nsTArray<uint8_t>& aCodecSpecificInfo,
                         const nsTArray<GMPVideoFrameType>& aFrameTypes) override;
   virtual GMPErr SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) override;
   virtual GMPErr SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) override;
   virtual GMPErr SetPeriodicKeyFrames(bool aEnable) override;
-  virtual const uint64_t ParentID() override { return reinterpret_cast<uint64_t>(mPlugin.get()); }
+  virtual const uint32_t ParentID() override;
 
   // GMPSharedMemManager
   virtual bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aMem) override
   {
 #ifdef GMP_SAFE_SHMEM
     return AllocShmem(aSize, aType, aMem);
 #else
     return AllocUnsafeShmem(aSize, aType, aMem);
--- a/dom/media/gmp/GMPVideoEncoderProxy.h
+++ b/dom/media/gmp/GMPVideoEncoderProxy.h
@@ -41,16 +41,16 @@ public:
                             int32_t aNumberOfCores,
                             uint32_t aMaxPayloadSize) = 0;
   virtual GMPErr Encode(mozilla::GMPUniquePtr<GMPVideoi420Frame> aInputFrame,
                         const nsTArray<uint8_t>& aCodecSpecificInfo,
                         const nsTArray<GMPVideoFrameType>& aFrameTypes) = 0;
   virtual GMPErr SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) = 0;
   virtual GMPErr SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) = 0;
   virtual GMPErr SetPeriodicKeyFrames(bool aEnable) = 0;
-  virtual const uint64_t ParentID() = 0;
+  virtual const uint32_t ParentID() = 0;
 
   // Call to tell GMP/plugin the consumer will no longer use this
   // interface/codec.
   virtual void Close() = 0;
 };
 
 #endif // GMPVideoEncoderProxy_h_
--- a/dom/media/gmp/PGMPService.ipdl
+++ b/dom/media/gmp/PGMPService.ipdl
@@ -12,16 +12,16 @@ namespace gmp {
 
 sync protocol PGMPService
 {
   parent spawns PGMP as child;
 
 parent:
   sync LoadGMP(nsCString nodeId, nsCString api, nsCString[] tags,
                ProcessId[] alreadyBridgedTo)
-    returns (ProcessId id, nsCString displayName, nsCString pluginId);
+    returns (ProcessId id, nsCString displayName, uint32_t pluginId);
   sync GetGMPNodeId(nsString origin, nsString topLevelOrigin,
                     bool inPrivateBrowsing)
     returns (nsCString id);
 };
 
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/omx/MediaOmxCommonDecoder.cpp
+++ b/dom/media/omx/MediaOmxCommonDecoder.cpp
@@ -47,18 +47,18 @@ MediaOmxCommonDecoder::SetPlatformCanOff
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   mCanOffloadAudio = aCanOffloadAudio;
 }
 
 bool
 MediaOmxCommonDecoder::CheckDecoderCanOffloadAudio()
 {
-  return (mCanOffloadAudio && !mFallbackToStateMachine && !mOutputStreams.Length() &&
-      mInitialPlaybackRate == 1.0);
+  return (mCanOffloadAudio && !mFallbackToStateMachine &&
+          !mOutputStreams.Length() && mPlaybackRate == 1.0);
 }
 
 void
 MediaOmxCommonDecoder::FirstFrameLoaded(nsAutoPtr<MediaInfo> aInfo,
                                         MediaDecoderEventVisibility aEventVisibility)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -10,16 +10,17 @@
 
 #include "mozilla/plugins/PluginModuleParent.h"
 
 #include "base/process_util.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/PCrashReporterParent.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/ipc/MessageChannel.h"
 #include "mozilla/plugins/BrowserStreamParent.h"
 #include "mozilla/plugins/PluginAsyncSurrogate.h"
 #include "mozilla/plugins/PluginBridge.h"
 #include "mozilla/plugins/PluginInstanceParent.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ProcessHangMonitor.h"
 #include "mozilla/Services.h"
@@ -53,16 +54,17 @@
 #include "PluginInterposeOSX.h"
 #include "PluginUtilsOSX.h"
 #endif
 
 using base::KillProcess;
 
 using mozilla::PluginLibrary;
 using mozilla::ipc::MessageChannel;
+using mozilla::ipc::GeckoChildProcessHost;
 using mozilla::dom::PCrashReporterParent;
 using mozilla::dom::CrashReporterParent;
 
 using namespace mozilla;
 using namespace mozilla::plugins;
 using namespace mozilla::plugins::parent;
 
 #ifdef MOZ_CRASHREPORTER
@@ -653,20 +655,16 @@ PluginModuleContentParent::PluginModuleC
     Preferences::RegisterCallback(TimeoutChanged, kContentTimeoutPref, this);
 }
 
 PluginModuleContentParent::~PluginModuleContentParent()
 {
     Preferences::UnregisterCallback(TimeoutChanged, kContentTimeoutPref, this);
 }
 
-// We start the Run IDs at 1 so that we can use 0 as a way of detecting
-// errors in retrieving the run ID.
-uint32_t PluginModuleChromeParent::sNextRunID = 1;
-
 bool PluginModuleChromeParent::sInstantiated = false;
 
 PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath, uint32_t aPluginId)
     : PluginModuleParent(true)
     , mSubprocess(new PluginProcessParent(aFilePath))
     , mPluginId(aPluginId)
     , mChromeTaskFactory(this)
     , mHangAnnotationFlags(0)
@@ -688,17 +686,17 @@ PluginModuleChromeParent::PluginModuleCh
 #endif
     , mInitOnAsyncConnect(false)
     , mAsyncInitRv(NS_ERROR_NOT_INITIALIZED)
     , mAsyncInitError(NPERR_NO_ERROR)
     , mContentParent(nullptr)
 {
     NS_ASSERTION(mSubprocess, "Out of memory!");
     sInstantiated = true;
-    mRunID = sNextRunID++;
+    mRunID = GeckoChildProcessHost::GetUniqueID();
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
     InitPluginProfiling();
 #endif
 
     mozilla::HangMonitor::RegisterAnnotator(*this);
 }
 
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -552,16 +552,15 @@ private:
     friend class LaunchedTask;
 
     bool                mInitOnAsyncConnect;
     nsresult            mAsyncInitRv;
     NPError             mAsyncInitError;
     dom::ContentParent* mContentParent;
     nsCOMPtr<nsIObserver> mOfflineObserver;
     bool mIsBlocklisted;
-    static uint32_t sNextRunID;
     static bool sInstantiated;
 };
 
 } // namespace plugins
 } // namespace mozilla
 
 #endif // mozilla_plugins_PluginModuleParent_h
--- a/dom/webidl/PeerConnectionImpl.webidl
+++ b/dom/webidl/PeerConnectionImpl.webidl
@@ -58,17 +58,17 @@ interface PeerConnectionImpl  {
    */
   [Throws]
   void addIceCandidate(DOMString candidate, DOMString mid, unsigned short level);
 
   /* Puts the SIPCC engine back to 'kIdle', shuts down threads, deletes state */
   void close();
 
   /* Notify DOM window if this plugin crash is ours. */
-  boolean pluginCrash(unsigned long long pluginId, DOMString name, DOMString pluginDumpID);
+  boolean pluginCrash(unsigned long long pluginId, DOMString name);
 
   /* Attributes */
   [Constant]
   readonly attribute DOMString fingerprint;
   readonly attribute DOMString localDescription;
   readonly attribute DOMString remoteDescription;
 
   readonly attribute PCImplIceConnectionState iceConnectionState;
--- a/dom/webidl/PluginCrashedEvent.webidl
+++ b/dom/webidl/PluginCrashedEvent.webidl
@@ -2,25 +2,27 @@
 /* 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/.
  */
 
 [Constructor(DOMString type, optional PluginCrashedEventInit eventInitDict), ChromeOnly]
 interface PluginCrashedEvent : Event
 {
+  readonly attribute unsigned long pluginID;
   readonly attribute DOMString pluginDumpID;
   readonly attribute DOMString pluginName;
   readonly attribute DOMString? browserDumpID;
   readonly attribute DOMString? pluginFilename;
   readonly attribute boolean submittedCrashReport;
   readonly attribute boolean gmpPlugin;
 };
 
 dictionary PluginCrashedEventInit : EventInit
 {
+  unsigned long pluginID = 0;
   DOMString pluginDumpID = "";
   DOMString pluginName = "";
   DOMString? browserDumpID = null;
   DOMString? pluginFilename = null;
   boolean submittedCrashReport = false;
   boolean gmpPlugin = false;
 };
--- a/image/src/imgLoader.cpp
+++ b/image/src/imgLoader.cpp
@@ -203,16 +203,18 @@ private:
     return NS_OK;
   }
 
   static nsresult ReportImage(nsIHandleReportCallback* aHandleReport,
                               nsISupports* aData,
                               const char* aPathPrefix,
                               const ImageMemoryCounter& aCounter)
   {
+    nsresult rv;
+
     nsAutoCString pathPrefix(NS_LITERAL_CSTRING("explicit/"));
     pathPrefix.Append(aPathPrefix);
     pathPrefix.Append(aCounter.Type() == imgIContainer::TYPE_RASTER
                         ? "/raster/"
                         : "/vector/");
     pathPrefix.Append(aCounter.IsUsed() ? "used/" : "unused/");
     pathPrefix.Append("image(");
     pathPrefix.AppendInt(aCounter.IntrinsicSize().width);
@@ -223,17 +225,23 @@ private:
     if (aCounter.URI().IsEmpty()) {
       pathPrefix.Append("<unknown URI>");
     } else {
       pathPrefix.Append(aCounter.URI());
     }
 
     pathPrefix.Append(")/");
 
-    return ReportSurfaces(aHandleReport, aData, pathPrefix, aCounter);
+    rv = ReportSurfaces(aHandleReport, aData, pathPrefix, aCounter);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = ReportSourceValue(aHandleReport, aData, pathPrefix, aCounter.Values());
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    return NS_OK;
   }
 
   static nsresult ReportSurfaces(nsIHandleReportCallback* aHandleReport,
                                  nsISupports* aData,
                                  const nsACString& aPathPrefix,
                                  const ImageMemoryCounter& aCounter)
   {
     for (const SurfaceMemoryCounter& counter : aCounter.Surfaces()) {
@@ -327,20 +335,17 @@ private:
 
   static nsresult ReportValues(nsIHandleReportCallback* aHandleReport,
                                nsISupports* aData,
                                const nsACString& aPathPrefix,
                                const MemoryCounter& aCounter)
   {
     nsresult rv;
 
-    rv = ReportValue(aHandleReport, aData, KIND_HEAP, aPathPrefix,
-                     "source",
-                     "Raster image source data and vector image documents.",
-                     aCounter.Source());
+    rv = ReportSourceValue(aHandleReport, aData, aPathPrefix, aCounter);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = ReportValue(aHandleReport, aData, KIND_HEAP, aPathPrefix,
                      "decoded-heap",
                      "Decoded image data which is stored on the heap.",
                      aCounter.DecodedHeap());
     NS_ENSURE_SUCCESS(rv, rv);
 
@@ -348,16 +353,31 @@ private:
                      "decoded-nonheap",
                      "Decoded image data which isn't stored on the heap.",
                      aCounter.DecodedNonHeap());
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
   }
 
+  static nsresult ReportSourceValue(nsIHandleReportCallback* aHandleReport,
+                                    nsISupports* aData,
+                                    const nsACString& aPathPrefix,
+                                    const MemoryCounter& aCounter)
+  {
+    nsresult rv;
+
+    rv = ReportValue(aHandleReport, aData, KIND_HEAP, aPathPrefix,
+                     "source",
+                     "Raster image source data and vector image documents.",
+                     aCounter.Source());
+
+    return rv;
+  }
+
   static nsresult ReportValue(nsIHandleReportCallback* aHandleReport,
                               nsISupports* aData,
                               int32_t aKind,
                               const nsACString& aPathPrefix,
                               const char* aPathSuffix,
                               const char* aDescription,
                               size_t aValue)
   {
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -249,16 +249,27 @@ uint32_t GeckoChildProcessHost::GetSuppo
     }
     return pluginContainerArchs;
   }
 #endif
 
   return base::GetCurrentProcessArchitecture();
 }
 
+// We start the unique IDs at 1 so that 0 can be used to mean that
+// a component has no unique ID assigned to it.
+uint32_t GeckoChildProcessHost::sNextUniqueID = 1;
+
+/* static */
+uint32_t
+GeckoChildProcessHost::GetUniqueID()
+{
+  return sNextUniqueID++;
+}
+
 void
 GeckoChildProcessHost::PrepareLaunch()
 {
 #ifdef MOZ_CRASHREPORTER
   if (CrashReporter::GetEnabled()) {
     CrashReporter::OOPInit();
   }
 #endif
--- a/ipc/glue/GeckoChildProcessHost.h
+++ b/ipc/glue/GeckoChildProcessHost.h
@@ -42,16 +42,18 @@ public:
                                  ChildPrivileges aPrivileges=base::PRIVILEGES_DEFAULT);
 
   ~GeckoChildProcessHost();
 
   static nsresult GetArchitecturesForBinary(const char *path, uint32_t *result);
 
   static uint32_t GetSupportedArchitecturesForProcessType(GeckoProcessType type);
 
+  static uint32_t GetUniqueID();
+
   // Block until the IPC channel for our subprocess is initialized,
   // but no longer.  The child process may or may not have been
   // created when this method returns.
   bool AsyncLaunch(StringVector aExtraOpts=StringVector(),
                    base::ProcessArchitecture arch=base::GetCurrentProcessArchitecture());
 
   virtual bool WaitUntilConnected(int32_t aTimeoutMs = 0);
 
@@ -191,16 +193,18 @@ private:
   // In between launching the subprocess and handing off its IPC
   // channel, there's a small window of time in which *we* might still
   // be the channel listener, and receive messages.  That's bad
   // because we have no idea what to do with those messages.  So queue
   // them here until we hand off the eventual listener.
   //
   // FIXME/cjones: this strongly indicates bad design.  Shame on us.
   std::queue<IPC::Message> mQueue;
+
+  static uint32_t sNextUniqueID;
 };
 
 #ifdef MOZ_NUWA_PROCESS
 class GeckoExistingProcessHost final : public GeckoChildProcessHost
 {
 public:
   GeckoExistingProcessHost(GeckoProcessType aProcessType,
                            base::ProcessHandle aProcess,
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -586,17 +586,17 @@ struct JSClass {
 
 #define JSCLASS_IS_ANONYMOUS            (1<<(JSCLASS_HIGH_FLAGS_SHIFT+0))
 #define JSCLASS_IS_GLOBAL               (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1))
 #define JSCLASS_INTERNAL_FLAG2          (1<<(JSCLASS_HIGH_FLAGS_SHIFT+2))
 #define JSCLASS_INTERNAL_FLAG3          (1<<(JSCLASS_HIGH_FLAGS_SHIFT+3))
 
 #define JSCLASS_IS_PROXY                (1<<(JSCLASS_HIGH_FLAGS_SHIFT+4))
 
-#define JSCLASS_FINALIZE_FROM_NURSERY   (1<<(JSCLASS_HIGH_FLAGS_SHIFT+5))
+#define JSCLASS_SKIP_NURSERY_FINALIZE   (1<<(JSCLASS_HIGH_FLAGS_SHIFT+5))
 
 // Reserved for embeddings.
 #define JSCLASS_USERBIT2                (1<<(JSCLASS_HIGH_FLAGS_SHIFT+6))
 #define JSCLASS_USERBIT3                (1<<(JSCLASS_HIGH_FLAGS_SHIFT+7))
 
 #define JSCLASS_BACKGROUND_FINALIZE     (1<<(JSCLASS_HIGH_FLAGS_SHIFT+8))
 
 // Bits 26 through 31 are reserved for the CACHED_PROTO_KEY mechanism, see
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -220,17 +220,17 @@ struct CodeSizes
 
 // Data for tracking GC memory usage.
 struct GCSizes
 {
 #define FOR_EACH_SIZE(macro) \
     macro(_, _, marker) \
     macro(_, _, nurseryCommitted) \
     macro(_, _, nurseryDecommitted) \
-    macro(_, _, nurseryHugeSlots) \
+    macro(_, _, nurseryMallocedBuffers) \
     macro(_, _, storeBufferVals) \
     macro(_, _, storeBufferCells) \
     macro(_, _, storeBufferSlots) \
     macro(_, _, storeBufferWholeCells) \
     macro(_, _, storeBufferRelocVals) \
     macro(_, _, storeBufferRelocCells) \
     macro(_, _, storeBufferGenerics)
 
--- a/js/src/devtools/automation/autospider.sh
+++ b/js/src/devtools/automation/autospider.sh
@@ -100,28 +100,35 @@ elif [ "$OSTYPE" = "linux-gnu" ]; then
     export CXX=$GCCDIR/bin/g++
     if $USE_64BIT; then
       export LD_LIBRARY_PATH=$GCCDIR/lib64
     else
       export LD_LIBRARY_PATH=$GCCDIR/lib
     fi
   fi
 elif [ "$OSTYPE" = "msys" ]; then
-  USE_64BIT=false
-  if [ "$platform" = "win64" ]; then
-      USE_64BIT=true
-  fi
+  case "$platform" in
+  win64*)
+    USE_64BIT=true
+    ;;
+  *)
+    USE_64BIT=false
+    ;;
+  esac
   MAKE=${MAKE:-mozmake}
   source "$ABSDIR/winbuildenv.sh"
 fi
 
 MAKE=${MAKE:-make}
 
 if $USE_64BIT; then
   NSPR64="--enable-64bit"
+  if [ "$OSTYPE" = "msys" ]; then
+    CONFIGURE_ARGS="$CONFIGURE_ARGS --target=x86_64-pc-mingw32 --host=x86_64-pc-mingw32"
+  fi
 else
   NSPR64=""
   if [ "$OSTYPE" != "msys" ]; then
     export CC="${CC:-/usr/bin/gcc} -m32"
     export CXX="${CXX:-/usr/bin/g++} -m32"
     export AR=ar
   fi
 fi
@@ -164,18 +171,24 @@ elif [[ "$VARIANT" = "compacting" ]]; th
     export JSTESTS_EXTRA_ARGS=--exclude-file=$ABSDIR/cgc-jstests-slow.txt
 
     case "$platform" in
     win*)
         RUN_JSTESTS=false
     esac
 fi
 
-if [[ "$VARIANT" = "warnaserr" ]]; then
+if [[ "$VARIANT" = "warnaserr" ||
+      "$VARIANT" = "warnaserrdebug" ||
+      "$VARIANT" = "plain" ]]; then
     export JSTESTS_EXTRA_ARGS=--tbpl
+elif [[ "$VARIANT" = "arm-sim" ||
+        "$VARIANT" = "rootanalysis" ||
+        "$VARIANT" = "plaindebug" ]]; then
+    export JSTESTS_EXTRA_ARGS=--tbpl-debug
 fi
 
 $COMMAND_PREFIX $MAKE check || exit 1
 $COMMAND_PREFIX $MAKE check-jit-test || exit 1
 $COMMAND_PREFIX $OBJDIR/dist/bin/jsapi-tests || exit 1
 if $RUN_JSTESTS; then
     $COMMAND_PREFIX $MAKE check-jstests || exit 1
 fi
--- a/js/src/gc/Nursery-inl.h
+++ b/js/src/gc/Nursery-inl.h
@@ -9,23 +9,68 @@
 #define gc_Nursery_inl_h
 
 #include "gc/Nursery.h"
 
 #include "gc/Heap.h"
 #include "js/TracingAPI.h"
 #include "vm/Runtime.h"
 
+namespace js {
+
 template <typename T>
 MOZ_ALWAYS_INLINE bool
-js::Nursery::getForwardedPointer(T** ref)
+Nursery::getForwardedPointer(T** ref)
 {
     MOZ_ASSERT(ref);
     MOZ_ASSERT(isInside((void*)*ref));
     const gc::RelocationOverlay* overlay = reinterpret_cast<const gc::RelocationOverlay*>(*ref);
     if (!overlay->isForwarded())
         return false;
     /* This static cast from Cell* restricts T to valid (GC thing) types. */
     *ref = static_cast<T*>(overlay->forwardingAddress());
     return true;
 }
 
+// The allocation methods below will not run the garbage collector. If the
+// nursery cannot accomodate the allocation, the malloc heap will be used
+// instead.
+
+template <typename T>
+static inline T*
+AllocateObjectBuffer(ExclusiveContext* cx, uint32_t count)
+{
+    if (cx->isJSContext()) {
+        Nursery& nursery = cx->asJSContext()->runtime()->gc.nursery;
+        return static_cast<T*>(nursery.allocateBuffer(cx->zone(), count * sizeof(T)));
+    }
+    return cx->zone()->pod_malloc<T>(count);
+}
+
+template <typename T>
+static inline T*
+AllocateObjectBuffer(ExclusiveContext* cx, JSObject* obj, uint32_t count)
+{
+    if (cx->isJSContext()) {
+        Nursery& nursery = cx->asJSContext()->runtime()->gc.nursery;
+        return static_cast<T*>(nursery.allocateBuffer(obj, count * sizeof(T)));
+    }
+    return obj->zone()->pod_malloc<T>(count);
+}
+
+// If this returns null then the old buffer will be left alone.
+template <typename T>
+static inline T*
+ReallocateObjectBuffer(ExclusiveContext* cx, JSObject* obj, T* oldBuffer,
+                       uint32_t oldCount, uint32_t newCount)
+{
+    if (cx->isJSContext()) {
+        Nursery& nursery = cx->asJSContext()->runtime()->gc.nursery;
+        return static_cast<T*>(nursery.reallocateBuffer(obj, oldBuffer,
+                                                        oldCount * sizeof(T),
+                                                        newCount * sizeof(T)));
+    }
+    return obj->zone()->pod_realloc<T>(oldBuffer, oldCount, newCount);
+}
+
+} // namespace js
+
 #endif /* gc_Nursery_inl_h */
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -32,49 +32,49 @@
 
 using namespace js;
 using namespace gc;
 
 using mozilla::ArrayLength;
 using mozilla::PodCopy;
 using mozilla::PodZero;
 
-struct js::Nursery::FreeHugeSlotsTask : public GCParallelTask
+struct js::Nursery::FreeMallocedBuffersTask : public GCParallelTask
 {
-    explicit FreeHugeSlotsTask(FreeOp* fop) : fop_(fop) {}
-    bool init() { return slots_.init(); }
-    void transferSlotsToFree(HugeSlotsSet& slotsToFree);
-    ~FreeHugeSlotsTask() override { join(); }
+    explicit FreeMallocedBuffersTask(FreeOp* fop) : fop_(fop) {}
+    bool init() { return buffers_.init(); }
+    void transferBuffersToFree(MallocedBuffersSet& buffersToFree);
+    ~FreeMallocedBuffersTask() override { join(); }
 
   private:
     FreeOp* fop_;
-    HugeSlotsSet slots_;
+    MallocedBuffersSet buffers_;
 
     virtual void run() override;
 };
 
 bool
 js::Nursery::init(uint32_t maxNurseryBytes)
 {
     /* maxNurseryBytes parameter is rounded down to a multiple of chunk size. */
     numNurseryChunks_ = maxNurseryBytes >> ChunkShift;
 
     /* If no chunks are specified then the nursery is permenantly disabled. */
     if (numNurseryChunks_ == 0)
         return true;
 
-    if (!hugeSlots.init())
+    if (!mallocedBuffers.init())
         return false;
 
     void* heap = MapAlignedPages(nurserySize(), Alignment);
     if (!heap)
         return false;
 
-    freeHugeSlotsTask = js_new<FreeHugeSlotsTask>(runtime()->defaultFreeOp());
-    if (!freeHugeSlotsTask || !freeHugeSlotsTask->init())
+    freeMallocedBuffersTask = js_new<FreeMallocedBuffersTask>(runtime()->defaultFreeOp());
+    if (!freeMallocedBuffersTask || !freeMallocedBuffersTask->init())
         return false;
 
     heapStart_ = uintptr_t(heap);
     heapEnd_ = heapStart_ + nurserySize();
     currentStart_ = start();
     numActiveChunks_ = 1;
     JS_POISON(heap, JS_FRESH_NURSERY_PATTERN, nurserySize());
     setCurrentChunk(0);
@@ -95,17 +95,17 @@ js::Nursery::init(uint32_t maxNurseryByt
     return true;
 }
 
 js::Nursery::~Nursery()
 {
     if (start())
         UnmapPages((void*)start(), nurserySize());
 
-    js_delete(freeHugeSlotsTask);
+    js_delete(freeMallocedBuffersTask);
 }
 
 void
 js::Nursery::updateDecommittedRegion()
 {
 #ifndef JS_GC_ZEAL
     if (numActiveChunks_ < numNurseryChunks_) {
         // Bug 994054: madvise on MacOS is too slow to make this
@@ -170,81 +170,51 @@ js::Nursery::leaveZealMode() {
     if (isEnabled()) {
         MOZ_ASSERT(isEmpty());
         setCurrentChunk(0);
         currentStart_ = start();
     }
 }
 #endif // JS_GC_ZEAL
 
-void
-js::Nursery::verifyFinalizerList()
-{
-#ifdef DEBUG
-    for (ListItem* current = finalizers_; current; current = current->next()) {
-        JSObject* obj = current->get();
-        RelocationOverlay* overlay = RelocationOverlay::fromCell(obj);
-        if (overlay->isForwarded())
-            obj = static_cast<JSObject*>(overlay->forwardingAddress());
-        MOZ_ASSERT(obj);
-        MOZ_ASSERT(obj->group());
-        MOZ_ASSERT(obj->group()->clasp());
-        MOZ_ASSERT(obj->group()->clasp()->finalize);
-        MOZ_ASSERT(obj->group()->clasp()->flags & JSCLASS_FINALIZE_FROM_NURSERY);
-    }
-#endif // DEBUG
-}
-
 JSObject*
 js::Nursery::allocateObject(JSContext* cx, size_t size, size_t numDynamic, const js::Class* clasp)
 {
     /* Ensure there's enough space to replace the contents with a RelocationOverlay. */
     MOZ_ASSERT(size >= sizeof(RelocationOverlay));
-    verifyFinalizerList();
 
-    /* If we have a finalizer, get space for the list entry. */
-    ListItem* listEntry = nullptr;
-    if (clasp->finalize) {
-        listEntry = static_cast<ListItem*>(allocate(sizeof(ListItem)));
-        if (!listEntry)
-            return nullptr;
-    }
+    /*
+     * Classes with JSCLASS_SKIP_NURSERY_FINALIZE will not have their finalizer
+     * called if they are nursery allocated and not promoted to the tenured
+     * heap. The finalizers for these classes must do nothing except free data
+     * which was allocated via Nursery::allocateBuffer.
+     */
+    MOZ_ASSERT_IF(clasp->finalize, clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE);
 
     /* Make the object allocation. */
     JSObject* obj = static_cast<JSObject*>(allocate(size));
     if (!obj)
         return nullptr;
 
     /* If we want external slots, add them. */
     HeapSlot* slots = nullptr;
     if (numDynamic) {
-        /* Try to allocate in the nursery first. */
-        if (numDynamic <= MaxNurserySlots)
-            slots = static_cast<HeapSlot*>(allocate(numDynamic * sizeof(HeapSlot)));
-
-        /* If we are out of space or too large, use the malloc heap. */
-        if (!slots)
-            slots = allocateHugeSlots(cx->zone(), numDynamic);
-
-        /* It is safe to leave the allocated object uninitialized, since we do
-         * not visit unallocated things. */
-        if (!slots)
+        slots = static_cast<HeapSlot*>(allocateBuffer(cx->zone(), numDynamic * sizeof(HeapSlot)));
+        if (!slots) {
+            /*
+             * It is safe to leave the allocated object uninitialized, since we
+             * do not visit unallocated things in the nursery.
+             */
             return nullptr;
+        }
     }
 
     /* Always initialize the slots field to match the JIT behavior. */
     obj->setInitialSlotsMaybeNonNative(slots);
 
-    /* If we have a finalizer, link it into the finalizer list. */
-    if (clasp->finalize) {
-        MOZ_ASSERT(listEntry);
-        new (listEntry) ListItem(finalizers_, obj);
-        finalizers_ = listEntry;
-    }
-
     TraceNurseryAlloc(obj, size);
     return obj;
 }
 
 void*
 js::Nursery::allocate(size_t size)
 {
     MOZ_ASSERT(isEnabled());
@@ -259,99 +229,82 @@ js::Nursery::allocate(size_t size)
 
     void* thing = (void*)position();
     position_ = position() + size;
 
     JS_EXTRA_POISON(thing, JS_ALLOCATED_NURSERY_PATTERN, size);
     return thing;
 }
 
-/* Internally, this function is used to allocate elements as well as slots. */
-HeapSlot*
-js::Nursery::allocateSlots(JSObject* obj, uint32_t nslots)
+void*
+js::Nursery::allocateBuffer(Zone* zone, uint32_t nbytes)
 {
-    MOZ_ASSERT(obj);
-    MOZ_ASSERT(nslots > 0);
-
-    if (!IsInsideNursery(obj))
-        return obj->zone()->pod_malloc<HeapSlot>(nslots);
+    MOZ_ASSERT(nbytes > 0);
 
-    if (nslots > MaxNurserySlots)
-        return allocateHugeSlots(obj->zone(), nslots);
+    if (nbytes <= MaxNurseryBufferSize) {
+        void* buffer = allocate(nbytes);
+        if (buffer)
+            return buffer;
+    }
 
-    size_t size = sizeof(HeapSlot) * nslots;
-    HeapSlot* slots = static_cast<HeapSlot*>(allocate(size));
-    if (slots)
-        return slots;
-
-    return allocateHugeSlots(obj->zone(), nslots);
+    void* buffer = zone->pod_malloc<uint8_t>(nbytes);
+    if (buffer) {
+        /* If this put fails, we will only leak the slots. */
+        (void)mallocedBuffers.put(buffer);
+    }
+    return buffer;
 }
 
-ObjectElements*
-js::Nursery::allocateElements(JSObject* obj, uint32_t nelems)
+void*
+js::Nursery::allocateBuffer(JSObject* obj, uint32_t nbytes)
 {
-    MOZ_ASSERT(nelems >= ObjectElements::VALUES_PER_HEADER);
-    return reinterpret_cast<ObjectElements*>(allocateSlots(obj, nelems));
+    MOZ_ASSERT(obj);
+    MOZ_ASSERT(nbytes > 0);
+
+    if (!IsInsideNursery(obj))
+        return obj->zone()->pod_malloc<uint8_t>(nbytes);
+    return allocateBuffer(obj->zone(), nbytes);
 }
 
-HeapSlot*
-js::Nursery::reallocateSlots(JSObject* obj, HeapSlot* oldSlots,
-                             uint32_t oldCount, uint32_t newCount)
+void*
+js::Nursery::reallocateBuffer(JSObject* obj, void* oldBuffer,
+                              uint32_t oldBytes, uint32_t newBytes)
 {
     if (!IsInsideNursery(obj))
-        return obj->zone()->pod_realloc<HeapSlot>(oldSlots, oldCount, newCount);
+        return obj->zone()->pod_realloc<uint8_t>((uint8_t*)oldBuffer, oldBytes, newBytes);
 
-    if (!isInside(oldSlots)) {
-        HeapSlot* newSlots = obj->zone()->pod_realloc<HeapSlot>(oldSlots, oldCount, newCount);
-        if (newSlots && oldSlots != newSlots) {
-            hugeSlots.remove(oldSlots);
+    if (!isInside(oldBuffer)) {
+        void* newBuffer = obj->zone()->pod_realloc<uint8_t>((uint8_t*)oldBuffer, oldBytes, newBytes);
+        if (newBuffer && oldBytes != newBytes) {
+            removeMallocedBuffer(oldBuffer);
             /* If this put fails, we will only leak the slots. */
-            (void)hugeSlots.put(newSlots);
+            (void)mallocedBuffers.put(newBuffer);
         }
-        return newSlots;
+        return newBuffer;
     }
 
     /* The nursery cannot make use of the returned slots data. */
-    if (newCount < oldCount)
-        return oldSlots;
+    if (newBytes < oldBytes)
+        return oldBuffer;
 
-    HeapSlot* newSlots = allocateSlots(obj, newCount);
-    if (newSlots)
-        PodCopy(newSlots, oldSlots, oldCount);
-    return newSlots;
-}
-
-ObjectElements*
-js::Nursery::reallocateElements(JSObject* obj, ObjectElements* oldHeader,
-                                uint32_t oldCount, uint32_t newCount)
-{
-    HeapSlot* slots = reallocateSlots(obj, reinterpret_cast<HeapSlot*>(oldHeader),
-                                      oldCount, newCount);
-    return reinterpret_cast<ObjectElements*>(slots);
+    void* newBuffer = allocateBuffer(obj->zone(), newBytes);
+    if (newBuffer)
+        PodCopy((uint8_t*)newBuffer, (uint8_t*)oldBuffer, oldBytes);
+    return newBuffer;
 }
 
 void
-js::Nursery::freeSlots(HeapSlot* slots)
+js::Nursery::freeBuffer(void* buffer)
 {
-    if (!isInside(slots)) {
-        hugeSlots.remove(slots);
-        js_free(slots);
+    if (!isInside(buffer)) {
+        removeMallocedBuffer(buffer);
+        js_free(buffer);
     }
 }
 
-HeapSlot*
-js::Nursery::allocateHugeSlots(JS::Zone* zone, size_t nslots)
-{
-    HeapSlot* slots = zone->pod_malloc<HeapSlot>(nslots);
-    /* If this put fails, we will only leak the slots. */
-    if (slots)
-        (void)hugeSlots.put(slots);
-    return slots;
-}
-
 namespace js {
 namespace gc {
 
 class MinorCollectionTracer : public JS::CallbackTracer
 {
   public:
     Nursery* nursery;
     AutoTraceSession session;
@@ -686,31 +639,38 @@ js::Nursery::moveObjectToTenured(MinorCo
         // The shape's list head may point into the old object. This can only
         // happen for dictionaries, which are native objects.
         if (&nsrc->shape_ == ndst->shape_->listp) {
             MOZ_ASSERT(nsrc->shape_->inDictionary());
             ndst->shape_->listp = &ndst->shape_;
         }
     }
 
-    if (src->is<InlineTypedObject>())
+    if (src->is<InlineTypedObject>()) {
         InlineTypedObject::objectMovedDuringMinorGC(trc, dst, src);
+    } else if (src->is<UnboxedArrayObject>()) {
+        tenuredSize += UnboxedArrayObject::objectMovedDuringMinorGC(trc, dst, src, dstKind);
+    } else {
+        // Objects with JSCLASS_SKIP_NURSERY_FINALIZE need to be handled above
+        // to ensure any additional nursery buffers they hold are moved.
+        MOZ_ASSERT(!(src->getClass()->flags & JSCLASS_SKIP_NURSERY_FINALIZE));
+    }
 
     return tenuredSize;
 }
 
 MOZ_ALWAYS_INLINE size_t
 js::Nursery::moveSlotsToTenured(NativeObject* dst, NativeObject* src, AllocKind dstKind)
 {
     /* Fixed slots have already been copied over. */
     if (!src->hasDynamicSlots())
         return 0;
 
     if (!isInside(src->slots_)) {
-        hugeSlots.remove(src->slots_);
+        removeMallocedBuffer(src->slots_);
         return 0;
     }
 
     Zone* zone = src->zone();
     size_t count = src->numDynamicSlots();
     dst->slots_ = zone->pod_malloc<HeapSlot>(count);
     if (!dst->slots_)
         CrashAtUnhandlableOOM("Failed to allocate slots while tenuring.");
@@ -727,17 +687,17 @@ js::Nursery::moveElementsToTenured(Nativ
 
     Zone* zone = src->zone();
     ObjectElements* srcHeader = src->getElementsHeader();
     ObjectElements* dstHeader;
 
     /* TODO Bug 874151: Prefer to put element data inline if we have space. */
     if (!isInside(srcHeader)) {
         MOZ_ASSERT(src->elements_ == dst->elements_);
-        hugeSlots.remove(reinterpret_cast<HeapSlot*>(srcHeader));
+        removeMallocedBuffer(srcHeader);
         return 0;
     }
 
     size_t nslots = ObjectElements::VALUES_PER_HEADER + srcHeader->capacity;
 
     /* Unlike other objects, Arrays can have fixed elements. */
     if (src->is<ArrayObject>() && nslots <= GetGCKindSlots(dstKind)) {
         dst->as<ArrayObject>().setFixedElements();
@@ -874,23 +834,19 @@ js::Nursery::collect(JSRuntime* rt, JS::
 
     // Update any slot or element pointers whose destination has been tenured.
     TIME_START(updateJitActivations);
     js::jit::UpdateJitActivationsForMinorGC(rt, &trc);
     forwardedBuffers.finish();
     TIME_END(updateJitActivations);
 
     // Sweep.
-    TIME_START(runFinalizers);
-    runFinalizers();
-    TIME_END(runFinalizers);
-
-    TIME_START(freeHugeSlots);
-    freeHugeSlots();
-    TIME_END(freeHugeSlots);
+    TIME_START(freeMallocedBuffers);
+    freeMallocedBuffers();
+    TIME_END(freeMallocedBuffers);
 
     TIME_START(sweep);
     sweep();
     TIME_END(sweep);
 
     TIME_START(clearStoreBuffer);
     rt->gc.storeBuffer.clear();
     TIME_END(clearStoreBuffer);
@@ -942,17 +898,17 @@ js::Nursery::collect(JSRuntime* rt, JS::
         if (!printedHeader) {
             fprintf(stderr,
                     "MinorGC: Reason               PRate  Size Time   mkVals mkClls mkSlts mkWCll mkRVal mkRCll mkGnrc ckTbls mkRntm mkDbgr clrNOC collct swpABO updtIn runFin frSlts clrSB  sweep resize pretnr\n");
             printedHeader = true;
         }
 
 #define FMT " %6" PRIu64
         fprintf(stderr,
-                "MinorGC: %20s %5.1f%% %4d" FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT "\n",
+                "MinorGC: %20s %5.1f%% %4d" FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT "\n",
                 js::gcstats::ExplainReason(reason),
                 promotionRate * 100,
                 numActiveChunks_,
                 totalTime,
                 TIME_TOTAL(markValues),
                 TIME_TOTAL(markCells),
                 TIME_TOTAL(markSlots),
                 TIME_TOTAL(markWholeCells),
@@ -961,87 +917,71 @@ js::Nursery::collect(JSRuntime* rt, JS::
                 TIME_TOTAL(markGenericEntries),
                 TIME_TOTAL(checkHashTables),
                 TIME_TOTAL(markRuntime),
                 TIME_TOTAL(markDebugger),
                 TIME_TOTAL(clearNewObjectCache),
                 TIME_TOTAL(collectToFP),
                 TIME_TOTAL(sweepArrayBufferViewList),
                 TIME_TOTAL(updateJitActivations),
-                TIME_TOTAL(runFinalizers),
-                TIME_TOTAL(freeHugeSlots),
+                TIME_TOTAL(freeMallocedBuffers),
                 TIME_TOTAL(clearStoreBuffer),
                 TIME_TOTAL(sweep),
                 TIME_TOTAL(resize),
                 TIME_TOTAL(pretenure));
 #undef FMT
     }
 }
 
 #undef TIME_START
 #undef TIME_END
 #undef TIME_TOTAL
 
 void
-js::Nursery::FreeHugeSlotsTask::transferSlotsToFree(HugeSlotsSet& slotsToFree)
+js::Nursery::FreeMallocedBuffersTask::transferBuffersToFree(MallocedBuffersSet& buffersToFree)
 {
-    // Transfer the contents of the source set to the task's slots_ member by
+    // Transfer the contents of the source set to the task's buffers_ member by
     // swapping the sets, which also clears the source.
     MOZ_ASSERT(!isRunning());
-    MOZ_ASSERT(slots_.empty());
-    mozilla::Swap(slots_, slotsToFree);
+    MOZ_ASSERT(buffers_.empty());
+    mozilla::Swap(buffers_, buffersToFree);
 }
 
 void
-js::Nursery::FreeHugeSlotsTask::run()
+js::Nursery::FreeMallocedBuffersTask::run()
 {
-    for (HugeSlotsSet::Range r = slots_.all(); !r.empty(); r.popFront())
+    for (MallocedBuffersSet::Range r = buffers_.all(); !r.empty(); r.popFront())
         fop_->free_(r.front());
-    slots_.clear();
+    buffers_.clear();
 }
 
 void
-js::Nursery::freeHugeSlots()
+js::Nursery::freeMallocedBuffers()
 {
-    if (hugeSlots.empty())
+    if (mallocedBuffers.empty())
         return;
 
     bool started;
     {
         AutoLockHelperThreadState lock;
-        freeHugeSlotsTask->joinWithLockHeld();
-        freeHugeSlotsTask->transferSlotsToFree(hugeSlots);
-        started = freeHugeSlotsTask->startWithLockHeld();
+        freeMallocedBuffersTask->joinWithLockHeld();
+        freeMallocedBuffersTask->transferBuffersToFree(mallocedBuffers);
+        started = freeMallocedBuffersTask->startWithLockHeld();
     }
 
     if (!started)
-        freeHugeSlotsTask->runFromMainThread(runtime());
+        freeMallocedBuffersTask->runFromMainThread(runtime());
 
-    MOZ_ASSERT(hugeSlots.empty());
+    MOZ_ASSERT(mallocedBuffers.empty());
 }
 
 void
 js::Nursery::waitBackgroundFreeEnd()
 {
-    freeHugeSlotsTask->join();
-}
-
-void
-js::Nursery::runFinalizers()
-{
-    verifyFinalizerList();
-
-    FreeOp* fop = runtime()->defaultFreeOp();
-    for (ListItem* current = finalizers_; current; current = current->next()) {
-        JSObject* obj = current->get();
-        RelocationOverlay* overlay = RelocationOverlay::fromCell(obj);
-        if (!overlay->isForwarded())
-            obj->getClass()->finalize(fop, obj);
-    }
-    finalizers_ = nullptr;
+    freeMallocedBuffersTask->join();
 }
 
 void
 js::Nursery::sweep()
 {
 #ifdef JS_GC_ZEAL
     /* Poison the nursery contents so touching a freed object will crash. */
     JS_POISON((void*)start(), JS_SWEPT_NURSERY_PATTERN, nurserySize());
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -54,20 +54,19 @@ class Nursery
         position_(0),
         currentStart_(0),
         currentEnd_(0),
         heapStart_(0),
         heapEnd_(0),
         currentChunk_(0),
         numActiveChunks_(0),
         numNurseryChunks_(0),
-        finalizers_(nullptr),
         profileThreshold_(0),
         enableProfiling_(false),
-        freeHugeSlotsTask(nullptr)
+        freeMallocedBuffersTask(nullptr)
     {}
     ~Nursery();
 
     bool init(uint32_t maxNurseryBytes);
 
     bool exists() const { return numNurseryChunks_ != 0; }
     size_t numChunks() const { return numNurseryChunks_; }
     size_t nurserySize() const { return numNurseryChunks_ << ChunkShift; }
@@ -89,32 +88,31 @@ class Nursery
     }
 
     /*
      * Allocate and return a pointer to a new GC object with its |slots|
      * pointer pre-filled. Returns nullptr if the Nursery is full.
      */
     JSObject* allocateObject(JSContext* cx, size_t size, size_t numDynamic, const js::Class* clasp);
 
-    /* Allocate a slots array for the given object. */
-    HeapSlot* allocateSlots(JSObject* obj, uint32_t nslots);
-
-    /* Allocate an elements vector for the given object. */
-    ObjectElements* allocateElements(JSObject* obj, uint32_t nelems);
+    /* Allocate a buffer for a given zone, using the nursery if possible. */
+    void* allocateBuffer(Zone* zone, uint32_t nbytes);
 
-    /* Resize an existing slots array. */
-    HeapSlot* reallocateSlots(JSObject* obj, HeapSlot* oldSlots,
-                              uint32_t oldCount, uint32_t newCount);
+    /*
+     * Allocate a buffer for a given object, using the nursery if possible and
+     * obj is in the nursery.
+     */
+    void* allocateBuffer(JSObject* obj, uint32_t nbytes);
 
-    /* Resize an existing elements vector. */
-    ObjectElements* reallocateElements(JSObject* obj, ObjectElements* oldHeader,
-                                       uint32_t oldCount, uint32_t newCount);
+    /* Resize an existing object buffer. */
+    void* reallocateBuffer(JSObject* obj, void* oldBuffer,
+                           uint32_t oldBytes, uint32_t newBytes);
 
-    /* Free a slots array. */
-    void freeSlots(HeapSlot* slots);
+    /* Free an object buffer. */
+    void freeBuffer(void* buffer);
 
     typedef Vector<ObjectGroup*, 0, SystemAllocPolicy> ObjectGroupList;
 
     /*
      * Do a minor collection, optionally specifying a list to store groups which
      * should be pretenured afterwards.
      */
     void collect(JSRuntime* rt, JS::gcreason::Reason reason, ObjectGroupList* pretenureGroups);
@@ -130,29 +128,34 @@ class Nursery
     /* Forward a slots/elements pointer stored in an Ion frame. */
     void forwardBufferPointer(HeapSlot** pSlotsElems);
 
     void maybeSetForwardingPointer(JSTracer* trc, void* oldData, void* newData, bool direct) {
         if (IsMinorCollectionTracer(trc) && isInside(oldData))
             setForwardingPointer(oldData, newData, direct);
     }
 
+    /* Mark a malloced buffer as no longer needing to be freed. */
+    void removeMallocedBuffer(void* buffer) {
+        mallocedBuffers.remove(buffer);
+    }
+
     void waitBackgroundFreeEnd();
 
     size_t sizeOfHeapCommitted() const {
         return numActiveChunks_ * gc::ChunkSize;
     }
     size_t sizeOfHeapDecommitted() const {
         return (numNurseryChunks_ - numActiveChunks_) * gc::ChunkSize;
     }
-    size_t sizeOfHugeSlots(mozilla::MallocSizeOf mallocSizeOf) const {
+    size_t sizeOfMallocedBuffers(mozilla::MallocSizeOf mallocSizeOf) const {
         size_t total = 0;
-        for (HugeSlotsSet::Range r = hugeSlots.all(); !r.empty(); r.popFront())
+        for (MallocedBuffersSet::Range r = mallocedBuffers.all(); !r.empty(); r.popFront())
             total += mallocSizeOf(r.front());
-        total += hugeSlots.sizeOfExcludingThis(mallocSizeOf);
+        total += mallocedBuffers.sizeOfExcludingThis(mallocSizeOf);
         return total;
     }
 
     MOZ_ALWAYS_INLINE uintptr_t start() const {
         return heapStart_;
     }
 
     MOZ_ALWAYS_INLINE uintptr_t heapEnd() const {
@@ -193,54 +196,44 @@ class Nursery
     int currentChunk_;
 
     /* The index after the last chunk that we will allocate from. */
     int numActiveChunks_;
 
     /* Number of chunks allocated for the nursery. */
     int numNurseryChunks_;
 
-    /* Keep track of objects that need finalization. */
-    class ListItem {
-        ListItem* next_;
-        JSObject* object_;
-      public:
-        ListItem(ListItem* tail, JSObject* obj) : next_(tail), object_(obj) {}
-        ListItem* next() const { return next_; }
-        JSObject* get() { return object_; }
-    } *finalizers_;
-
     /* Report minor collections taking more than this many us, if enabled. */
     int64_t profileThreshold_;
     bool enableProfiling_;
 
     /*
-     * The set of externally malloced slots potentially kept live by objects
-     * stored in the nursery. Any external slots that do not belong to a
+     * The set of externally malloced buffers potentially kept live by objects
+     * stored in the nursery. Any external buffers that do not belong to a
      * tenured thing at the end of a minor GC must be freed.
      */
-    typedef HashSet<HeapSlot*, PointerHasher<HeapSlot*, 3>, SystemAllocPolicy> HugeSlotsSet;
-    HugeSlotsSet hugeSlots;
+    typedef HashSet<void*, PointerHasher<void*, 3>, SystemAllocPolicy> MallocedBuffersSet;
+    MallocedBuffersSet mallocedBuffers;
 
-    /* A task structure used to free the huge slots on a background thread. */
-    struct FreeHugeSlotsTask;
-    FreeHugeSlotsTask* freeHugeSlotsTask;
+    /* A task structure used to free the malloced bufers on a background thread. */
+    struct FreeMallocedBuffersTask;
+    FreeMallocedBuffersTask* freeMallocedBuffersTask;
 
     /*
      * During a collection most hoisted slot and element buffers indicate their
      * new location with a forwarding pointer at the base. This does not work
      * for buffers whose length is less than pointer width, or when different
      * buffers might overlap each other. For these, an entry in the following
      * table is used.
      */
     typedef HashMap<void*, void*, PointerHasher<void*, 1>, SystemAllocPolicy> ForwardedBufferMap;
     ForwardedBufferMap forwardedBuffers;
 
-    /* The maximum number of slots allowed to reside inline in the nursery. */
-    static const size_t MaxNurserySlots = 128;
+    /* The maximum number of bytes allowed to reside in nursery buffers. */
+    static const size_t MaxNurseryBufferSize = 1024;
 
     /* The amount of space in the mapped nursery available to allocations. */
     static const size_t NurseryChunkUsableSize = gc::ChunkSize - sizeof(gc::ChunkTrailer);
 
     struct NurseryChunkLayout {
         char data[NurseryChunkUsableSize];
         gc::ChunkTrailer trailer;
         uintptr_t start() { return uintptr_t(&data); }
@@ -287,27 +280,23 @@ class Nursery
         return (void*)&currentEnd_;
     }
 
     uintptr_t position() const { return position_; }
     void* addressOfPosition() const { return (void*)&position_; }
 
     JSRuntime* runtime() const { return runtime_; }
 
-    /* Allocates and registers external slots with the nursery. */
-    HeapSlot* allocateHugeSlots(JS::Zone* zone, size_t nslots);
-
     /* Allocates a new GC thing from the tenured generation during minor GC. */
     gc::TenuredCell* allocateFromTenured(JS::Zone* zone, gc::AllocKind thingKind);
 
     struct TenureCountCache;
 
     /* Common internal allocator function. */
     void* allocate(size_t size);
-    void verifyFinalizerList();
 
     /*
      * Move the object at |src| in the Nursery to an already-allocated cell
      * |dst| in Tenured.
      */
     void collectToFixedPoint(gc::MinorCollectionTracer* trc, TenureCountCache& tenureCounts);
     MOZ_ALWAYS_INLINE void traceObject(gc::MinorCollectionTracer* trc, JSObject* src);
     MOZ_ALWAYS_INLINE void markSlots(gc::MinorCollectionTracer* trc, HeapSlot* vp, uint32_t nslots);
@@ -324,21 +313,18 @@ class Nursery
 
     /* Handle relocation of slots/elements pointers stored in Ion frames. */
     void setForwardingPointer(void* oldData, void* newData, bool direct);
 
     void setSlotsForwardingPointer(HeapSlot* oldSlots, HeapSlot* newSlots, uint32_t nslots);
     void setElementsForwardingPointer(ObjectElements* oldHeader, ObjectElements* newHeader,
                                       uint32_t nelems);
 
-    /* Run finalizers on all finalizable things in the nursery. */
-    void runFinalizers();
-
     /* Free malloced pointers owned by freed things in the nursery. */
-    void freeHugeSlots();
+    void freeMallocedBuffers();
 
     /*
      * Frees all non-live nursery-allocated things at the end of a minor
      * collection.
      */
     void sweep();
 
     /* Change the allocable space provided by the nursery. */
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1080,19 +1080,20 @@ MacroAssembler::shouldNurseryAllocate(gc
 void
 MacroAssembler::nurseryAllocate(Register result, Register temp, gc::AllocKind allocKind,
                                 size_t nDynamicSlots, gc::InitialHeap initialHeap, Label* fail)
 {
     MOZ_ASSERT(IsNurseryAllocable(allocKind));
     MOZ_ASSERT(initialHeap != gc::TenuredHeap);
 
     // We still need to allocate in the nursery, per the comment in
-    // shouldNurseryAllocate; however, we need to insert into hugeSlots, so
-    // bail to do the nursery allocation in the interpreter.
-    if (nDynamicSlots >= Nursery::MaxNurserySlots) {
+    // shouldNurseryAllocate; however, we need to insert into the
+    // mallocedBuffers set, so bail to do the nursery allocation in the
+    // interpreter.
+    if (nDynamicSlots >= Nursery::MaxNurseryBufferSize / sizeof(Value)) {
         jump(fail);
         return;
     }
 
     // No explicit check for nursery.isEnabled() is needed, as the comparison
     // with the nursery's end will always fail in such cases.
     const Nursery& nursery = GetJitContext()->runtime->gcNursery();
     int thingSize = int(gc::Arena::thingSize(allocKind));
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -35,17 +35,16 @@ UNIFIED_SOURCES += [
     'testFunctionProperties.cpp',
     'testGCAllocator.cpp',
     'testGCCellPtr.cpp',
     'testGCChunkPool.cpp',
     'testGCExactRooting.cpp',
     'testGCFinalizeCallback.cpp',
     'testGCHeapPostBarriers.cpp',
     'testGCMarking.cpp',
-    'testGCNursery.cpp',
     'testGCOutOfMemory.cpp',
     'testGCStoreBufferRemoval.cpp',
     'testGetPropertyDescriptor.cpp',
     'testHashTable.cpp',
     'testIndexToString.cpp',
     'testIntern.cpp',
     'testIntString.cpp',
     'testIntTypesABI.cpp',
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3960,16 +3960,26 @@ JSObject::allocKindForTenure(const js::N
     MOZ_ASSERT(!IsProxy(this));
 
     // Unboxed plain objects are sized according to the data they store.
     if (is<UnboxedPlainObject>()) {
         size_t nbytes = as<UnboxedPlainObject>().layoutDontCheckGeneration().size();
         return GetGCObjectKindForBytes(UnboxedPlainObject::offsetOfData() + nbytes);
     }
 
+    // Unboxed arrays use inline data if their size is small enough.
+    if (is<UnboxedArrayObject>()) {
+        const UnboxedArrayObject* nobj = &as<UnboxedArrayObject>();
+        size_t nbytes = UnboxedArrayObject::offsetOfInlineElements() +
+                        nobj->capacity() * nobj->elementSize();
+        if (nbytes <= JSObject::MAX_BYTE_SIZE)
+            return GetGCObjectKindForBytes(nbytes);
+        return AllocKind::OBJECT0;
+    }
+
     // Inlined typed objects are followed by their data, so make sure we copy
     // it all over to the new object.
     if (is<InlineTypedObject>()) {
         // Figure out the size of this object, from the prototype's TypeDescr.
         // The objects we are traversing here are all tenured, so we don't need
         // to check forwarding pointers.
         TypeDescr& descr = as<InlineTypedObject>().typeDescr();
         MOZ_ASSERT(!IsInsideNursery(&descr));
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1107,17 +1107,17 @@ extern const char js_lookupSetter_str[];
 
 namespace js {
 
 inline gc::InitialHeap
 GetInitialHeap(NewObjectKind newKind, const Class* clasp)
 {
     if (newKind != GenericObject)
         return gc::TenuredHeap;
-    if (clasp->finalize && !(clasp->flags & JSCLASS_FINALIZE_FROM_NURSERY))
+    if (clasp->finalize && !(clasp->flags & JSCLASS_SKIP_NURSERY_FINALIZE))
         return gc::TenuredHeap;
     return gc::DefaultHeap;
 }
 
 // Specialized call for constructing |this| with a known function callee,
 // and a known prototype.
 extern JSObject*
 CreateThisForFunctionWithProto(JSContext* cx, js::HandleObject callee, HandleObject proto,
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -262,17 +262,17 @@ JSObject::create(js::ExclusiveContext* c
     MOZ_ASSERT(group->clasp() == shape->getObjectClass());
     MOZ_ASSERT(group->clasp() != &js::ArrayObject::class_);
     MOZ_ASSERT_IF(!js::ClassCanHaveFixedData(group->clasp()),
                   js::gc::GetGCKindSlots(kind, group->clasp()) == shape->numFixedSlots());
     MOZ_ASSERT_IF(group->clasp()->flags & JSCLASS_BACKGROUND_FINALIZE,
                   IsBackgroundFinalized(kind));
     MOZ_ASSERT_IF(group->clasp()->finalize,
                   heap == js::gc::TenuredHeap ||
-                  (group->clasp()->flags & JSCLASS_FINALIZE_FROM_NURSERY));
+                  (group->clasp()->flags & JSCLASS_SKIP_NURSERY_FINALIZE));
     MOZ_ASSERT_IF(group->hasUnanalyzedPreliminaryObjects(),
                   heap == js::gc::TenuredHeap);
 
     // Non-native classes cannot have reserved slots or private data, and the
     // objects can't have any fixed slots, for compatibility with
     // GetReservedOrProxyPrivateSlot.
     MOZ_ASSERT_IF(!group->clasp()->isNative(), JSCLASS_RESERVED_SLOTS(group->clasp()) == 0);
     MOZ_ASSERT_IF(!group->clasp()->isNative(), !group->clasp()->hasPrivate());
--- a/js/src/tests/jstests.py
+++ b/js/src/tests/jstests.py
@@ -8,17 +8,17 @@ See the adjacent README.txt for more det
 from __future__ import print_function
 
 import os, sys, textwrap
 from os.path import abspath, dirname, isfile, realpath
 from copy import copy
 from subprocess import list2cmdline, call
 
 from lib.results import NullTestOutput
-from lib.tests import TestCase, TBPL_FLAGS
+from lib.tests import TestCase, TBPL_FLAGS, TBPL_DEBUG_FLAGS
 from lib.results import ResultsSink
 from lib.progressbar import ProgressBar
 
 if sys.platform.startswith('linux') or sys.platform.startswith('darwin'):
     from lib.tasks_unix import run_all_tests
 else:
     from lib.tasks_win import run_all_tests
 
@@ -92,16 +92,19 @@ def parse_args():
                           ' (in seconds).')
     harness_og.add_option('-a', '--args', dest='shell_args', default='',
                           help='Extra args to pass to the JS shell.')
     harness_og.add_option('--jitflags', default='',
                           help="Obsolete. Does nothing.")
     harness_og.add_option('--tbpl', action='store_true',
                           help='Runs each test in all configurations tbpl'
                           ' tests.')
+    harness_og.add_option('--tbpl-debug', action='store_true',
+                          help='Runs each test in some faster configurations'
+                          ' tbpl tests.')
     harness_og.add_option('-g', '--debug', action='store_true',
                           help='Run a test in debugger.')
     harness_og.add_option('--debugger', default='gdb -q --args',
                           help='Debugger command.')
     harness_og.add_option('-J', '--jorendb', action='store_true',
                           help='Run under JS debugger.')
     harness_og.add_option('--passthrough', action='store_true',
                           help='Run tests with stdin/stdout attached to'
@@ -264,19 +267,19 @@ def load_tests(options, requested_paths,
                               xul_tester)
     skip_list = []
 
     if options.make_manifests:
         manifest.make_manifests(options.make_manifests, test_list)
         sys.exit()
 
     # Create a new test list. Apply each TBPL configuration to every test.
-    if options.tbpl:
+    if options.tbpl or options.tbpl_debug:
         new_test_list = []
-        flags_list = TBPL_FLAGS
+        flags_list = TBPL_FLAGS if options.tbpl else TBPL_DEBUG_FLAGS
         for test in test_list:
             for jitflags in flags_list:
                 tmp_test = copy(test)
                 tmp_test.jitflags = copy(test.jitflags)
                 tmp_test.jitflags.extend(jitflags)
                 new_test_list.append(tmp_test)
         test_list = new_test_list
 
--- a/js/src/tests/lib/tests.py
+++ b/js/src/tests/lib/tests.py
@@ -15,16 +15,22 @@ TBPL_FLAGS = [
     [], # no flags, normal baseline and ion
     ['--ion-eager', '--ion-offthread-compile=off'], # implies --baseline-eager
     ['--ion-eager', '--ion-offthread-compile=off',
      '--ion-check-range-analysis', '--ion-extra-checks', '--no-sse3', '--no-threads'],
     ['--baseline-eager'],
     ['--baseline-eager', '--no-fpu'],
     ['--no-baseline', '--no-ion'],
 ]
+# Run reduced variants on debug builds, since they take longer time.
+TBPL_DEBUG_FLAGS = [
+    [], # no flags, normal baseline and ion
+    ['--ion-eager', '--ion-offthread-compile=off'], # implies --baseline-eager
+    ['--baseline-eager'],
+]
 
 def do_run_cmd(cmd):
     l = [None, None]
     th_run_cmd(cmd, l)
     return l[1]
 
 def set_limits():
     # resource module not supported on all platforms
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -12,23 +12,25 @@
 
 #include "gc/Marking.h"
 #include "js/Value.h"
 #include "vm/Debugger.h"
 #include "vm/TypedArrayCommon.h"
 
 #include "jsobjinlines.h"
 
+#include "gc/Nursery-inl.h"
 #include "vm/ArrayObject-inl.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
 
 using JS::GenericNaN;
 using mozilla::DebugOnly;
+using mozilla::PodCopy;
 using mozilla::RoundUpPow2;
 
 static const ObjectElements emptyElementsHeader(0, 0);
 
 /* Objects with no elements share one empty set of elements. */
 HeapSlot* const js::emptyObjectElements =
     reinterpret_cast<HeapSlot*>(uintptr_t(&emptyElementsHeader) + sizeof(ObjectElements));
 
@@ -372,97 +374,72 @@ NativeObject::setSlotSpan(ExclusiveConte
 
     if (!updateSlotsForSpan(cx, oldSpan, span))
         return false;
 
     lastProperty()->base()->setSlotSpan(span);
     return true;
 }
 
-// This will not run the garbage collector.  If a nursery cannot accomodate the slot array
-// an attempt will be made to place the array in the tenured area.
-static HeapSlot*
-AllocateSlots(ExclusiveContext* cx, JSObject* obj, uint32_t nslots)
-{
-    if (cx->isJSContext())
-        return cx->asJSContext()->runtime()->gc.nursery.allocateSlots(obj, nslots);
-    return obj->zone()->pod_malloc<HeapSlot>(nslots);
-}
-
-// This will not run the garbage collector.  If a nursery cannot accomodate the slot array
-// an attempt will be made to place the array in the tenured area.
-//
-// If this returns null then the old slots will be left alone.
-static HeapSlot*
-ReallocateSlots(ExclusiveContext* cx, JSObject* obj, HeapSlot* oldSlots,
-                uint32_t oldCount, uint32_t newCount)
-{
-    if (cx->isJSContext()) {
-        return cx->asJSContext()->runtime()->gc.nursery.reallocateSlots(obj, oldSlots,
-                                                                        oldCount, newCount);
-    }
-    return obj->zone()->pod_realloc<HeapSlot>(oldSlots, oldCount, newCount);
-}
-
 bool
 NativeObject::growSlots(ExclusiveContext* cx, uint32_t oldCount, uint32_t newCount)
 {
     MOZ_ASSERT(newCount > oldCount);
     MOZ_ASSERT_IF(!is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
 
     /*
      * Slot capacities are determined by the span of allocated objects. Due to
      * the limited number of bits to store shape slots, object growth is
      * throttled well before the slot capacity can overflow.
      */
     NativeObject::slotsSizeMustNotOverflow();
     MOZ_ASSERT(newCount < NELEMENTS_LIMIT);
 
     if (!oldCount) {
-        slots_ = AllocateSlots(cx, this, newCount);
+        slots_ = AllocateObjectBuffer<HeapSlot>(cx, this, newCount);
         if (!slots_)
             return false;
         Debug_SetSlotRangeToCrashOnTouch(slots_, newCount);
         return true;
     }
 
-    HeapSlot* newslots = ReallocateSlots(cx, this, slots_, oldCount, newCount);
+    HeapSlot* newslots = ReallocateObjectBuffer<HeapSlot>(cx, this, slots_, oldCount, newCount);
     if (!newslots)
         return false;  /* Leave slots at its old size. */
 
     slots_ = newslots;
 
     Debug_SetSlotRangeToCrashOnTouch(slots_ + oldCount, newCount - oldCount);
 
     return true;
 }
 
 static void
 FreeSlots(ExclusiveContext* cx, HeapSlot* slots)
 {
     // Note: threads without a JSContext do not have access to GGC nursery allocated things.
     if (cx->isJSContext())
-        return cx->asJSContext()->runtime()->gc.nursery.freeSlots(slots);
+        return cx->asJSContext()->runtime()->gc.nursery.freeBuffer(slots);
     js_free(slots);
 }
 
 void
 NativeObject::shrinkSlots(ExclusiveContext* cx, uint32_t oldCount, uint32_t newCount)
 {
     MOZ_ASSERT(newCount < oldCount);
 
     if (newCount == 0) {
         FreeSlots(cx, slots_);
         slots_ = nullptr;
         return;
     }
 
     MOZ_ASSERT_IF(!is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
 
-    HeapSlot* newslots = ReallocateSlots(cx, this, slots_, oldCount, newCount);
+    HeapSlot* newslots = ReallocateObjectBuffer<HeapSlot>(cx, this, slots_, oldCount, newCount);
     if (!newslots)
         return;  /* Leave slots at its old size. */
 
     slots_ = newslots;
 }
 
 /* static */ bool
 NativeObject::sparsifyDenseElement(ExclusiveContext* cx, HandleNativeObject obj, uint32_t index)
@@ -659,41 +636,16 @@ NativeObject::maybeDensifySparseElements
      * to grow the object.
      */
     if (!obj->clearFlag(cx, BaseShape::INDEXED))
         return ED_FAILED;
 
     return ED_OK;
 }
 
-// This will not run the garbage collector.  If a nursery cannot accomodate the element array
-// an attempt will be made to place the array in the tenured area.
-static ObjectElements*
-AllocateElements(ExclusiveContext* cx, JSObject* obj, uint32_t nelems)
-{
-    if (cx->isJSContext())
-        return cx->asJSContext()->runtime()->gc.nursery.allocateElements(obj, nelems);
-    return reinterpret_cast<js::ObjectElements*>(obj->zone()->pod_malloc<HeapSlot>(nelems));
-}
-
-// This will not run the garbage collector.  If a nursery cannot accomodate the element array
-// an attempt will be made to place the array in the tenured area.
-static ObjectElements*
-ReallocateElements(ExclusiveContext* cx, JSObject* obj, ObjectElements* oldHeader,
-                   uint32_t oldCount, uint32_t newCount)
-{
-    if (cx->isJSContext()) {
-        return cx->asJSContext()->runtime()->gc.nursery.reallocateElements(obj, oldHeader,
-                                                                           oldCount, newCount);
-    }
-    return reinterpret_cast<js::ObjectElements*>(
-            obj->zone()->pod_realloc<HeapSlot>(reinterpret_cast<HeapSlot*>(oldHeader),
-                                               oldCount, newCount));
-}
-
 // Round up |reqAllocated| to a good size. Up to 1 Mebi (i.e. 1,048,576) the
 // slot count is usually a power-of-two:
 //
 //   8, 16, 32, 64, ..., 256 Ki, 512 Ki, 1 Mi
 //
 // Beyond that, we use this formula:
 //
 //   count(n+1) = Math.ceil(count(n) * 1.125)
@@ -815,29 +767,30 @@ NativeObject::growElements(ExclusiveCont
     MOZ_ASSERT(newCapacity > oldCapacity && newCapacity >= reqCapacity);
 
     // Don't let nelements get close to wrapping around uint32_t.
     if (newCapacity >= NELEMENTS_LIMIT)
         return false;
 
     uint32_t initlen = getDenseInitializedLength();
 
-    ObjectElements* newheader;
+    HeapSlot* oldHeaderSlots = reinterpret_cast<HeapSlot*>(getElementsHeader());
+    HeapSlot* newHeaderSlots;
     if (hasDynamicElements()) {
-        newheader = ReallocateElements(cx, this, getElementsHeader(), oldAllocated, newAllocated);
-        if (!newheader)
+        newHeaderSlots = ReallocateObjectBuffer<HeapSlot>(cx, this, oldHeaderSlots, oldAllocated, newAllocated);
+        if (!newHeaderSlots)
             return false;   // Leave elements at its old size.
     } else {
-        newheader = AllocateElements(cx, this, newAllocated);
-        if (!newheader)
+        newHeaderSlots = AllocateObjectBuffer<HeapSlot>(cx, this, newAllocated);
+        if (!newHeaderSlots)
             return false;   // Leave elements at its old size.
-        js_memcpy(newheader, getElementsHeader(),
-                  (ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value));
+        PodCopy(newHeaderSlots, oldHeaderSlots, ObjectElements::VALUES_PER_HEADER + initlen);
     }
 
+    ObjectElements* newheader = reinterpret_cast<ObjectElements*>(newHeaderSlots);
     newheader->capacity = newCapacity;
     elements_ = newheader->elements();
 
     Debug_SetSlotRangeToCrashOnTouch(elements_ + initlen, newCapacity - initlen);
 
     return true;
 }
 
@@ -858,23 +811,25 @@ NativeObject::shrinkElements(ExclusiveCo
     uint32_t reqAllocated = reqCapacity + ObjectElements::VALUES_PER_HEADER;
     uint32_t newAllocated = goodAllocated(reqAllocated);
     if (newAllocated == oldAllocated)
         return;  // Leave elements at its old size.
 
     MOZ_ASSERT(newAllocated > ObjectElements::VALUES_PER_HEADER);
     uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER;
 
-    ObjectElements* newheader = ReallocateElements(cx, this, getElementsHeader(),
-                                                   oldAllocated, newAllocated);
-    if (!newheader) {
+    HeapSlot* oldHeaderSlots = reinterpret_cast<HeapSlot*>(getElementsHeader());
+    HeapSlot* newHeaderSlots = ReallocateObjectBuffer<HeapSlot>(cx, this, oldHeaderSlots,
+                                                                oldAllocated, newAllocated);
+    if (!newHeaderSlots) {
         cx->recoverFromOutOfMemory();
         return;  // Leave elements at its old size.
     }
 
+    ObjectElements* newheader = reinterpret_cast<ObjectElements*>(newHeaderSlots);
     newheader->capacity = newCapacity;
     elements_ = newheader->elements();
 }
 
 /* static */ bool
 NativeObject::CopyElementsForWrite(ExclusiveContext* cx, NativeObject* obj)
 {
     MOZ_ASSERT(obj->denseElementsAreCopyOnWrite());
@@ -888,19 +843,20 @@ NativeObject::CopyElementsForWrite(Exclu
 
     uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER;
 
     if (newCapacity >= NELEMENTS_LIMIT)
         return false;
 
     JSObject::writeBarrierPre(obj->getElementsHeader()->ownerObject());
 
-    ObjectElements* newheader = AllocateElements(cx, obj, newAllocated);
-    if (!newheader)
+    HeapSlot* newHeaderSlots = AllocateObjectBuffer<HeapSlot>(cx, obj, newAllocated);
+    if (!newHeaderSlots)
         return false;
+    ObjectElements* newheader = reinterpret_cast<ObjectElements*>(newHeaderSlots);
     js_memcpy(newheader, obj->getElementsHeader(),
               (ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value));
 
     newheader->capacity = newCapacity;
     newheader->clearCopyOnWrite();
     obj->elements_ = newheader->elements();
 
     Debug_SetSlotRangeToCrashOnTouch(obj->elements_ + initlen, newCapacity - initlen);
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -516,17 +516,17 @@ JSRuntime::addSizeOfIncludingThis(mozill
         rtSizes->scriptData += mallocSizeOf(r.front());
 
     if (jitRuntime_)
         jitRuntime_->execAlloc().addSizeOfCode(&rtSizes->code);
 
     rtSizes->gc.marker += gc.marker.sizeOfExcludingThis(mallocSizeOf);
     rtSizes->gc.nurseryCommitted += gc.nursery.sizeOfHeapCommitted();
     rtSizes->gc.nurseryDecommitted += gc.nursery.sizeOfHeapDecommitted();
-    rtSizes->gc.nurseryHugeSlots += gc.nursery.sizeOfHugeSlots(mallocSizeOf);
+    rtSizes->gc.nurseryMallocedBuffers += gc.nursery.sizeOfMallocedBuffers(mallocSizeOf);
     gc.storeBuffer.addSizeOfExcludingThis(mallocSizeOf, &rtSizes->gc);
 }
 
 static bool
 InvokeInterruptCallback(JSContext* cx)
 {
     MOZ_ASSERT(cx->runtime()->requestDepth >= 1);
 
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -6,16 +6,17 @@
 
 #include "vm/UnboxedObject.h"
 
 #include "jit/JitCommon.h"
 #include "jit/Linker.h"
 
 #include "jsobjinlines.h"
 
+#include "gc/Nursery-inl.h"
 #include "vm/Shape-inl.h"
 
 using mozilla::ArrayLength;
 using mozilla::DebugOnly;
 using mozilla::PodCopy;
 using mozilla::UniquePtr;
 
 using namespace js;
@@ -1029,26 +1030,28 @@ UnboxedArrayObject::create(ExclusiveCont
         res = NewObjectWithGroup<UnboxedArrayObject>(cx, group, allocKind, newKind);
         if (!res)
             return nullptr;
         res->setInlineElements();
 
         size_t capacity = (GetGCKindBytes(allocKind) - offsetOfInlineElements()) / elementSize;
         res->setCapacityIndex(exactCapacityIndex(capacity));
     } else {
-        UniquePtr<uint8_t[], JS::FreePolicy> elements(
-            cx->zone()->pod_malloc<uint8_t>(length * elementSize));
-        if (!elements)
-            return nullptr;
-
         res = NewObjectWithGroup<UnboxedArrayObject>(cx, group, gc::AllocKind::OBJECT0, newKind);
         if (!res)
             return nullptr;
 
-        res->elements_ = elements.release();
+        res->elements_ = AllocateObjectBuffer<uint8_t>(cx, res, length * elementSize);
+        if (!res->elements_) {
+            // Make the object safe for GC.
+            res->setInlineElements();
+            res->setInitializedLength(0);
+            return nullptr;
+        }
+
         res->setCapacityIndex(CapacityMatchesLengthIndex);
     }
 
     res->setLength(length);
     res->setInitializedLength(0);
     return res;
 }
 
@@ -1140,20 +1143,61 @@ UnboxedArrayObject::objectMoved(JSObject
     // Fix up possible inline data pointer.
     if (src.hasInlineElements())
         dst.setInlineElements();
 }
 
 /* static */ void
 UnboxedArrayObject::finalize(FreeOp* fop, JSObject* obj)
 {
+    MOZ_ASSERT(!IsInsideNursery(obj));
     if (!obj->as<UnboxedArrayObject>().hasInlineElements())
         js_free(obj->as<UnboxedArrayObject>().elements());
 }
 
+/* static */ size_t
+UnboxedArrayObject::objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src,
+                                             gc::AllocKind allocKind)
+{
+    UnboxedArrayObject* ndst = &dst->as<UnboxedArrayObject>();
+    UnboxedArrayObject* nsrc = &src->as<UnboxedArrayObject>();
+    MOZ_ASSERT(ndst->elements() == nsrc->elements());
+
+    Nursery& nursery = trc->runtime()->gc.nursery;
+
+    if (!nursery.isInside(nsrc->elements())) {
+        nursery.removeMallocedBuffer(nsrc->elements());
+        return 0;
+    }
+
+    // Determine if we can use inline data for the target array. If this is
+    // possible, the nursery will have picked an allocation size that is large
+    // enough.
+    size_t nbytes = nsrc->capacity() * nsrc->elementSize();
+    if (offsetOfInlineElements() + nbytes <= GetGCKindBytes(allocKind)) {
+        ndst->setInlineElements();
+    } else {
+        MOZ_ASSERT(allocKind == gc::AllocKind::OBJECT0);
+
+        uint8_t* data = nsrc->zone()->pod_malloc<uint8_t>(nbytes);
+        if (!data)
+            CrashAtUnhandlableOOM("Failed to allocate unboxed array elements while tenuring.");
+        ndst->elements_ = data;
+    }
+
+    PodCopy(ndst->elements(), nsrc->elements(), nsrc->initializedLength() * nsrc->elementSize());
+
+    // Set a forwarding pointer for the element buffers in case they were
+    // preserved on the stack by Ion.
+    bool direct = nsrc->capacity() * nsrc->elementSize() >= sizeof(uintptr_t);
+    nursery.maybeSetForwardingPointer(trc, nsrc->elements(), ndst->elements(), direct);
+
+    return ndst->hasInlineElements() ? 0 : nbytes;
+}
+
 // Possible capacities for unboxed arrays. Some of these capacities might seem
 // a little weird, but were chosen to allow the inline data of objects of each
 // size to be fully utilized for arrays of the various types on both 32 bit and
 // 64 bit platforms.
 /* static */ const uint32_t
 UnboxedArrayObject::CapacityArray[] = {
     UINT32_MAX, // For CapacityMatchesLengthIndex.
     0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 13, 16, 17, 18, 20, 24, 26, 32, 34, 36, 48, 52, 64, 68,
@@ -1261,23 +1305,24 @@ UnboxedArrayObject::growElements(Exclusi
     MOZ_ASSERT(oldCapacity < cap);
     MOZ_ASSERT(cap <= newCapacity);
 
     // The allocation size computation below cannot have integer overflows.
     JS_STATIC_ASSERT(MaximumCapacity < UINT32_MAX / sizeof(double));
 
     uint8_t* newElements;
     if (hasInlineElements()) {
-        newElements = cx->zone()->pod_malloc<uint8_t>(newCapacity * elementSize());
+        newElements = AllocateObjectBuffer<uint8_t>(cx, this, newCapacity * elementSize());
         if (!newElements)
             return false;
         js_memcpy(newElements, elements(), initializedLength() * elementSize());
     } else {
-        newElements = cx->zone()->pod_realloc<uint8_t>(elements(), oldCapacity * elementSize(),
-                                                       newCapacity * elementSize());
+        newElements = ReallocateObjectBuffer<uint8_t>(cx, this, elements(),
+                                                      oldCapacity * elementSize(),
+                                                      newCapacity * elementSize());
         if (!newElements)
             return false;
     }
 
     elements_ = newElements;
     setCapacityIndex(newCapacityIndex);
 
     return true;
@@ -1294,19 +1339,19 @@ UnboxedArrayObject::shrinkElements(Exclu
     uint32_t newCapacity = computeCapacity(newCapacityIndex, 0);
 
     MOZ_ASSERT(cap < oldCapacity);
     MOZ_ASSERT(cap <= newCapacity);
 
     if (newCapacity >= oldCapacity)
         return;
 
-    uint8_t* newElements =
-        cx->zone()->pod_realloc<uint8_t>(elements(), oldCapacity * elementSize(),
-                                         newCapacity * elementSize());
+    uint8_t* newElements = ReallocateObjectBuffer<uint8_t>(cx, this, elements(),
+                                                           oldCapacity * elementSize(),
+                                                           newCapacity * elementSize());
     if (!newElements)
         return;
 
     elements_ = newElements;
     setCapacityIndex(newCapacityIndex);
 }
 
 bool
@@ -1491,17 +1536,18 @@ UnboxedArrayObject::obj_enumerate(JSCont
     }
     return properties.append(NameToId(cx->names().length));
 }
 
 const Class UnboxedArrayObject::class_ = {
     "Array",
     Class::NON_NATIVE |
     JSCLASS_IMPLEMENTS_BARRIERS |
-    0 /* FIXME using this flag can severely hurt performance: JSCLASS_BACKGROUND_FINALIZE */,
+    JSCLASS_SKIP_NURSERY_FINALIZE |
+    JSCLASS_BACKGROUND_FINALIZE,
     nullptr,        /* addProperty */
     nullptr,        /* delProperty */
     nullptr,        /* getProperty */
     nullptr,        /* setProperty */
     nullptr,        /* enumerate   */
     nullptr,        /* resolve     */
     nullptr,        /* mayResolve  */
     nullptr,        /* convert     */
--- a/js/src/vm/UnboxedObject.h
+++ b/js/src/vm/UnboxedObject.h
@@ -394,16 +394,19 @@ class UnboxedArrayObject : public JSObje
 
     void fillAfterConvert(ExclusiveContext* cx,
                           const AutoValueVector& values, size_t* valueCursor);
 
     static void trace(JSTracer* trc, JSObject* object);
     static void objectMoved(JSObject* obj, const JSObject* old);
     static void finalize(FreeOp* fop, JSObject* obj);
 
+    static size_t objectMovedDuringMinorGC(JSTracer* trc, JSObject* dst, JSObject* src,
+                                           gc::AllocKind allocKind);
+
     uint8_t* elements() {
         return elements_;
     }
 
     bool hasInlineElements() const {
         return elements_ == &inlineElements_[0];
     }
 
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -940,17 +940,21 @@ xpc::CreateSandboxObject(JSContext* cx, 
         JSAutoCompartment ac(cx, sandbox);
 
         if (options.proto) {
             bool ok = JS_WrapObject(cx, &options.proto);
             if (!ok)
                 return NS_ERROR_XPC_UNEXPECTED;
 
             // Now check what sort of thing we've got in |proto|
-            JSObject* unwrappedProto = js::UncheckedUnwrap(options.proto, false);
+            JSObject* unwrappedProto = js::CheckedUnwrap(options.proto, false);
+            if (!unwrappedProto) {
+                JS_ReportError(cx, "Sandbox must subsume sandboxPrototype");
+                return NS_ERROR_INVALID_ARG;
+            }
             const js::Class* unwrappedClass = js::GetObjectClass(unwrappedProto);
             if (IS_WN_CLASS(unwrappedClass) ||
                 mozilla::dom::IsDOMClass(Jsvalify(unwrappedClass))) {
                 // Wrap it up in a proxy that will do the right thing in terms
                 // of this-binding for methods.
                 RootedValue priv(cx, ObjectValue(*options.proto));
                 options.proto = js::NewProxyObject(cx, &xpc::sandboxProxyHandler,
                                                    priv, nullptr);
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -2548,18 +2548,18 @@ ReportJSRuntimeExplicitTreeStats(const J
     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/marker"),
         KIND_HEAP, rtStats.runtime.gc.marker,
         "The GC mark stack and gray roots.");
 
     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/nursery-committed"),
         KIND_NONHEAP, rtStats.runtime.gc.nurseryCommitted,
         "Memory being used by the GC's nursery.");
 
-    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/nursery-huge-slots"),
-        KIND_NONHEAP, rtStats.runtime.gc.nurseryHugeSlots,
+    RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/nursery-malloced-buffers"),
+        KIND_NONHEAP, rtStats.runtime.gc.nurseryMallocedBuffers,
         "Out-of-line slots and elements belonging to objects in the "
         "nursery.");
 
     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/vals"),
         KIND_HEAP, rtStats.runtime.gc.storeBufferVals,
         "Values in the store buffer.");
 
     RREPORT_BYTES(rtPath + NS_LITERAL_CSTRING("runtime/gc/store-buffer/cells"),
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -22,16 +22,17 @@
 #include "nsArrayEnumerator.h"
 #include "nsCOMArray.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsIJSRuntimeService.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsJSPrincipals.h"
 #include "xpcpublic.h"
+#include "xpcprivate.h"
 #include "BackstagePass.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIPrincipal.h"
 #include "nsJSUtils.h"
 
 #include "base/histogram.h"
 
 #ifdef ANDROID
@@ -627,16 +628,45 @@ SimulateActivityCallback(JSContext* cx, 
     if (args.length() != 1 || !args[0].isBoolean()) {
         JS_ReportError(cx, "Wrong number of arguments");
         return false;
     }
     xpc::SimulateActivityCallback(args[0].toBoolean());
     return true;
 }
 
+static bool
+RegisterAppManifest(JSContext* cx, unsigned argc, jsval* vp)
+{
+    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+    if (args.length() != 1) {
+        JS_ReportError(cx, "Wrong number of arguments");
+        return false;
+    }
+    if (!args[0].isObject()) {
+        JS_ReportError(cx, "Expected object as argument 1 to registerAppManifest");
+        return false;
+    }
+
+    Rooted<JSObject*> arg1(cx, &args[0].toObject());
+    nsCOMPtr<nsIFile> file;
+    nsresult rv = nsXPConnect::XPConnect()->
+        WrapJS(cx, arg1, NS_GET_IID(nsIFile), getter_AddRefs(file));
+    if (NS_FAILED(rv)) {
+        XPCThrower::Throw(rv, cx);
+        return false;
+    }
+    rv = XRE_AddManifestLocation(NS_APP_LOCATION, file);
+    if (NS_FAILED(rv)) {
+        XPCThrower::Throw(rv, cx);
+        return false;
+    }
+    return true;
+}
+
 static const JSFunctionSpec glob_functions[] = {
     JS_FS("print",           Print,          0,0),
     JS_FS("readline",        ReadLine,       1,0),
     JS_FS("load",            Load,           1,0),
     JS_FS("quit",            Quit,           0,0),
     JS_FS("ignoreReportedErrors", IgnoreReportedErrors, 1,0),
     JS_FS("version",         Version,        1,0),
     JS_FS("build",           BuildDate,      0,0),
@@ -647,16 +677,17 @@ static const JSFunctionSpec glob_functio
     JS_FS("gczeal",          GCZeal,         1,0),
 #endif
     JS_FS("options",         Options,        0,0),
     JS_FS("sendCommand",     SendCommand,    1,0),
     JS_FS("atob",            Atob,           1,0),
     JS_FS("btoa",            Btoa,           1,0),
     JS_FS("setInterruptCallback", SetInterruptCallback, 1,0),
     JS_FS("simulateActivityCallback", SimulateActivityCallback, 1,0),
+    JS_FS("registerAppManifest", RegisterAppManifest, 1, 0),
     JS_FS_END
 };
 
 static bool
 env_setProperty(JSContext* cx, HandleObject obj, HandleId id, MutableHandleValue vp,
                 ObjectOpResult& result)
 {
 /* XXX porting may be easy, but these don't seem to supply setenv by default */
@@ -1336,17 +1367,17 @@ XRE_XPCShellMain(int argc, char** argv, 
             return usage();
 
         nsCOMPtr<nsIFile> lf;
         rv = XRE_GetFileFromPath(argv[2], getter_AddRefs(lf));
         if (NS_FAILED(rv)) {
             printf("Couldn't get manifest file.\n");
             return 1;
         }
-        XRE_AddManifestLocation(NS_COMPONENT_LOCATION, lf);
+        XRE_AddManifestLocation(NS_APP_LOCATION, lf);
 
         argc -= 2;
         argv += 2;
     }
 
 #ifdef MOZ_CRASHREPORTER
     const char* val = getenv("MOZ_CRASHREPORTER");
     if (val && *val) {
--- a/js/xpconnect/tests/unit/test_attributes.js
+++ b/js/xpconnect/tests/unit/test_attributes.js
@@ -3,18 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 function run_test() {
 
   // Load the component manifests.
-  Components.manager.autoRegister(do_get_file('../components/native/xpctest.manifest'));
-  Components.manager.autoRegister(do_get_file('../components/js/xpctest.manifest'));
+  registerAppManifest(do_get_file('../components/native/xpctest.manifest'));
+  registerAppManifest(do_get_file('../components/js/xpctest.manifest'));
 
   // Test for each component.
   test_component_readwrite("@mozilla.org/js/xpc/test/native/ObjectReadWrite;1");
   test_component_readwrite("@mozilla.org/js/xpc/test/js/ObjectReadWrite;1");
   test_component_readonly("@mozilla.org/js/xpc/test/native/ObjectReadOnly;1");
   test_component_readonly("@mozilla.org/js/xpc/test/js/ObjectReadOnly;1");
 }
 
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug1151385.js
@@ -0,0 +1,9 @@
+function run_test()
+{
+  try {
+    var sandbox = new Components.utils.Sandbox(null, {"sandboxPrototype" : {}});
+    do_check_true(false);
+  } catch (e) {
+    do_check_true(/must subsume sandboxPrototype/.test(e));
+  }
+}
--- a/js/xpconnect/tests/unit/test_params.js
+++ b/js/xpconnect/tests/unit/test_params.js
@@ -3,18 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 function run_test() {
 
   // Load the component manifests.
-  Components.manager.autoRegister(do_get_file('../components/native/xpctest.manifest'));
-  Components.manager.autoRegister(do_get_file('../components/js/xpctest.manifest'));
+  registerAppManifest(do_get_file('../components/native/xpctest.manifest'));
+  registerAppManifest(do_get_file('../components/js/xpctest.manifest'));
 
   // Test for each component.
   test_component("@mozilla.org/js/xpc/test/native/Params;1");
   test_component("@mozilla.org/js/xpc/test/js/Params;1");
 }
 
 function test_component(contractid) {
 
--- a/js/xpconnect/tests/unit/test_returncode.js
+++ b/js/xpconnect/tests/unit/test_returncode.js
@@ -11,18 +11,18 @@ function getConsoleMessages() {
   let messages = [m.toString() for (m of consoleService.getMessageArray())];
   // reset ready for the next call.
   consoleService.reset();
   return messages;
 }
 
 function run_test() {
   // Load the component manifests.
-  Cm.autoRegister(do_get_file('../components/native/xpctest.manifest'));
-  Cm.autoRegister(do_get_file('../components/js/xpctest.manifest'));
+  registerAppManifest(do_get_file('../components/native/xpctest.manifest'));
+  registerAppManifest(do_get_file('../components/js/xpctest.manifest'));
 
   // and the tests.
   test_simple();
   test_nested();
 }
 
 function test_simple() {
   let parent = Cc["@mozilla.org/js/xpc/test/native/ReturnCodeParent;1"]
--- a/js/xpconnect/tests/unit/xpcshell.ini
+++ b/js/xpconnect/tests/unit/xpcshell.ini
@@ -50,16 +50,17 @@ support-files =
 [test_bug1033253.js]
 [test_bug1033920.js]
 [test_bug1033927.js]
 [test_bug1034262.js]
 [test_bug1082450.js]
 [test_bug1081990.js]
 [test_bug1110546.js]
 [test_bug1150771.js]
+[test_bug1151385.js]
 [test_bug_442086.js]
 [test_callFunctionWithAsyncStack.js]
 [test_file.js]
 [test_blob.js]
 [test_blob2.js]
 [test_file2.js]
 [test_import.js]
 [test_import_fail.js]
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -2683,17 +2683,17 @@ nsCSSFrameConstructor::ConstructRootFram
 
   // Bind the viewport frame to the root view
   nsView* rootView = mPresShell->GetViewManager()->GetRootView();
   viewportFrame->SetView(rootView);
 
   nsContainerFrame::SyncFrameViewProperties(mPresShell->GetPresContext(), viewportFrame,
                                             viewportPseudoStyle, rootView);
   nsContainerFrame::SyncWindowProperties(mPresShell->GetPresContext(), viewportFrame,
-                                         rootView);
+                                         rootView, nullptr, nsContainerFrame::SET_ASYNC);
 
   // Make it an absolute container for fixed-pos elements
   viewportFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
   viewportFrame->MarkAsAbsoluteContainingBlock();
 
   return viewportFrame;
 }
 
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -135,18 +135,18 @@ typedef struct CapturingContentInfo {
   bool mPointerLock;
   bool mRetargetToElement;
   bool mPreventDrag;
   mozilla::StaticRefPtr<nsIContent> mContent;
 } CapturingContentInfo;
 
 // d910f009-d209-74c1-6b04-30c83c051c78
 #define NS_IPRESSHELL_IID \
-  { 0xd910f009, 0xd209, 0x74c1, \
-    { 0x6b, 0x04, 0x30, 0xc8, 0x3c, 0x05, 0x1c, 0x78 } }
+  { 0x025264c6, 0x0b12, 0x4804, \
+    { 0xa3, 0x3e, 0xb7, 0x73, 0xf2, 0x19, 0x48, 0x90 } }
 
 // debug VerifyReflow flags
 #define VERIFY_REFLOW_ON                    0x01
 #define VERIFY_REFLOW_NOISY                 0x02
 #define VERIFY_REFLOW_ALL                   0x04
 #define VERIFY_REFLOW_DUMP_COMMANDS         0x08
 #define VERIFY_REFLOW_NOISY_RC              0x10
 #define VERIFY_REFLOW_REALLY_NOISY_RC       0x20
@@ -1656,16 +1656,18 @@ public:
 
   void SetNeverPainting(bool aNeverPainting) {
     mIsNeverPainting = aNeverPainting;
   }
 
   bool HasPendingReflow() const
     { return mReflowScheduled || mReflowContinueTimer; }
 
+  void SyncWindowProperties(nsView* aView);
+
 protected:
   friend class nsRefreshDriver;
 
   // IMPORTANT: The ownership implicit in the following member variables
   // has been explicitly checked.  If you add any members to this class,
   // please make the ownership explicit (pinkerton, scc).
 
   // These are the same Document and PresContext owned by the DocViewer.
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -3111,20 +3111,22 @@ nsLayoutUtils::PaintFrame(nsRenderingCon
     if (ignoreViewportScrolling && presContext->IsRootContentDocument()) {
       if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
         if (nsIContent* content = rootScrollFrame->GetContent()) {
           id = nsLayoutUtils::FindOrCreateIDFor(content);
         }
       }
     }
 #if !defined(MOZ_WIDGET_ANDROID) || defined(MOZ_ANDROID_APZ)
-    else if (presShell->GetDocument() && presShell->GetDocument()->IsRootDisplayDocument()) {
+    else if (presShell->GetDocument() && presShell->GetDocument()->IsRootDisplayDocument()
+        && !presShell->GetRootScrollFrame()) {
       // In cases where the root document is a XUL document, we want to take
       // the ViewID from the root element, as that will be the ViewID of the
-      // root APZC in the tree.
+      // root APZC in the tree. Skip doing this in cases where we know
+      // nsGfxScrollFrame::BuilDisplayList will do it instead.
       if (dom::Element* element = presShell->GetDocument()->GetDocumentElement()) {
         id = nsLayoutUtils::FindOrCreateIDFor(element);
       }
     }
 #endif
 
     nsDisplayListBuilder::AutoCurrentScrollParentIdSetter idSetter(&builder, id);
 
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -9240,17 +9240,18 @@ PresShell::DoReflow(nsIFrame* target, bo
   // Always use boundsRelativeToTarget here, not desiredSize.GetVisualOverflowArea(),
   // because for root frames (where they could be different, since root frames
   // are allowed to have overflow) the root view bounds need to match the
   // viewport bounds; the view manager "window dimensions" code depends on it.
   nsContainerFrame::SyncFrameViewAfterReflow(mPresContext, target,
                                              target->GetView(),
                                              boundsRelativeToTarget);
   nsContainerFrame::SyncWindowProperties(mPresContext, target,
-                                         target->GetView(), &rcx);
+                                         target->GetView(), &rcx,
+                                         nsContainerFrame::SET_ASYNC);
 
   target->DidReflow(mPresContext, nullptr, nsDidReflowStatus::FINISHED);
   if (target == rootFrame && size.BSize(wm) == NS_UNCONSTRAINEDSIZE) {
     mPresContext->SetVisibleArea(boundsRelativeToTarget);
   }
 
 #ifdef DEBUG
   mCurrentReflowRoot = nullptr;
@@ -11076,8 +11077,18 @@ void
 PresShell::ResumePainting()
 {
   if (GetPresContext()->RefreshDriver()->PresContext() != GetPresContext())
     return;
 
   mPaintingIsFrozen = false;
   GetPresContext()->RefreshDriver()->Thaw();
 }
+
+void
+nsIPresShell::SyncWindowProperties(nsView* aView)
+{
+  nsIFrame* frame = aView->GetFrame();
+  if (frame && mPresContext) {
+    nsRenderingContext rcx(CreateReferenceRenderingContext());
+    nsContainerFrame::SyncWindowProperties(mPresContext, frame, aView, &rcx, 0);
+  }
+}
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -604,24 +604,25 @@ IsTopLevelWidget(nsIWidget* aWidget)
          windowType == eWindowType_dialog ||
          windowType == eWindowType_sheet;
   // popups aren't toplevel so they're not handled here
 }
 
 void
 nsContainerFrame::SyncWindowProperties(nsPresContext*       aPresContext,
                                        nsIFrame*            aFrame,
-                                       nsView*             aView,
-                                       nsRenderingContext*  aRC)
+                                       nsView*              aView,
+                                       nsRenderingContext*  aRC,
+                                       uint32_t             aFlags)
 {
 #ifdef MOZ_XUL
   if (!aView || !nsCSSRendering::IsCanvasFrame(aFrame) || !aView->HasWidget())
     return;
 
-  nsIWidget* windowWidget = GetPresContextContainerWidget(aPresContext);
+  nsCOMPtr<nsIWidget> windowWidget = GetPresContextContainerWidget(aPresContext);
   if (!windowWidget || !IsTopLevelWidget(windowWidget))
     return;
 
   nsViewManager* vm = aView->GetViewManager();
   nsView* rootView = vm->GetRootView();
 
   if (aView != rootView)
     return;
@@ -645,24 +646,37 @@ nsContainerFrame::SyncWindowProperties(n
     // even if the HTML doesn't have a background-color set.
     return;
   }
 
   nsIFrame *rootFrame = aPresContext->PresShell()->FrameConstructor()->GetRootElementStyleFrame();
   if (!rootFrame)
     return;
 
+  if (aFlags & SET_ASYNC) {
+    aView->SetNeedsWindowPropertiesSync();
+    return;
+  }
+
+  nsRefPtr<nsPresContext> kungFuDeathGrip(aPresContext);
+  nsWeakFrame weak(rootFrame);
+
   nsTransparencyMode mode = nsLayoutUtils::GetFrameTransparency(aFrame, rootFrame);
-  nsIWidget* viewWidget = aView->GetWidget();
+  int32_t shadow = rootFrame->StyleUIReset()->mWindowShadow;
+  nsCOMPtr<nsIWidget> viewWidget = aView->GetWidget();
   viewWidget->SetTransparencyMode(mode);
-  windowWidget->SetWindowShadowStyle(rootFrame->StyleUIReset()->mWindowShadow);
+  windowWidget->SetWindowShadowStyle(shadow);
 
   if (!aRC)
     return;
-  
+
+  if (!weak.IsAlive()) {
+    return;
+  }
+
   nsBoxLayoutState aState(aPresContext, aRC);
   nsSize minSize = rootFrame->GetMinSize(aState);
   nsSize maxSize = rootFrame->GetMaxSize(aState);
 
   SetSizeConstraints(aPresContext, windowWidget, minSize, maxSize);
 #endif
 }
 
--- a/layout/generic/nsContainerFrame.h
+++ b/layout/generic/nsContainerFrame.h
@@ -175,20 +175,26 @@ public:
   static void SyncFrameViewAfterReflow(nsPresContext* aPresContext,
                                        nsIFrame*       aFrame,
                                        nsView*        aView,
                                        const nsRect&   aVisualOverflowArea,
                                        uint32_t        aFlags = 0);
 
   // Syncs properties to the top level view and window, like transparency and
   // shadow.
+  // The SET_ASYNC indicates that the actual nsIWidget calls to sync the window
+  // properties should be done async.
+  enum {
+    SET_ASYNC = 0x01,
+  };
   static void SyncWindowProperties(nsPresContext*       aPresContext,
                                    nsIFrame*            aFrame,
-                                   nsView*             aView,
-                                   nsRenderingContext*  aRC = nullptr);
+                                   nsView*              aView,
+                                   nsRenderingContext*  aRC,
+                                   uint32_t             aFlags);
 
   // Sets the view's attributes from the frame style.
   // - visibility
   // - clip
   // Call this when one of these styles changes or when the view has just
   // been created.
   // @param aStyleContext can be null, in which case the frame's style context is used
   static void SyncFrameViewProperties(nsPresContext*  aPresContext,
--- a/layout/generic/nsHTMLReflowState.cpp
+++ b/layout/generic/nsHTMLReflowState.cpp
@@ -354,16 +354,23 @@ nsHTMLReflowState::Init(nsPresContext* a
     }
   }
 
   NS_WARN_IF_FALSE(AvailableISize() != NS_UNCONSTRAINEDSIZE,
                    "have unconstrained inline-size; this should only result from "
                    "very large sizes, not attempts at intrinsic inline-size "
                    "calculation");
 
+  if (AvailableBSize() != NS_UNCONSTRAINEDSIZE && parentReflowState &&
+      parentReflowState->GetWritingMode().IsOrthogonalTo(mWritingMode)) {
+    // Orthogonal frames are always reflowed with unconstrained block-size,
+    // to avoid incomplete reflow across an orthogonal boundary.
+    AvailableBSize() = NS_UNCONSTRAINEDSIZE;
+  }
+
   mStylePosition = frame->StylePosition();
   mStyleDisplay = frame->StyleDisplay();
   mStyleVisibility = frame->StyleVisibility();
   mStyleBorder = frame->StyleBorder();
   mStyleMargin = frame->StyleMargin();
   mStylePadding = frame->StylePadding();
   mStyleText = frame->StyleText();
 
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/1152941-1-orthogonal-blocksize-overflow-ref.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<style>
+html {
+  -webkit-writing-mode: vertical-rl;
+  writing-mode: vertical-rl;
+  font: 16px/24px monospace;
+  padding: 20px;
+}
+body {
+  width: 40em;
+  height: 25em;
+}
+blockquote {
+  -webkit-writing-mode: horizontal-tb;
+  writing-mode: horizontal-tb;
+  width: 20em;
+  color: transparent;
+}
+</style>
+</head>
+
+<body>
+  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ac fringilla quam,
+  eu ultricies augue.</p>
+
+  <blockquote>
+    <!-- Short enough text that it won't overflow. -->
+    <p>Ut accumsan dui eu elit dapibus rutrum. Nunc tristique urna eget ex dictum
+    placerat. Nunc venenatis enim sed odio iaculis, consequat consectetur sem
+    elementum.</p>
+  </blockquote>
+
+  <p>Maecenas nec ornare ligula. Phasellus eleifend
+  elit leo, nec vestibulum sapien consectetur quis.</p>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/writing-mode/1152941-1-orthogonal-blocksize-overflow.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<style>
+html {
+  -webkit-writing-mode: vertical-rl;
+  writing-mode: vertical-rl;
+  font: 16px/24px monospace;
+  padding: 20px;
+}
+body {
+  width: 40em;
+  height: 25em;
+}
+blockquote {
+  -webkit-writing-mode: horizontal-tb;
+  writing-mode: horizontal-tb;
+  width: 20em;
+  color: transparent;
+}
+</style>
+</head>
+
+<body>
+  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ac fringilla quam,
+  eu ultricies augue.</p>
+
+  <blockquote>
+    <!-- The (invisible) text in the orthogonal block should NOT make following
+         content vanish if it overflows the <body>'s height! -->
+    <p>Ut accumsan dui eu elit dapibus rutrum. Nunc tristique urna eget ex dictum
+    placerat. Nunc venenatis enim sed odio iaculis, consequat consectetur sem
+    elementum.</p>
+
+    <p>Fusce eros eros, eleifend eget eros at, convallis tempor tortor. Cras
+    at gravida leo. Proin ultricies ipsum vitae felis suscipit, a tincidunt orci
+    mattis. Cras in suscipit mauris.</p>
+
+    <p>Etiam eu pellentesque nisi. Quisque
+    ullamcorper dui odio, eu feugiat nunc interdum vitae. Morbi egestas dolor a
+    nulla pellentesque, faucibus tincidunt diam facilisis. Suspendisse at urna
+    varius, pellentesque nibh non, venenatis ante.</p>
+
+    <p>Nullam aliquet orci vel dui
+    dapibus, nec facilisis enim interdum. Morbi condimentum venenatis commodo. Sed
+    viverra diam nec lacinia congue. Etiam ultrices luctus volutpat.</p>
+  </blockquote>
+
+  <p>Maecenas nec ornare ligula. Phasellus eleifend
+  elit leo, nec vestibulum sapien consectetur quis.</p>
+</body>
+</html>
--- a/layout/reftests/writing-mode/reftest.list
+++ b/layout/reftests/writing-mode/reftest.list
@@ -123,11 +123,12 @@ test-pref(dom.meta-viewport.enabled,true
 test-pref(dom.meta-viewport.enabled,true) test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) != font-inflation-1d.html font-inflation-1-ref.html
 pref(dom.meta-viewport.enabled,true) pref(font.size.inflation.emPerLine,15) pref(font.size.inflation.forceEnabled,true) pref(font.size.inflation.lineThreshold,0) != font-inflation-1c.html font-inflation-1d.html
 test-pref(dom.meta-viewport.enabled,true) test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == font-inflation-1c.html font-inflation-1c-ref.html
 test-pref(dom.meta-viewport.enabled,true) test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == font-inflation-1d.html font-inflation-1d-ref.html
 
 == 1144501-1a-block-end-margin-orthogonal-size.html 1144501-1-block-end-margin-orthogonal-size-ref.html
 == 1144501-1b-block-end-margin-orthogonal-size.html 1144501-1-block-end-margin-orthogonal-size-ref.html
 == 1151993-1-orthogonal-block-size.html 1151993-1-orthogonal-block-size-ref.html
+== 1152941-1-orthogonal-blocksize-overflow.html 1152941-1-orthogonal-blocksize-overflow-ref.html
 == 1156021-text-indent-percent.html 1156021-text-indent-percent-ref.html
 == 1157752-upright-bidi.html 1157752-upright-bidi-ref.html
 == 1158549-1-vertical-block-size-constraints.html 1158549-1-vertical-block-size-constraints-ref.html
--- a/layout/style/test/chrome/chrome.ini
+++ b/layout/style/test/chrome/chrome.ini
@@ -5,12 +5,13 @@ support-files =
   bug535806-html.html
   bug535806-xul.xul
   hover_helper.html
 
 [test_addSheet.html]
 [test_additional_sheets.html]
 [test_author_specified_style.html]
 [test_bug1157097.html]
+[test_bug1160724.xul]
 [test_bug535806.xul]
 [test_hover.html]
 skip-if = buildapp == 'mulet'
 [test_moz_document_rules.html]
new file mode 100644
--- /dev/null
+++ b/layout/style/test/chrome/test_bug1160724.xul
@@ -0,0 +1,74 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+
+<?xml-stylesheet href="data:text/css,:root{--test:9px}" type="text/css"?>
+
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1160724
+-->
+<window title="Mozilla Bug 1160724" onload="test()"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1160724"
+     target="_blank">Mozilla Bug 1160724</a>
+  </body>
+
+  <script type="application/javascript">
+  <![CDATA[
+  var errorLogged = false;
+  const serv = Components.classes["@mozilla.org/consoleservice;1"]
+                         .getService(Components.interfaces.nsIConsoleService);
+  var listener = {
+    QueryInterface(iid) {
+      if (!iid.equals(Components.interfaces.nsISupports) &&
+          !iid.equals(Components.interfaces.nsIConsoleListener)) {
+        throw Components.results.NS_NOINTERFACE;
+      }
+      return this;
+    },
+
+    observe(msg) {
+      errorLogged = true;
+    }
+  };
+  serv.registerListener(listener);
+  ]]>
+  </script>
+
+  <vbox id="w" style="-moz-binding: url(#binding)">
+    <vbox id="v" style="display: none; transform: translateY(var(--test));" />
+  </vbox>
+
+  <bindings xmlns="http://www.mozilla.org/xbl">
+    <binding id="binding">
+      <implementation>
+        <constructor>this.firstChild</constructor>
+      </implementation>
+    </binding>
+  </bindings>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+  /** Test for Bug 1160724 **/
+  SimpleTest.waitForExplicitFinish();
+
+  function test() {
+    var v = document.getElementById("v");
+    is(getComputedStyle(v, "").transform, "matrix(1, 0, 0, 1, 0, 9)");
+
+    // nsIConsoleListeners are notified by a runnable.
+    setTimeout(() => {
+      ok(!errorLogged, "Should be no errors");
+      serv.unregisterListener(listener);
+      SimpleTest.finish();
+    })
+  }
+  ]]>
+  </script>
+</window>
--- a/layout/style/test/test_transitions_events.html
+++ b/layout/style/test/test_transitions_events.html
@@ -78,16 +78,17 @@ var got_two_target = false;
 var got_three_top = false;
 var got_three_right = false;
 var got_three_bottom = false;
 var got_three_left = false;
 var got_four_root = false;
 var got_body = false;
 var did_stops = false;
 var got_before = false;
+var got_after = false;
 
 document.documentElement.addEventListener("transitionend",
   function(event) {
     if (event.target == $("one")) {
       ok(!got_one_root, "transitionend on one on root");
       is(event.propertyName, "border-right-color",
          "propertyName for transitionend on one");
       is(event.elapsedTime, 0.5,
@@ -117,20 +118,23 @@ document.documentElement.addEventListene
          "elapsedTime for transitionend on body");
       got_body = true;
       finished_test();
     } else if (event.target == $("seven")) {
       if (!got_before) {
         got_before = true;
         is(event.pseudoElement, "::before");
       } else {
+        ok(!got_after, "transitionend on #seven::after");
+        got_after = true;
         is(event.pseudoElement, "::after");
       }
       is(event.propertyName, "color");
       is(event.isTrusted, true);
+      finished_test();
     } else {
       if (!did_stops &&
           (event.target == $("five") || event.target == $("six"))) {
         todo(false,
              "timeout to stop transitions firing later than it should be");
         return;
       }
       ok(false,
@@ -198,22 +202,25 @@ document.documentElement.addEventListene
     }
     is(event.elapsedTime, 0.5,
        "elapsedTime for transitionend on one");
     is(cs("one").getPropertyValue(event.propertyName), "rgb(0, 255, 0)",
        "computed style of " + event.propertyName + " for transitionend on one");
     finished_test();
   }, false);
 
-started_test();
-started_test();
-started_test();
-started_test();
-started_test();
-started_test();
+started_test(); // color on #one
+started_test(); // border-top-color on #one
+started_test(); // border-right-color on #one
+started_test(); // border-right-color on #one (listener on root)
+started_test(); // border-bottom-color on #one
+started_test(); // border-left-color on #one
+started_test(); // -moz-column-rule-color on #one
+started_test(); // text-decoration-color on #one
+started_test(); // outline-color on #one
 $("one").style.color = "lime";
 
 
 $("two").addEventListener("transitionend",
   function(event) {
     event.stopPropagation();
 
     ok(!got_two_target, "transitionend on two on target");
@@ -226,17 +233,17 @@ started_test();
     is(event.cancelable, false,
        "transitionend events should not be cancelable");
     is(cs("two").marginLeft, "10px",
        "computed style for transitionend on two");
     got_two_target = true;
     finished_test();
   }, false);
 
-started_test();
+started_test(); // #two
 $("two").className = "bar";
 
 $("three").addEventListener("transitionend",
   function(event) {
     event.stopPropagation();
 
     switch (event.propertyName) {
       case "margin-top":
@@ -261,37 +268,37 @@ started_test();
     }
     is(event.elapsedTime, 0.5,
        "elapsedTime for transitionend on three");
     is(cs("three").getPropertyValue(event.propertyName), "10px",
        "computed style for transitionend on three");
     finished_test();
   }, true);
 
-started_test();
-started_test();
-started_test();
-started_test();
+started_test(); // margin-top on #three
+started_test(); // margin-right on #three
+started_test(); // margin-bottom on #three
+started_test(); // margin-left on #three
 $("three").className = "bar";
 
 // We reverse the transition on four, and we should only get an event
 // at the end of the second transition.
-started_test();
+started_test(); // #four (listener on root)
 $("four").style.color = "lime";
 
 // We cancel the transition on five by changing 'transition-property',
 // and should thus get no event.
 $("five").style.color = "lime";
 
 // We cancel the transition on six by changing 'transition-duration' and
 // then changing the value, so we should get no event.
 $("six").style.color = "lime";
 
-started_test();
-started_test();
+started_test(); // #seven::before (listener on root)
+started_test(); // #seven::after (listener on root)
 $("seven").setAttribute("foo", "bar");
 
 setTimeout(function() {
              if (cs("five") != "rgb(0, 255, 0)" &&
                  cs("six") != "rgb(0, 255, 0)") {
                // The transition hasn't finished already.
                did_stops = true;
              }
@@ -307,17 +314,17 @@ function poll_start_reversal() {
   } else {
     // The forward transition has not started yet.
     setTimeout(poll_start_reversal, 20);
   }
 }
 setTimeout(poll_start_reversal, 200);
 
 // And make our own event to dispatch to the body.
-started_test();
+started_test(); // synthesized event to body (listener on root)
 
 var e = new TransitionEvent("transitionend",
                             {
                               bubbles: true,
                               cancelable: true,
                               propertyName: "some-unknown-prop",
                               elapsedTime: 0.5,
                               pseudoElement: "pseudo"
--- a/media/webrtc/signaling/src/media-conduit/CodecStatistics.cpp
+++ b/media/webrtc/signaling/src/media-conduit/CodecStatistics.cpp
@@ -11,47 +11,54 @@
 
 using namespace mozilla;
 using namespace webrtc;
 
 // use the same tag as VideoConduit
 static const char* logTag ="WebrtcVideoSessionConduit";
 
 VideoCodecStatistics::VideoCodecStatistics(int channel,
-                                           ViECodec* codec,
-                                           bool encoder) :
+                                           ViECodec* codec) :
   mChannel(channel),
   mSentRawFrames(0),
   mPtrViECodec(codec),
   mEncoderDroppedFrames(0),
   mDecoderDiscardedPackets(0),
-  mEncoderMode(encoder),
+  mRegisteredEncode(false),
+  mRegisteredDecode(false),
   mReceiveState(kReceiveStateInitial)
 #ifdef MOZILLA_INTERNAL_API
   , mRecoveredBeforeLoss(0)
   , mRecoveredLosses(0)
 #endif
 {
   MOZ_ASSERT(mPtrViECodec);
-  if (mEncoderMode) {
-    mPtrViECodec->RegisterEncoderObserver(mChannel, *this);
-  } else {
-    mPtrViECodec->RegisterDecoderObserver(mChannel, *this);
-  }
 }
 
 VideoCodecStatistics::~VideoCodecStatistics()
 {
-  if (mEncoderMode) {
+  if (mRegisteredEncode) {
     mPtrViECodec->DeregisterEncoderObserver(mChannel);
-  } else {
+  }
+  if (mRegisteredDecode) {
     mPtrViECodec->DeregisterDecoderObserver(mChannel);
   }
 }
 
+void VideoCodecStatistics::Register(bool encoder)
+{
+  if (encoder && !mRegisteredEncode) {
+    mPtrViECodec->RegisterEncoderObserver(mChannel, *this);
+    mRegisteredEncode = true;
+  } else if (!encoder && !mRegisteredDecode) {
+    mPtrViECodec->RegisterDecoderObserver(mChannel, *this);
+    mRegisteredDecode = true;
+  }
+}
+
 void VideoCodecStatistics::OutgoingRate(const int video_channel,
                                         const uint32_t framerate,
                                         const uint32_t bitrate)
 {
   unsigned int keyFrames, deltaFrames;
   mPtrViECodec->GetSendCodecStatistics(video_channel, keyFrames, deltaFrames);
   uint32_t dropped = mSentRawFrames - (keyFrames + deltaFrames);
   CSFLogDebug(logTag,
--- a/media/webrtc/signaling/src/media-conduit/CodecStatistics.h
+++ b/media/webrtc/signaling/src/media-conduit/CodecStatistics.h
@@ -16,18 +16,19 @@
 namespace mozilla {
 
 // Statistics-gathering observer for Video Encoder and Decoder
 
 class VideoCodecStatistics : public webrtc::ViEEncoderObserver
                            , public webrtc::ViEDecoderObserver
 {
 public:
-  VideoCodecStatistics(int channel, webrtc::ViECodec* vieCodec, bool encoder);
+  VideoCodecStatistics(int channel, webrtc::ViECodec* vieCodec);
   ~VideoCodecStatistics();
+  void Register(bool encoder);
 
   void SentFrame();
   virtual void OutgoingRate(const int video_channel,
     const unsigned int framerate, const unsigned int bitrate) override;
 
   virtual void IncomingCodecChanged(const int video_channel,
     const webrtc::VideoCodec& video_codec) override;
 
@@ -87,17 +88,18 @@ private:
   ScopedCustomReleasePtr<webrtc::ViECodec> mPtrViECodec; // back-pointer
 
   RunningStat mEncoderBitRate;
   RunningStat mEncoderFps;
   uint32_t mEncoderDroppedFrames;
   RunningStat mDecoderBitRate;
   RunningStat mDecoderFps;
   uint32_t mDecoderDiscardedPackets;
-  const bool mEncoderMode;
+  bool mRegisteredEncode;
+  bool mRegisteredDecode;
 
   webrtc::VideoReceiveState mReceiveState;
 #ifdef MOZILLA_INTERNAL_API
   TimeStamp mFirstDecodeTime;
   TimeStamp mReceiveFailureTime;
   TimeDuration mTotalLossTime;
   uint32_t mRecoveredBeforeLoss;
   uint32_t mRecoveredLosses;
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ -639,18 +639,19 @@ WebrtcVideoConduit::ConfigureSendMediaCo
       return kMediaConduitInvalidSendCodec;
     }
     CSFLogError(logTag, "%s SetSendCodec Failed %d ", __FUNCTION__,
                 mPtrViEBase->LastError());
     return kMediaConduitUnknownError;
   }
 
   if (!mVideoCodecStat) {
-    mVideoCodecStat = new VideoCodecStatistics(mChannel, mPtrViECodec, true);
+    mVideoCodecStat = new VideoCodecStatistics(mChannel, mPtrViECodec);
   }
+  mVideoCodecStat->Register(true);
 
   mSendingWidth = 0;
   mSendingHeight = 0;
   mSendingFramerate = video_codec.maxFramerate;
 
   if(codecConfig->RtcpFbNackIsSet("")) {
     CSFLogDebug(logTag, "Enabling NACK (send) for video stream\n");
     if (mPtrRTP->SetNACKStatus(mChannel, true) != 0)
@@ -795,18 +796,19 @@ WebrtcVideoConduit::ConfigureRecvMediaCo
 
   if(!success)
   {
     CSFLogError(logTag, "%s Setting Receive Codec Failed ", __FUNCTION__);
     return kMediaConduitInvalidReceiveCodec;
   }
 
   if (!mVideoCodecStat) {
-    mVideoCodecStat = new VideoCodecStatistics(mChannel, mPtrViECodec, false);
+    mVideoCodecStat = new VideoCodecStatistics(mChannel, mPtrViECodec);
   }
+  mVideoCodecStat->Register(false);
 
   // XXX Currently, we gather up all of the feedback types that the remote
   // party indicated it supports for all video codecs and configure the entire
   // conduit based on those capabilities. This is technically out of spec,
   // as these values should be configured on a per-codec basis. However,
   // the video engine only provides this API on a per-conduit basis, so that's
   // how we have to do it. The approach of considering the remote capablities
   // for the entire conduit to be a union of all remote codec capabilities
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -2285,19 +2285,18 @@ PeerConnectionImpl::Close()
   PC_AUTO_ENTER_API_CALL_NO_CHECK();
 
   SetSignalingState_m(PCImplSignalingState::SignalingClosed);
 
   return NS_OK;
 }
 
 bool
-PeerConnectionImpl::PluginCrash(uint64_t aPluginID,
-                                const nsAString& aPluginName,
-                                const nsAString& aPluginDumpID)
+PeerConnectionImpl::PluginCrash(uint32_t aPluginID,
+                                const nsAString& aPluginName)
 {
   // fire an event to the DOM window if this is "ours"
   bool result = mMedia ? mMedia->AnyCodecHasPluginID(aPluginID) : false;
   if (!result) {
     return false;
   }
 
   CSFLogError(logTag, "%s: Our plugin %llu crashed", __FUNCTION__, static_cast<unsigned long long>(aPluginID));
@@ -2305,17 +2304,17 @@ PeerConnectionImpl::PluginCrash(uint64_t
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
   nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
   if (!doc) {
     NS_WARNING("Couldn't get document for PluginCrashed event!");
     return true;
   }
 
   PluginCrashedEventInit init;
-  init.mPluginDumpID = aPluginDumpID;
+  init.mPluginID = aPluginID;
   init.mPluginName = aPluginName;
   init.mSubmittedCrashReport = false;
   init.mGmpPlugin = true;
   init.mBubbles = true;
   init.mCancelable = true;
 
   nsRefPtr<PluginCrashedEvent> event =
     PluginCrashedEvent::Constructor(doc, NS_LITERAL_STRING("PluginCrashed"), init);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -521,19 +521,18 @@ public:
 
   NS_IMETHODIMP Close();
 
   void Close(ErrorResult &rv)
   {
     rv = Close();
   }
 
-  bool PluginCrash(uint64_t aPluginID,
-                   const nsAString& aPluginName,
-                   const nsAString& aPluginDumpID);
+  bool PluginCrash(uint32_t aPluginID,
+                   const nsAString& aPluginName);
 
   nsresult InitializeDataChannel();
 
   NS_IMETHODIMP_TO_ERRORRESULT_RETREF(nsDOMDataChannel,
                                       CreateDataChannel, ErrorResult &rv,
                                       const nsAString& aLabel,
                                       const nsAString& aProtocol,
                                       uint16_t aType,
deleted file mode 100644
--- a/mobile/android/extensions/Makefile.in
+++ /dev/null
@@ -1,27 +0,0 @@
-# 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 $(topsrcdir)/config/rules.mk
-
-SHUMWAY_BROWSER_EXTENSION = $(topsrcdir)/browser/extensions/shumway
-
-exclude_files = \
-  test \
-  $(NULL)
-
-ifdef NIGHTLY_BUILD
-$(FINAL_TARGET)/chrome/shumway.manifest: $(GLOBAL_DEPS)
-	$(call py_action,buildlist,$@ "manifest shumway/chrome.manifest")
-
-libs:: $(SHUMWAY_BROWSER_EXTENSION) $(GLOBAL_DEPS)
-	$(PYTHON) $(topsrcdir)/config/nsinstall.py \
-	  $(SHUMWAY_BROWSER_EXTENSION) \
-          $(foreach exclude,$(exclude_files), -X $(SHUMWAY_BROWSER_EXTENSION)/$(exclude)) \
-          $(FINAL_TARGET)/chrome
-
-libs:: $(FINAL_TARGET)/chrome/shumway.manifest
-	$(call py_action,buildlist,$(FINAL_TARGET)/chrome.manifest "manifest chrome/shumway.manifest")
-endif
-
-
--- a/mobile/android/extensions/moz.build
+++ b/mobile/android/extensions/moz.build
@@ -1,6 +1,7 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+JAR_MANIFESTS += ['../../../browser/extensions/shumway/jar.mn']
--- a/python/mozbuild/mozbuild/jar.py
+++ b/python/mozbuild/mozbuild/jar.py
@@ -19,16 +19,17 @@ from cStringIO import StringIO
 
 from mozbuild.util import (
     lock_file,
     PushbackIter,
 )
 
 from mozbuild.preprocessor import Preprocessor
 from mozbuild.action.buildlist import addEntriesToListFile
+from mozpack.files import FileFinder
 if sys.platform == 'win32':
     from ctypes import windll, WinError
     CreateHardLink = windll.kernel32.CreateHardLinkA
 
 __all__ = ['JarMaker']
 
 
 class ZipEntry(object):
@@ -69,17 +70,17 @@ class JarMaker(object):
       '''
 
     ignore = re.compile('\s*(\#.*)?$')
     jarline = re.compile('(?:(?P<jarfile>[\w\d.\-\_\\\/]+).jar\:)|(?:\s*(\#.*)?)\s*$')
     relsrcline = re.compile('relativesrcdir\s+(?P<relativesrcdir>.+?):')
     regline = re.compile('\%\s+(.*)$')
     entryre = '(?P<optPreprocess>\*)?(?P<optOverwrite>\+?)\s+'
     entryline = re.compile(entryre
-                           + '(?P<output>[\w\d.\-\_\\\/\+\@]+)\s*(\((?P<locale>\%?)(?P<source>[\w\d.\-\_\\\/\@]+)\))?\s*$'
+                           + '(?P<output>[\w\d.\-\_\\\/\+\@]+)\s*(\((?P<locale>\%?)(?P<source>[\w\d.\-\_\\\/\@\*]+)\))?\s*$'
                            )
 
     def __init__(self, outputFormat='flat', useJarfileManifest=True,
         useChromeManifest=False):
 
         self.outputFormat = outputFormat
         self.useJarfileManifest = useJarfileManifest
         self.useChromeManifest = useChromeManifest
@@ -368,16 +369,39 @@ class JarMaker(object):
             # refers to a path relative to topsourcedir, use that as base
             # and strip the leading '/'
             src_base = [self.topsourcedir]
             src = src[1:]
         else:
             # use srcdirs and the objdir (current working dir) for relative paths
             src_base = self.sourcedirs + [os.getcwd()]
 
+        if '*' in src:
+            if not out.endswith('/'):
+                out += '/'
+            def _prefix(s):
+                for p in s.split('/'):
+                    if '*' not in p:
+                        yield p + '/'
+            prefix = ''.join(_prefix(src))
+            fmt = '%s%s %s%%s (%s%%s)' % (
+                m.group('optPreprocess') or '',
+                m.group('optOverwrite') or '',
+                out,
+                m.group('locale') or '',
+            )
+            for _srcdir in src_base:
+                finder = FileFinder(_srcdir, find_executables=False)
+                for path, _ in finder.find(src):
+                    line = fmt % (path[len(prefix):], path)
+                    m = self.entryline.match(line)
+                    if m:
+                        self._processEntryLine(m, outHelper, jf)
+            return
+
         # check if the source file exists
         realsrc = None
         for _srcdir in src_base:
             if os.path.isfile(os.path.join(_srcdir, src)):
                 realsrc = os.path.join(_srcdir, src)
                 break
         if realsrc is None:
             if jf is not None:
--- a/python/mozbuild/mozbuild/test/test_jarmaker.py
+++ b/python/mozbuild/mozbuild/test/test_jarmaker.py
@@ -228,30 +228,89 @@ class TestJarMaker(unittest.TestCase):
         # call JarMaker
         rv = self._jar_and_compare(os.path.join(self.srcdir,'jar.mn'),
                                    sourcedirs = [self.srcdir])
         self.assertTrue(not rv, rv)
 
     def test_a_simple_symlink(self):
         '''Test a simple jar.mn with a symlink'''
         if not symlinks_supported(self.srcdir):
-            return
+            raise unittest.SkipTest('symlinks not supported')
 
         self._create_simple_setup()
         jm = JarMaker(outputFormat='symlink')
         jm.sourcedirs = [self.srcdir]
         jm.topsourcedir = self.srcdir
         jardir = os.path.join(self.builddir, 'chrome')
         jm.makeJar(os.path.join(self.srcdir,'jar.mn'), jardir)
         # All we do is check that srcdir/bar points to builddir/chrome/test/dir/foo
         srcbar = os.path.join(self.srcdir, 'bar')
         destfoo = os.path.join(self.builddir, 'chrome', 'test', 'dir', 'foo')
         self.assertTrue(is_symlink_to(destfoo, srcbar),
                         "{0} is not a symlink to {1}".format(destfoo, srcbar))
 
+    def _create_wildcard_setup(self):
+        # create src content
+        jarf = open(os.path.join(self.srcdir, 'jar.mn'), 'w')
+        jarf.write('''test.jar:
+ dir/bar (*.js)
+ dir/hoge (qux/*)
+''')
+        jarf.close()
+        open(os.path.join(self.srcdir,'foo.js'),'w').write('foo.js\n')
+        open(os.path.join(self.srcdir,'bar.js'),'w').write('bar.js\n')
+        os.makedirs(os.path.join(self.srcdir, 'qux', 'foo'))
+        open(os.path.join(self.srcdir,'qux', 'foo', '1'),'w').write('1\n')
+        open(os.path.join(self.srcdir,'qux', 'foo', '2'),'w').write('2\n')
+        open(os.path.join(self.srcdir,'qux', 'baz'),'w').write('baz\n')
+        # create reference
+        refpath = os.path.join(self.refdir, 'chrome', 'test.jar', 'dir')
+        os.makedirs(os.path.join(refpath, 'bar'))
+        os.makedirs(os.path.join(refpath, 'hoge', 'foo'))
+        open(os.path.join(refpath, 'bar', 'foo.js'), 'w').write('foo.js\n')
+        open(os.path.join(refpath, 'bar', 'bar.js'), 'w').write('bar.js\n')
+        open(os.path.join(refpath, 'hoge', 'foo', '1'), 'w').write('1\n')
+        open(os.path.join(refpath, 'hoge', 'foo', '2'), 'w').write('2\n')
+        open(os.path.join(refpath, 'hoge', 'baz'), 'w').write('baz\n')
+
+    def test_a_wildcard_jar(self):
+        '''Test a wildcard in jar.mn'''
+        self._create_wildcard_setup()
+        # call JarMaker
+        rv = self._jar_and_compare(os.path.join(self.srcdir,'jar.mn'),
+                                   sourcedirs = [self.srcdir])
+        self.assertTrue(not rv, rv)
+
+    def test_a_wildcard_symlink(self):
+        '''Test a wildcard in jar.mn with symlinks'''
+        if not symlinks_supported(self.srcdir):
+            raise unittest.SkipTest('symlinks not supported')
+
+        self._create_wildcard_setup()
+        jm = JarMaker(outputFormat='symlink')
+        jm.sourcedirs = [self.srcdir]
+        jm.topsourcedir = self.srcdir
+        jardir = os.path.join(self.builddir, 'chrome')
+        jm.makeJar(os.path.join(self.srcdir,'jar.mn'), jardir)
+
+        expected_symlinks = {
+            ('bar', 'foo.js'): ('foo.js',),
+            ('bar', 'bar.js'): ('bar.js',),
+            ('hoge', 'foo', '1'): ('qux', 'foo', '1'),
+            ('hoge', 'foo', '2'): ('qux', 'foo', '2'),
+            ('hoge', 'baz'): ('qux', 'baz'),
+        }
+        for dest, src in expected_symlinks.iteritems():
+            srcpath = os.path.join(self.srcdir, *src)
+            destpath = os.path.join(self.builddir, 'chrome', 'test', 'dir',
+                                    *dest)
+            self.assertTrue(is_symlink_to(destpath, srcpath),
+                            "{0} is not a symlink to {1}".format(destpath,
+                                                                 srcpath))
+
 
 class Test_relativesrcdir(unittest.TestCase):
     def setUp(self):
         self.jm = JarMaker()
         self.jm.topsourcedir = '/TOPSOURCEDIR'
         self.jm.relativesrcdir = 'browser/locales'
         self.fake_empty_file = StringIO()
         self.fake_empty_file.name = 'fake_empty_file'
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-NSS_3_19_BETA5
+NSS_3_19_RTM
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,9 +5,8 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
-
--- a/security/nss/lib/nss/nss.def
+++ b/security/nss/lib/nss/nss.def
@@ -1065,14 +1065,14 @@ PK11_PrivDecrypt;
 ;+NSS_3.18 { 	# NSS 3.18 release
 ;+    global:
 __PK11_SetCertificateNickname;
 SEC_CheckCrlTimes;
 SEC_GetCrlTimes;
 ;+    local:
 ;+       *;
 ;+};
-;+NSS_3.18.1 { 	# NSS 3.18.1 release
+;+NSS_3.19 { 	# NSS 3.19 release
 ;+    global:
 CERT_GetImposedNameConstraints;
 ;+    local:
 ;+       *;
 ;+};
--- a/security/nss/lib/nss/nss.h
+++ b/security/nss/lib/nss/nss.h
@@ -28,22 +28,22 @@
 
 /*
  * NSS's major version, minor version, patch level, build number, and whether
  * this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define NSS_VERSION  "3.19" _NSS_ECC_STRING _NSS_CUSTOMIZED " Beta"
+#define NSS_VERSION  "3.19" _NSS_ECC_STRING _NSS_CUSTOMIZED
 #define NSS_VMAJOR   3
 #define NSS_VMINOR   19
 #define NSS_VPATCH   0
 #define NSS_VBUILD   0
-#define NSS_BETA     PR_TRUE
+#define NSS_BETA     PR_FALSE
 
 #ifndef RC_INVOKED
 
 #include "seccomon.h"
 
 typedef struct NSSInitParametersStr NSSInitParameters;
 
 /*
--- a/security/nss/lib/softoken/softkver.h
+++ b/security/nss/lib/softoken/softkver.h
@@ -20,16 +20,16 @@
 
 /*
  * Softoken's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <ECC>][ <Beta>]"
  */
-#define SOFTOKEN_VERSION  "3.19" SOFTOKEN_ECC_STRING " Beta"
+#define SOFTOKEN_VERSION  "3.19" SOFTOKEN_ECC_STRING
 #define SOFTOKEN_VMAJOR   3
 #define SOFTOKEN_VMINOR   19
 #define SOFTOKEN_VPATCH   0
 #define SOFTOKEN_VBUILD   0
-#define SOFTOKEN_BETA     PR_TRUE
+#define SOFTOKEN_BETA     PR_FALSE
 
 #endif /* _SOFTKVER_H_ */
--- a/security/nss/lib/util/nssutil.h
+++ b/security/nss/lib/util/nssutil.h
@@ -14,22 +14,22 @@
 
 /*
  * NSS utilities's major version, minor version, patch level, build number,
  * and whether this is a beta release.
  *
  * The format of the version string should be
  *     "<major version>.<minor version>[.<patch level>[.<build number>]][ <Beta>]"
  */
-#define NSSUTIL_VERSION  "3.19 Beta"
+#define NSSUTIL_VERSION  "3.19"
 #define NSSUTIL_VMAJOR   3
 #define NSSUTIL_VMINOR   19
 #define NSSUTIL_VPATCH   0
 #define NSSUTIL_VBUILD   0
-#define NSSUTIL_BETA     PR_TRUE
+#define NSSUTIL_BETA     PR_FALSE
 
 SEC_BEGIN_PROTOS
 
 /*
  * Returns a const string of the UTIL library version.
  */
 extern const char *NSSUTIL_GetVersion(void);
 
--- a/startupcache/test/TestStartupCache.cpp
+++ b/startupcache/test/TestStartupCache.cpp
@@ -433,17 +433,17 @@ int main(int argc, char** argv)
     manifest->AppendNative(
       NS_LITERAL_CSTRING("TestStartupCacheTelemetry.manifest"));
   }
 #else
   manifest->AppendNative(
     NS_LITERAL_CSTRING("TestStartupCacheTelemetry.manifest"));
 #endif
 
-  XRE_AddManifestLocation(NS_COMPONENT_LOCATION, manifest);
+  XRE_AddManifestLocation(NS_APP_LOCATION, manifest);
 
   nsCOMPtr<nsIObserver> telemetryThing =
     do_GetService("@mozilla.org/testing/startup-cache-telemetry.js");
   if (!telemetryThing) {
     fail("telemetryThing");
     return 1;
   }
   scrv = telemetryThing->Observe(nullptr, "save-initial", nullptr);
--- a/testing/marionette/client/marionette/marionette_test.py
+++ b/testing/marionette/client/marionette/marionette_test.py
@@ -641,16 +641,17 @@ class MarionetteTestCase(CommonTestCase)
         CommonTestCase.setUp(self)
         self.marionette.test_name = self.test_name
         self.marionette.execute_script("log('TEST-START: %s:%s')" %
                                        (self.filepath.replace('\\', '\\\\'), self.methodName))
 
     def tearDown(self):
         if not self.marionette.check_for_crash():
            self.marionette.set_context("content")
+           self.marionette.clear_imported_scripts()
            self.marionette.execute_script("log('TEST-END: %s:%s')" %
                                           (self.filepath.replace('\\', '\\\\'), self.methodName))
         self.marionette.test_name = None
         CommonTestCase.tearDown(self)
 
     def get_new_emulator(self):
         self.extra_emulator_index += 1
         if len(self.marionette.extra_emulators) == self.extra_emulator_index:
--- a/testing/marionette/client/marionette/runner/base.py
+++ b/testing/marionette/client/marionette/runner/base.py
@@ -488,16 +488,17 @@ class BaseMarionetteOptions(OptionParser
             handler(options, tests)
 
         return (options, tests)
 
 
 class BaseMarionetteTestRunner(object):
 
     textrunnerclass = MarionetteTextTestRunner
+    driverclass = Marionette
 
     def __init__(self, address=None, emulator=None, emulator_binary=None,
                  emulator_img=None, emulator_res='480x800', homedir=None,
                  app=None, app_args=None, binary=None, profile=None,
                  logger=None, no_window=False, logdir=None, logcat_stdout=False,
                  xml_output=None, repeat=0, testvars=None, tree=None, type=None,
                  device_serial=None, symbols_path=None, timeout=None,
                  shuffle=False, shuffle_seed=random.randint(0, sys.maxint),
@@ -691,17 +692,17 @@ class BaseMarionetteTestRunner(object):
                 'emulator_img': self.emulator_img,
                 'emulator_res': self.emulator_res,
                 'no_window': self.no_window,
                 'sdcard': self.sdcard,
             })
         return kwargs
 
     def start_marionette(self):
-        self.marionette = Marionette(**self._build_kwargs())
+        self.marionette = self.driverclass(**self._build_kwargs())
 
     def launch_test_container(self):
         if self.marionette.session is None:
             self.marionette.start_session()
         self.marionette.set_context(self.marionette.CONTEXT_CONTENT)
 
         result = self.marionette.execute_async_script("""
 if((navigator.mozSettings == undefined) || (navigator.mozSettings == null) || (navigator.mozApps == undefined) || (navigator.mozApps == null)) {
--- a/testing/marionette/client/marionette/runner/mixins/reporting.py
+++ b/testing/marionette/client/marionette/runner/mixins/reporting.py
@@ -250,20 +250,20 @@ class HTMLReportingTestResultMixin(objec
         # In the event we're gathering debug without starting a session, skip marionette commands
         if self.marionette.session is not None:
             try:
                 self.marionette.set_context(self.marionette.CONTEXT_CHROME)
                 debug['screenshot'] = self.marionette.screenshot()
                 self.marionette.set_context(self.marionette.CONTEXT_CONTENT)
                 debug['source'] = self.marionette.page_source
                 self.marionette.switch_to_frame()
+                self.marionette.push_permission('settings-read', True)
+                self.marionette.push_permission('settings-api-read', True)
                 debug['settings'] = json.dumps(self.marionette.execute_async_script("""
-SpecialPowers.addPermission('settings-read', true, document);
-SpecialPowers.addPermission('settings-api-read', true, document);
 var req = window.navigator.mozSettings.createLock().get('*');
 req.onsuccess = function() {
   marionetteScriptFinished(req.result);
-}""", special_powers=True), sort_keys=True, indent=4, separators=(',', ': '))
+}""", sandbox='system'), sort_keys=True, indent=4, separators=(',', ': '))
             except:
                 logger = get_default_logger()
                 logger.warning('Failed to gather test failure debug.', exc_info=True)
         return debug
 
new file mode 100644
--- /dev/null
+++ b/testing/marionette/client/marionette/tests/unit/test_execute_sandboxes.py
@@ -0,0 +1,72 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from marionette import MarionetteTestCase
+from marionette_driver.errors import JavascriptException
+
+
+class TestExecuteSandboxes(MarionetteTestCase):
+    def setUp(self):
+        super(TestExecuteSandboxes, self).setUp()
+
+    def test_execute_system_sandbox(self):
+        # Test that 'system' sandbox has elevated privileges in execute_script
+        result = self.marionette.execute_script("""
+            return Components.interfaces.nsIPermissionManager.ALLOW_ACTION;
+            """, sandbox='system')
+        self.assertEqual(result, 1)
+
+    def test_execute_async_system_sandbox(self):
+        # Test that 'system' sandbox has elevated privileges in
+        # execute_async_script.
+        result = self.marionette.execute_async_script("""
+            let result = Components.interfaces.nsIPermissionManager.ALLOW_ACTION;
+            marionetteScriptFinished(result);
+            """, sandbox='system')
+        self.assertEqual(result, 1)
+
+    def test_execute_switch_sandboxes(self):
+        # Test that sandboxes are retained when switching between them
+        # for execute_script.
+        self.marionette.execute_script("foo = 1;", sandbox='1')
+        self.marionette.execute_script("foo = 2;", sandbox='2')
+        foo = self.marionette.execute_script("return foo;", sandbox='1',
+                                             new_sandbox=False)
+        self.assertEqual(foo, 1)
+        foo = self.marionette.execute_script("return foo;", sandbox='2',
+                                             new_sandbox=False)
+        self.assertEqual(foo, 2)
+
+    def test_execute_new_sandbox(self):
+        # Test that clearing a sandbox does not affect other sandboxes
+        self.marionette.execute_script("foo = 1;", sandbox='1')
+        self.marionette.execute_script("foo = 2;", sandbox='2')
+        self.assertRaises(JavascriptException,
+                          self.marionette.execute_script,
+                          "return foo;", sandbox='1', new_sandbox=True)
+        foo = self.marionette.execute_script("return foo;", sandbox='2',
+                                             new_sandbox=False)
+        self.assertEqual(foo, 2)
+
+    def test_execute_async_switch_sandboxes(self):
+        # Test that sandboxes are retained when switching between them
+        # for execute_async_script.
+        self.marionette.execute_async_script("foo = 1; marionetteScriptFinished()",
+                                             sandbox='1')
+        self.marionette.execute_async_script("foo = 2; marionetteScriptFinished()",
+                                             sandbox='2')
+        foo = self.marionette.execute_async_script("marionetteScriptFinished(foo);",
+                                                   sandbox='1',
+                                                   new_sandbox=False)
+        self.assertEqual(foo, 1)
+        foo = self.marionette.execute_async_script("marionetteScriptFinished(foo);",
+                                                   sandbox='2',
+                                                   new_sandbox=False)
+        self.assertEqual(foo, 2)
+
+
+class TestExecuteSandboxesChrome(TestExecuteSandboxes):
+    def setUp(self):
+        super(TestExecuteSandboxesChrome, self).setUp()
+        self.marionette.set_context("chrome")
--- a/testing/marionette/client/marionette/tests/unit/test_getactiveframe_oop.py
+++ b/testing/marionette/client/marionette/tests/unit/test_getactiveframe_oop.py
@@ -24,17 +24,17 @@ class TestGetActiveFrameOOP(MarionetteTe
             SpecialPowers.setBoolPref('dom.ipc.browser_frames.oop_by_default', true);
             """)
         self.marionette.execute_script("""
             SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', true);
             """)
 
     def test_active_frame_oop(self):
         self.marionette.navigate(self.marionette.absolute_url("test.html"))
-        self.marionette.execute_script("SpecialPowers.addPermission('browser', true, document)")
+        self.marionette.push_permission('browser', True)
 
         # Create first OOP frame
         self.marionette.execute_script("""
             let iframe1 = document.createElement("iframe");
             SpecialPowers.wrap(iframe1).mozbrowser = true;
             SpecialPowers.wrap(iframe1).remote = true;
             iframe1.id = "remote_iframe1";
             iframe1.style.height = "100px";
--- a/testing/marionette/client/marionette/tests/unit/test_switch_remote_frame.py
+++ b/testing/marionette/client/marionette/tests/unit/test_switch_remote_frame.py
@@ -28,18 +28,17 @@ class TestSwitchRemoteFrame(MarionetteTe
                 try {
                   return Services.appinfo.browserTabsRemoteAutostart;
                 } catch (e) {
                   return false;
                 }""")
 
     def test_remote_frame(self):
         self.marionette.navigate(self.marionette.absolute_url("test.html"))
-        self.marionette.execute_async_script(
-            "SpecialPowers.pushPermissions([{'type': 'browser', 'allow': true, 'context': document}], marionetteScriptFinished);")
+        self.marionette.push_permission('browser', True)
         self.marionette.execute_script("""
             let iframe = document.createElement("iframe");
             SpecialPowers.wrap(iframe).mozbrowser = true;
             SpecialPowers.wrap(iframe).remote = true;
             iframe.id = "remote_iframe";
             iframe.style.height = "100px";
             iframe.style.width = "100%%";
             iframe.src = "%s";
@@ -50,18 +49,17 @@ class TestSwitchRemoteFrame(MarionetteTe
         main_process = self.marionette.execute_script("""
             return SpecialPowers.isMainProcess();
             """)
         self.assertFalse(main_process)
 
     def test_remote_frame_revisit(self):
         # test if we can revisit a remote frame (this takes a different codepath)
         self.marionette.navigate(self.marionette.absolute_url("test.html"))
-        self.marionette.execute_async_script(
-            "SpecialPowers.pushPermissions([{'type': 'browser', 'allow': true, 'context': document}], marionetteScriptFinished);")
+        self.marionette.push_permission('browser', True)
         self.marionette.execute_script("""
             let iframe = document.createElement("iframe");
             SpecialPowers.wrap(iframe).mozbrowser = true;
             SpecialPowers.wrap(iframe).remote = true;
             iframe.id = "remote_iframe";
             iframe.style.height = "100px";
             iframe.style.width = "100%%";
             iframe.src = "%s";
@@ -84,18 +82,17 @@ class TestSwitchRemoteFrame(MarionetteTe
         main_process = self.marionette.execute_script("""
             return SpecialPowers.isMainProcess();
             """)
         self.assertFalse(main_process)
 
     def test_we_can_switch_to_a_remote_frame_by_index(self):
         # test if we can revisit a remote frame (this takes a different codepath)
         self.marionette.navigate(self.marionette.absolute_url("test.html"))
-        self.marionette.execute_async_script(
-            "SpecialPowers.pushPermissions([{'type': 'browser', 'allow': true, 'context': document}], marionetteScriptFinished);")
+        self.marionette.push_permission('browser', True)
         self.marionette.execute_script("""
             let iframe = document.createElement("iframe");
             SpecialPowers.wrap(iframe).mozbrowser = true;
             SpecialPowers.wrap(iframe).remote = true;
             iframe.id = "remote_iframe";
             iframe.style.height = "100px";
             iframe.style.width = "100%%";
             iframe.src = "%s";
--- a/testing/marionette/client/marionette/tests/unit/unit-tests.ini
+++ b/testing/marionette/client/marionette/tests/unit/unit-tests.ini
@@ -151,8 +151,10 @@ b2g = false
 [test_key_actions.py]
 [test_mouse_action.py]
 b2g = false
 [test_teardown_context_preserved.py]
 b2g = false
 [test_file_upload.py]
 b2g = false
 skip-if = os == "win" # http://bugs.python.org/issue14574
+
+[test_execute_sandboxes.py]
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -126,17 +126,17 @@ this.GeckoDriver = function(appName, dev
   this.importedScripts = FileUtils.getFile("TmpD", ["marionetteChromeScripts"]);
   this.importedScriptHashes = {};
   this.importedScriptHashes[Context.CONTENT] = [];
   this.importedScriptHashes[Context.CHROME] = [];
   this.currentFrameElement = null;
   this.testName = null;
   this.mozBrowserClose = null;
   this.enabled_security_pref = false;
-  this.sandbox = null;
+  this.sandboxes = {};
   // frame ID of the current remote frame, used for mozbrowserclose events
   this.oopFrameId = null;
   this.observing = null;
   this._browserIds = new WeakMap();
   this.actions = new ActionChain(utils);
 
   this.sessionCapabilities = {
     // Mandated capabilities
@@ -710,21 +710,27 @@ GeckoDriver.prototype.getContext = funct
  * @param {Object} args
  *     Arguments given by client.
  * @param {boolean} sp
  *     True to enable special powers in the sandbox, false not to.
  *
  * @return {nsIXPCComponents_utils_Sandbox}
  *     Returns the sandbox.
  */
-GeckoDriver.prototype.createExecuteSandbox = function(win, mn, sp) {
-  let sb = new Cu.Sandbox(win,
+GeckoDriver.prototype.createExecuteSandbox = function(win, mn, sp, sandboxName) {
+  let principal = win;
+  if (sandboxName == 'system') {
+    principal = Cc["@mozilla.org/systemprincipal;1"].
+                createInstance(Ci.nsIPrincipal);
+  }
+  let sb = new Cu.Sandbox(principal,
       {sandboxPrototype: win, wantXrays: false, sandboxName: ""});
   sb.global = sb;
   sb.testUtils = utils;
+  sb.proto = win;
 
   mn.exports.forEach(function(fn) {
     if (typeof mn[fn] === 'function') {
       sb[fn] = mn[fn].bind(mn);
     } else {
       sb[fn] = mn[fn];
     }
   });
@@ -735,17 +741,17 @@ GeckoDriver.prototype.createExecuteSandb
     let pow = [
       "chrome://specialpowers/content/specialpowersAPI.js",
       "chrome://specialpowers/content/SpecialPowersObserverAPI.js",
       "chrome://specialpowers/content/ChromePowers.js",
     ];
     pow.map(s => loader.loadSubScript(s, sb));
   }
 
-  return sb;
+  this.sandboxes[sandboxName] = sb;
 };
 
 /**
  * Apply arguments sent from the client to the current (possibly reused)
  * execution sandbox.
  */
 GeckoDriver.prototype.applyArgumentsToSandbox = function(win, sb, args) {
   sb.__marionetteParams = this.curBrowser.elementManager.convertWrappedArguments(args, win);
@@ -815,33 +821,35 @@ GeckoDriver.prototype.execute = function
   let {inactivityTimeout,
        scriptTimeout,
        script,
        newSandbox,
        args,
        specialPowers,
        filename,
        line} = cmd.parameters;
+  let sandboxName = cmd.parameters.sandbox || 'default';
 
   if (!scriptTimeout) {
     scriptTimeout = this.scriptTimeout;
   }
   if (typeof newSandbox == "undefined") {
     newSandbox = true;
   }
 
   if (this.context == Context.CONTENT) {
     resp.value = yield this.listener.executeScript({
       script: script,
       args: args,
       newSandbox: newSandbox,
       timeout: scriptTimeout,
       specialPowers: specialPowers,
       filename: filename,
-      line: line
+      line: line,
+      sandboxName: sandboxName
     });
     return;
   }
 
   // handle the inactivity timeout
   let that = this;
   if (inactivityTimeout) {
     let setTimer = function() {
@@ -855,49 +863,52 @@ GeckoDriver.prototype.execute = function
     setTimer();
     this.heartbeatCallback = function() {
       that.inactivityTimer.cancel();
       setTimer();
     };
   }
 
   let win = this.getCurrentWindow();
-  if (!this.sandbox || newSandbox) {
+  if (newSandbox ||
+      !(sandboxName in this.sandboxes) ||
+      (this.sandboxes[sandboxName].proto != win)) {
     let marionette = new Marionette(
         this,
         win,
         "chrome",
         this.marionetteLog,
         scriptTimeout,
         this.heartbeatCallback,
         this.testName);
-    this.sandbox = this.createExecuteSandbox(
+    this.createExecuteSandbox(
         win,
         marionette,
-        specialPowers);
-    if (!this.sandbox) {
+        specialPowers,
+        sandboxName);
+    if (!this.sandboxes[sandboxName]) {
       return;
     }
   }
-  this.applyArgumentsToSandbox(win, this.sandbox, args);
+  this.applyArgumentsToSandbox(win, this.sandboxes[sandboxName], args);
 
   try {
-    this.sandbox.finish = () => {
+    this.sandboxes[sandboxName].finish = () => {
       if (this.inactivityTimer !== null) {
         this.inactivityTimer.cancel();
       }
-      return this.sandbox.generate_results();
+      return this.sandboxes[sandboxName].generate_results();
     };
 
     if (!directInject) {
       script = "let func = function() { " + script + " }; func.apply(null, __marionetteParams);";
     }
     this.executeScriptInSandbox(
         resp,
-        this.sandbox,
+        this.sandboxes[sandboxName],
         script,
         directInject,
         false /* async */,
         scriptTimeout);
   } catch (e) {
     throw new JavaScriptError(e, "execute_script", filename, line, script);
   }
 };
@@ -946,16 +957,17 @@ GeckoDriver.prototype.executeJSScript = 
         newSandbox: cmd.parameters.newSandbox,
         async: cmd.parameters.async,
         timeout: cmd.parameters.scriptTimeout ?
             cmd.parameters.scriptTimeout : this.scriptTimeout,
         inactivityTimeout: cmd.parameters.inactivityTimeout,
         specialPowers: cmd.parameters.specialPowers,
         filename: cmd.parameters.filename,
         line: cmd.parameters.line,
+        sandboxName: cmd.parameters.sandbox || 'default',
       });
       break;
  }
 };
 
 /**
  * This function is used by executeAsync and executeJSScript to execute
  * a script in a sandbox.
@@ -975,16 +987,17 @@ GeckoDriver.prototype.executeWithCallbac
   let {script,
       args,
       newSandbox,
       inactivityTimeout,
       scriptTimeout,
       specialPowers,
       filename,
       line} = cmd.parameters;
+  let sandboxName = cmd.parameters.sandbox || 'default';
 
   if (!scriptTimeout) {
     scriptTimeout = this.scriptTimeout;
   }
   if (typeof newSandbox == "undefined") {
     newSandbox = true;
   }
 
@@ -993,17 +1006,18 @@ GeckoDriver.prototype.executeWithCallbac
       script: script,
       args: args,
       id: cmd.id,
       newSandbox: newSandbox,
       timeout: scriptTimeout,
       inactivityTimeout: inactivityTimeout,
       specialPowers: specialPowers,
       filename: filename,
-      line: line
+      line: line,
+      sandboxName: sandboxName,
     });
     return;
   }
 
   // handle the inactivity timeout
   let that = this;
   if (inactivityTimeout) {
     this.inactivityTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
@@ -1029,17 +1043,17 @@ GeckoDriver.prototype.executeWithCallbac
 
   let res = yield new Promise(function(resolve, reject) {
     let chromeAsyncReturnFunc = function(val) {
       if (that.emulator.cbs.length > 0) {
         that.emulator.cbs = [];
         throw new WebDriverError("Emulator callback still pending when finish() called");
       }
 
-      if (cmd.id == that.sandbox.command_id) {
+      if (cmd.id == that.sandboxes[sandboxName].command_id) {
         if (that.timer !== null) {
           that.timer.cancel();
           that.timer = null;
         }
 
         win.onerror = origOnError;
 
         if (error.isError(val)) {
@@ -1050,55 +1064,57 @@ GeckoDriver.prototype.executeWithCallbac
       }
 
       if (that.inactivityTimer !== null) {
         that.inactivityTimer.cancel();
       }
     };
 
     let chromeAsyncFinish = function() {
-      let res = that.sandbox.generate_results();
+      let res = that.sandboxes[sandboxName].generate_results();
       chromeAsyncReturnFunc(res);
     };
 
     let chromeAsyncError = function(e, func, file, line, script) {
       let err = new JavaScriptError(e, func, file, line, script);
       chromeAsyncReturnFunc(err);
     };
 
-    if (!this.sandbox || newSandbox) {
+    if (newSandbox || !(sandboxName in this.sandboxes)) {
       let marionette = new Marionette(
           this,
           win,
           "chrome",
           this.marionetteLog,
           scriptTimeout,
           this.heartbeatCallback,
           this.testName);
-      this.sandbox = this.createExecuteSandbox(win, marionette, specialPowers);
-      if (!this.sandbox) {
-        return;
-      }
+      this.createExecuteSandbox(win, marionette,
+                                specialPowers, sandboxName);
     }
-    this.sandbox.command_id = cmd.id;
-    this.sandbox.runEmulatorCmd = (cmd, cb) => {
+    if (!this.sandboxes[sandboxName]) {
+      return;
+    }
+
+    this.sandboxes[sandboxName].command_id = cmd.id;
+    this.sandboxes[sandboxName].runEmulatorCmd = (cmd, cb) => {
       let ecb = new EmulatorCallback();
       ecb.onresult = cb;
       ecb.onerror = chromeAsyncError;
       this.emulator.pushCallback(ecb);
       this.emulator.send({emulator_cmd: cmd, id: ecb.id});
     };
-    this.sandbox.runEmulatorShell = (args, cb) => {
+    this.sandboxes[sandboxName].runEmulatorShell = (args, cb) => {
       let ecb = new EmulatorCallback();
       ecb.onresult = cb;
       ecb.onerror = chromeAsyncError;
       this.emulator.pushCallback(ecb);
       this.emulator.send({emulator_shell: args, id: ecb.id});
     };
-    this.applyArgumentsToSandbox(win, this.sandbox, args);
+    this.applyArgumentsToSandbox(win, this.sandboxes[sandboxName], args);
 
     // NB: win.onerror is not hooked by default due to the inability to
     // differentiate content exceptions from chrome exceptions. See bug
     // 1128760 for more details. A debug_script flag can be set to
     // reenable onerror hooking to help debug test scripts.
     if (cmd.parameters.debug_script) {
       win.onerror = function(msg, url, line) {
         let err = new JavaScriptError(`${msg} at: ${url} line: ${line}`);
@@ -1110,29 +1126,29 @@ GeckoDriver.prototype.executeWithCallbac
     try {
       this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
       if (this.timer !== null) {
         this.timer.initWithCallback(function() {
           chromeAsyncReturnFunc(new ScriptTimeoutError("timed out"));
         }, that.timeout, Ci.nsITimer.TYPE_ONE_SHOT);
       }
 
-      this.sandbox.returnFunc = chromeAsyncReturnFunc;
-      this.sandbox.finish = chromeAsyncFinish;
+      this.sandboxes[sandboxName].returnFunc = chromeAsyncReturnFunc;
+      this.sandboxes[sandboxName].finish = chromeAsyncFinish;
 
       if (!directInject) {
         script =  "__marionetteParams.push(returnFunc);" +
             "let marionetteScriptFinished = returnFunc;" +
             "let __marionetteFunc = function() {" + script + "};" +
             "__marionetteFunc.apply(null, __marionetteParams);";
       }
 
       this.executeScriptInSandbox(
           resp,
-          this.sandbox,
+          this.sandboxes[sandboxName],
           script,
           directInject,
           true /* async */,
           scriptTimeout);
     } catch (e) {
       chromeAsyncError(e, "execute_async_script", filename, line, script);
     }
   }.bind(this));
@@ -1516,20 +1532,16 @@ GeckoDriver.prototype.switchToWindow = f
       if (byNameOrId(win.name, outerId)) {
         found = {win: win, outerId: outerId};
         break;
       }
     }
   }
 
   if (found) {
-    // As in content, switching to a new window invalidates a sandbox
-    // for reuse.
-    this.sandbox = null;
-
     // Initialise Marionette if browser has not been seen before,
     // otherwise switch to known browser and activate the tab if it's a
     // content browser.
     if (!(found.outerId in this.browsers)) {
       let registerBrowsers, browserListening;
       if (found.contentId) {
         registerBrowsers = this.registerPromise();
         browserListening = this.listeningPromise();
@@ -2447,16 +2459,17 @@ GeckoDriver.prototype.sessionTearDown = 
   this.deleteFile("marionetteContentScripts");
 
   if (this.observing !== null) {
     for (let topic in this.observing) {
       Services.obs.removeObserver(this.observing[topic], topic);
     }
     this.observing = null;
   }
+  this.sandboxes = {};
 };
 
 /**
  * Processes the "deleteSession" request from the client by tearing down
  * the session and responding "ok".
  */
 GeckoDriver.prototype.deleteSession = function(cmd, resp) {
   this.sessionTearDown();
--- a/testing/marionette/driver/marionette_driver/marionette.py
+++ b/testing/marionette/driver/marionette_driver/marionette.py
@@ -782,16 +782,80 @@ class Marionette(object):
                 val = str(val)
                 for i in range(len(val)):
                     typing.append(val[i])
             else:
                 for i in range(len(val)):
                     typing.append(val[i])
         return typing
 
+    def push_permission(self, perm_type, allow):
+        with self.using_context('content'):
+            perm = self.execute_script("""
+                let allow = arguments[0];
+                if (allow) {
+                  allow = Components.interfaces.nsIPermissionManager.ALLOW_ACTION;
+                }
+                else {
+                  allow = Components.interfaces.nsIPermissionManager.DENY_ACTION;
+                }
+                let perm_type = arguments[1];
+
+                Components.utils.import("resource://gre/modules/Services.jsm");
+                window.wrappedJSObject.permChanged = false;
+                window.wrappedJSObject.permObserver = function(subject, topic, data) {
+                  if (topic == "perm-changed") {
+                    let permission = subject.QueryInterface(Components.interfaces.nsIPermission);
+                    if (perm_type == permission.type) {
+                      Services.obs.removeObserver(window.wrappedJSObject.permObserver, "perm-changed");
+                      window.wrappedJSObject.permChanged = true;
+                    }
+                  }
+                };
+                Services.obs.addObserver(window.wrappedJSObject.permObserver,
+                                         "perm-changed", false);
+
+                let value = {
+                              'url': document.nodePrincipal.URI.spec,
+                              'appId': document.nodePrincipal.appId,
+                              'isInBrowserElement': document.nodePrincipal.isInBrowserElement,
+                              'type': perm_type,
+                              'action': allow
+                            };
+                return value;
+                """, script_args=[allow, perm_type], sandbox='system')
+
+        with self.using_context('chrome'):
+            waiting = self.execute_script("""
+                Components.utils.import("resource://gre/modules/Services.jsm");
+                let perm = arguments[0];
+                let secMan = Services.scriptSecurityManager;
+                let principal = secMan.getAppCodebasePrincipal(Services.io.newURI(perm.url, null, null),
+                                perm.appId, perm.isInBrowserElement);
+                let testPerm = Services.perms.testPermissionFromPrincipal(principal, perm.type, perm.action);
+                if (testPerm == perm.action) {
+                  return false;
+                }
+                Services.perms.addFromPrincipal(principal, perm.type, perm.action);
+                return true;
+                """, script_args=[perm])
+
+        with self.using_context('content'):
+            if waiting:
+                self.execute_async_script("""
+                    waitFor(marionetteScriptFinished, function() {
+                      return window.wrappedJSObject.permChanged;
+                    });
+                    """, sandbox='system')
+            else:
+                self.execute_script("""
+                    Components.utils.import("resource://gre/modules/Services.jsm");
+                    Services.obs.removeObserver(window.wrappedJSObject.permObserver, "perm-changed");
+                    """, sandbox='system')
+
     def enforce_gecko_prefs(self, prefs):
         """
         Checks if the running instance has the given prefs. If not, it will kill the
         currently running instance, and spawn a new instance with the requested preferences.
 
         : param prefs: A dictionary whose keys are preference names.
         """
         if not self.instance:
@@ -1282,17 +1346,17 @@ class Marionette(object):
         else:
             unwrapped = value
 
         return unwrapped
 
     def execute_js_script(self, script, script_args=None, async=True,
                           new_sandbox=True, special_powers=False,
                           script_timeout=None, inactivity_timeout=None,
-                          filename=None):
+                          filename=None, sandbox='default'):
         if script_args is None:
             script_args = []
         args = self.wrapArguments(script_args)
         response = self._send_message('executeJSScript',
                                       'value',
                                       script=script,
                                       args=args,
                                       async=async,
@@ -1300,31 +1364,35 @@ class Marionette(object):
                                       specialPowers=special_powers,
                                       scriptTimeout=script_timeout,
                                       inactivityTimeout=inactivity_timeout,
                                       filename=filename,
                                       line=None)
         return self.unwrapValue(response)
 
     def execute_script(self, script, script_args=None, new_sandbox=True,
-                       special_powers=False, script_timeout=None):
+                       special_powers=False, sandbox='default', script_timeout=None):
         '''
         Executes a synchronous JavaScript script, and returns the result (or None if the script does return a value).
 
         The script is executed in the context set by the most recent
         set_context() call, or to the CONTEXT_CONTENT context if set_context()
         has not been called.
 
         :param script: A string containing the JavaScript to execute.
         :param script_args: A list of arguments to pass to the script.
         :param special_powers: Whether or not you want access to SpecialPowers
          in your script. Set to False by default because it shouldn't really
          be used, since you already have access to chrome-level commands if you
          set context to chrome and do an execute_script. This method was added
          only to help us run existing Mochitests.
+        :param sandbox: A tag referring to the sandbox you wish to use; if
+         you specify a new tag, a new sandbox will be created.  If you use the
+         special tag 'system', the sandbox will be created using the system
+         principal which has elevated privileges.
         :param new_sandbox: If False, preserve global variables from the last
          execute_*script call. This is True by default, in which case no
          globals are preserved.
 
         Simple usage example:
 
         ::
 
@@ -1372,37 +1440,44 @@ class Marionette(object):
         args = self.wrapArguments(script_args)
         stack = traceback.extract_stack()
         frame = stack[-2:-1][0] # grab the second-to-last frame
         response = self._send_message('executeScript',
                                       'value',
                                       script=script,
                                       args=args,
                                       newSandbox=new_sandbox,
+                                      sandbox=sandbox,
                                       specialPowers=special_powers,
                                       scriptTimeout=script_timeout,
                                       line=int(frame[1]),
                                       filename=os.path.basename(frame[0]))
         return self.unwrapValue(response)
 
-    def execute_async_script(self, script, script_args=None, new_sandbox=True, special_powers=False, script_timeout=None, debug_script=False):
+    def execute_async_script(self, script, script_args=None, new_sandbox=True,
+                             sandbox='default', script_timeout=None,
+                             special_powers=False, debug_script=False):
         '''
         Executes an asynchronous JavaScript script, and returns the result (or None if the script does return a value).
 
         The script is executed in the context set by the most recent
         set_context() call, or to the CONTEXT_CONTENT context if set_context()
         has not been called.
 
         :param script: A string containing the JavaScript to execute.
         :param script_args: A list of arguments to pass to the script.
         :param special_powers: Whether or not you want access to SpecialPowers
          in your script. Set to False by default because it shouldn't really
          be used, since you already have access to chrome-level commands if you
          set context to chrome and do an execute_script. This method was added
          only to help us run existing Mochitests.
+        :param sandbox: A tag referring to the sandbox you wish to use; if
+         you specify a new tag, a new sandbox will be created.  If you use the
+         special tag 'system', the sandbox will be created using the system
+         principal which has elevated privileges.
         :param new_sandbox: If False, preserve global variables from the last
          execute_*script call. This is True by default, in which case no
          globals are preserved.
         :param debug_script: Capture javascript exceptions when in
          CONTEXT_CHROME context.
 
         Usage example:
 
@@ -1422,16 +1497,17 @@ class Marionette(object):
         args = self.wrapArguments(script_args)
         stack = traceback.extract_stack()
         frame = stack[-2:-1][0] # grab the second-to-last frame
         response = self._send_message('executeAsyncScript',
                                       'value',
                                       script=script,
                                       args=args,
                                       newSandbox=new_sandbox,
+                                      sandbox=sandbox,
                                       specialPowers=special_powers,
                                       scriptTimeout=script_timeout,
                                       line=int(frame[1]),
                                       filename=os.path.basename(frame[0]),
                                       debug_script=debug_script)
         return self.unwrapValue(response)
 
     def find_element(self, method, target, id=None):
--- a/testing/marionette/listener.js
+++ b/testing/marionette/listener.js
@@ -41,19 +41,20 @@ let listenerId = null; // unique ID of t
 let curFrame = content;
 let isRemoteBrowser = () => curFrame.contentWindow !== null;
 let previousFrame = null;
 let elementManager = new ElementManager([]);
 let accessibility = new Accessibility();
 let actions = new ActionChain(utils, checkForInterrupted);
 let importedScripts = null;
 
-// The sandbox we execute test scripts in. Gets lazily created in
-// createExecuteContentSandbox().
-let sandbox;
+// A dict of sandboxes used this session
+let sandboxes = {};
+// The name of the current sandbox
+let sandboxName = 'default';
 
 // the unload handler
 let onunload;
 
 // Flag to indicate whether an async script is currently running or not.
 let asyncTestRunning = false;
 let asyncTestCommandId;
 let asyncTestTimeoutId;
@@ -80,17 +81,16 @@ logger.info("loaded listener.js");
 let modalHandler = function() {
   // This gets called on the system app only since it receives the mozbrowserprompt event
   sendSyncMessage("Marionette:switchedToFrame", { frameValue: null, storePrevious: true });
   let isLocal = sendSyncMessage("MarionetteFrame:handleModal", {})[0].value;
   if (isLocal) {
     previousFrame = curFrame;
   }
   curFrame = content;
-  sandbox = null;
 };
 
 /**
  * Called when listener is first started up.
  * The listener sends its unique window ID and its current URI to the actor.
  * If the actor returns an ID, we start the listeners. Otherwise, nothing happens.
  */
 function registerSelf() {
@@ -398,17 +398,17 @@ function sendLog(msg) {
 function sendError(err, cmdId) {
   sendToServer("Marionette:error", null, {error: err}, cmdId);
 }
 
 /**
  * Clear test values after completion of test
  */
 function resetValues() {
-  sandbox = null;
+  sandboxes = {};
   curFrame = content;
   actions.mouseEventsOnly = false;
 }
 
 /**
  * Dump a logline to stdout. Prepends logline with a timestamp.
  */
 function dumpLog(logline) {
@@ -432,17 +432,16 @@ function wasInterrupted() {
 }
 
 function checkForInterrupted() {
     if (wasInterrupted()) {
       if (previousFrame) {
         //if previousFrame is set, then we're in a single process environment
         curFrame = actions.frame = previousFrame;
         previousFrame = null;
-        sandbox = null;
       }
       else {
         //else we're in OOP environment, so we'll switch to the original OOP frame
         sendSyncMessage("Marionette:switchToModalOrigin");
       }
       sendSyncMessage("Marionette:switchedToFrame", { restorePrevious: true });
     }
 }
@@ -461,17 +460,22 @@ function createExecuteContentSandbox(win
       "content",
       marionetteLogObj,
       timeout,
       heartbeatCallback,
       marionetteTestName);
   mn.runEmulatorCmd = (cmd, cb) => this.runEmulatorCmd(cmd, cb);
   mn.runEmulatorShell = (args, cb) => this.runEmulatorShell(args, cb);
 
-  let sandbox = new Cu.Sandbox(win, {sandboxPrototype: win});
+  let principal = win;
+  if (sandboxName == 'system') {
+    principal = Cc["@mozilla.org/systemprincipal;1"].
+                createInstance(Ci.nsIPrincipal);
+  }
+  let sandbox = new Cu.Sandbox(principal, {sandboxPrototype: win});
   sandbox.global = sandbox;
   sandbox.window = win;
   sandbox.document = sandbox.window.document;
   sandbox.navigator = sandbox.window.navigator;
   sandbox.testUtils = utils;
   sandbox.asyncTestCommandId = asyncTestCommandId;
   sandbox.marionette = mn;
 
@@ -527,17 +531,17 @@ function createExecuteContentSandbox(win
       sandbox.asyncComplete(mn.generate_results(), sandbox.asyncTestCommandId);
     } else {
       return mn.generate_results();
     }
   };
   sandbox.marionetteScriptFinished = val =>
       sandbox.asyncComplete(val, sandbox.asyncTestCommandId);
 
-  return sandbox;
+  sandboxes[sandboxName] = sandbox;
 }
 
 /**
  * Execute the given script either as a function body (executeScript)
  * or directly (for mochitest like JS Marionette tests).
  */
 function executeScript(msg, directInject) {
   // Set up inactivity timeout.
@@ -552,28 +556,32 @@ function executeScript(msg, directInject
     heartbeatCallback = function() {
       curFrame.clearTimeout(inactivityTimeoutId);
       setTimer();
     };
   }
 
   asyncTestCommandId = msg.json.command_id;
   let script = msg.json.script;
+  sandboxName = msg.json.sandboxName;
 
-  if (msg.json.newSandbox || !sandbox) {
-    sandbox = createExecuteContentSandbox(curFrame,
-                                          msg.json.timeout);
-    if (!sandbox) {
+  if (msg.json.newSandbox ||
+      !(sandboxName in sandboxes) ||
+      (sandboxes[sandboxName].window != curFrame)) {
+    createExecuteContentSandbox(curFrame, msg.json.timeout);
+    if (!sandboxes[sandboxName]) {
       sendError(new WebDriverError("Could not create sandbox!"), asyncTestCommandId);
       return;
     }
   } else {
-    sandbox.asyncTestCommandId = asyncTestCommandId;
+    sandboxes[sandboxName].asyncTestCommandId = asyncTestCommandId;
   }
 
+  let sandbox = sandboxes[sandboxName];
+
   try {
     if (directInject) {
       if (importedScripts.exists()) {
         let stream = Components.classes["@mozilla.org/network/file-input-stream;1"].
                       createInstance(Components.interfaces.nsIFileInputStream);
         stream.init(importedScripts, -1, 0, 0);
         let data = NetUtil.readInputStreamToString(stream, stream.available());
         stream.close();
@@ -675,33 +683,36 @@ function executeWithCallback(msg, useFin
     heartbeatCallback = function() {
       curFrame.clearTimeout(inactivityTimeoutId);
       setTimer();
     };
   }
 
   let script = msg.json.script;
   asyncTestCommandId = msg.json.command_id;
+  sandboxName = msg.json.sandboxName;
 
   onunload = function() {
     sendError(new JavaScriptError("unload was called"), asyncTestCommandId);
   };
   curFrame.addEventListener("unload", onunload, false);
 
-  if (msg.json.newSandbox || !sandbox) {
-    sandbox = createExecuteContentSandbox(curFrame,
-                                          msg.json.timeout);
-    if (!sandbox) {
+  if (msg.json.newSandbox ||
+      !(sandboxName in sandboxes) ||
+      (sandboxes[sandboxName].window != curFrame)) {
+    createExecuteContentSandbox(curFrame, msg.json.timeout);
+    if (!sandboxes[sandboxName]) {
       sendError(new JavaScriptError("Could not create sandbox!"), asyncTestCommandId);
       return;
     }
   }
   else {
-    sandbox.asyncTestCommandId = asyncTestCommandId;
+    sandboxes[sandboxName].asyncTestCommandId = asyncTestCommandId;
   }
+  let sandbox = sandboxes[sandboxName];
   sandbox.tag = script;
 
   asyncTestTimeoutId = curFrame.setTimeout(function() {
     sandbox.asyncComplete(new ScriptTimeoutError("timed out"), asyncTestCommandId);
   }, msg.json.timeout);
 
   originalOnError = curFrame.onerror;
   curFrame.onerror = function errHandler(msg, url, line) {
@@ -1637,17 +1648,17 @@ function switchToFrame(msg) {
   if ((msg.json.id === null || msg.json.id === undefined) && (msg.json.element == null)) {
     // returning to root frame
     sendSyncMessage("Marionette:switchedToFrame", { frameValue: null });
 
     curFrame = content;
     if(msg.json.focus == true) {
       curFrame.focus();
     }
-    sandbox = null;
+
     checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT);
     return;
   }
   if (msg.json.element != undefined) {
     if (elementManager.seenItems[msg.json.element] != undefined) {
       let wantedFrame;
       try {
         wantedFrame = elementManager.getKnownElement(msg.json.element, curFrame); //Frame Element
@@ -1689,17 +1700,17 @@ function switchToFrame(msg) {
         else {
           // If foundFrame is null at this point then we have the top level browsing
           // context so should treat it accordingly.
           sendSyncMessage("Marionette:switchedToFrame", { frameValue: null});
           curFrame = content;
           if(msg.json.focus == true) {
             curFrame.focus();
           }
-          sandbox = null;
+
           checkTimer.initWithCallback(checkLoad, 100, Ci.nsITimer.TYPE_ONE_SHOT);
           return;
         }
       } catch (e) {
         // Since window.frames does not return OOP frames it will throw
         // and we land up here. Let's not give up and check if there are
         // iframes and switch to the indexed frame there
         let iframes = curFrame.document.getElementsByTagName("iframe");
@@ -1711,18 +1722,16 @@ function switchToFrame(msg) {
     }
   }
 
   if (foundFrame === null) {
     sendError(new NoSuchFrameError("Unable to locate frame: " + (msg.json.id || msg.json.element)), command_id);
     return true;
   }
 
-  sandbox = null;
-
   // send a synchronous message to let the server update the currently active
   // frame element (for getActiveFrame)
   let frameValue = elementManager.wrapValue(curFrame.wrappedJSObject)['ELEMENT'];
   sendSyncMessage("Marionette:switchedToFrame", { frameValue: frameValue });
 
   let rv = null;
   if (curFrame.contentWindow === null) {
     // The frame we want to switch to is a remote/OOP frame;
@@ -1872,17 +1881,17 @@ function runEmulatorShell(args, callback
     _emu_cbs[_emu_cb_id] = callback;
   }
   sendAsyncMessage("Marionette:runEmulatorShell", {emulator_shell: args, id: _emu_cb_id});
   _emu_cb_id += 1;
 }
 
 function emulatorCmdResult(msg) {
   let message = msg.json;
-  if (!sandbox) {
+  if (!sandboxes[sandboxName]) {
     return;
   }
   let cb = _emu_cbs[message.id];
   delete _emu_cbs[message.id];
   if (!cb) {
     return;
   }
   try {
--- a/testing/mozbase/mozcrash/mozcrash/mozcrash.py
+++ b/testing/mozbase/mozcrash/mozcrash/mozcrash.py
@@ -330,12 +330,12 @@ def check_for_java_exception(logcat, qui
                 if m and m.group(1):
                     exception_type = m.group(1)
                 m = logre.search(logcat[i+2])
                 if m and m.group(1):
                     exception_location = m.group(1)
                 if not quiet:
                     print "PROCESS-CRASH | java-exception | %s %s" % (exception_type, exception_location)
             else:
-                print "Automation Error: Logcat is truncated!"
+                print "Automation Error: java exception in logcat at line %d of %d: %s" % (i, len(logcat), line)
             break
 
     return found_exception
--- a/testing/mozbase/mozdevice/mozdevice/devicemanager.py
+++ b/testing/mozbase/mozdevice/mozdevice/devicemanager.py
@@ -147,21 +147,23 @@ class DeviceManager(object):
         self.shellCheckOutput(['/system/bin/logcat', '-c'], root=self._logcatNeedsRoot)
 
     def getLogcat(self, filterSpecs=["dalvikvm:I", "ConnectivityService:S",
                                       "WifiMonitor:S", "WifiStateTracker:S",
                                       "wpa_supplicant:S", "NetworkStateTracker:S"],
                   format="time",
                   filterOutRegexps=[]):
         """
-        Returns the contents of the logcat file as a list of strings
+        Returns the contents of the logcat file as a list of
+        '\n' terminated strings
         """
         cmdline = ["/system/bin/logcat", "-v", format, "-d"] + filterSpecs
-        lines = self.shellCheckOutput(cmdline,
-                                      root=self._logcatNeedsRoot).split('\r')
+        output = self.shellCheckOutput(cmdline,
+                                      root=self._logcatNeedsRoot)
+        lines = output.replace('\r\n', '\n').splitlines(True)
 
         for regex in filterOutRegexps:
             lines = [line for line in lines if not re.search(regex, line)]
 
         return lines
 
     def saveScreenshot(self, filename):
         """
--- a/testing/mozbase/mozdevice/tests/sut_logcat.py
+++ b/testing/mozbase/mozdevice/tests/sut_logcat.py
@@ -2,46 +2,46 @@
 
 import mozdevice
 import mozlog
 import unittest
 from sut import MockAgent
 
 
 class TestLogCat(unittest.TestCase):
-    """ Class to test methods assosiated with logcat """
+    """ Class to test methods associated with logcat """
 
     def test_getLogcat(self):
 
-        logcat_output = ("07-17 00:51:10.377 I/SUTAgentAndroid( 2933): onCreate\n\r"
-        "07-17 00:51:10.457 D/dalvikvm( 2933): GC_CONCURRENT freed 351K, 17% free 2523K/3008K, paused 5ms+2ms, total 38ms\n\r"
-        "07-17 00:51:10.497 I/SUTAgentAndroid( 2933): Caught exception creating file in /data/local/tmp: open failed: EACCES (Permission denied)\n\r"
-        "07-17 00:51:10.507 E/SUTAgentAndroid( 2933): ERROR: Cannot access world writeable test root\n\r"
-        "07-17 00:51:10.547 D/GeckoHealthRec( 3253): Initializing profile cache.\n\r"
-        "07-17 00:51:10.607 D/GeckoHealthRec( 3253): Looking for /data/data/org.mozilla.fennec/files/mozilla/c09kfhne.default/times.json\n\r"
-        "07-17 00:51:10.637 D/GeckoHealthRec( 3253): Using times.json for profile creation time.\n\r"
-        "07-17 00:51:10.707 D/GeckoHealthRec( 3253): Incorporating environment: times.json profile creation = 1374026758604\n\r"
-        "07-17 00:51:10.507 D/GeckoHealthRec( 3253): Requested prefs.\n\r"
-        "07-17 06:50:54.907 I/SUTAgentAndroid( 3876): \n\r"
-        "07-17 06:50:54.907 I/SUTAgentAndroid( 3876): Total Private Dirty Memory         3176 kb\n\r"
-        "07-17 06:50:54.907 I/SUTAgentAndroid( 3876): Total Proportional Set Size Memory 5679 kb\n\r"
-        "07-17 06:50:54.907 I/SUTAgentAndroid( 3876): Total Shared Dirty Memory          9216 kb\n\r"
+        logcat_output = ("07-17 00:51:10.377 I/SUTAgentAndroid( 2933): onCreate\r\n"
+        "07-17 00:51:10.457 D/dalvikvm( 2933): GC_CONCURRENT freed 351K, 17% free 2523K/3008K, paused 5ms+2ms, total 38ms\r\n"
+        "07-17 00:51:10.497 I/SUTAgentAndroid( 2933): Caught exception creating file in /data/local/tmp: open failed: EACCES (Permission denied)\r\n"
+        "07-17 00:51:10.507 E/SUTAgentAndroid( 2933): ERROR: Cannot access world writeable test root\r\n"
+        "07-17 00:51:10.547 D/GeckoHealthRec( 3253): Initializing profile cache.\r\n"
+        "07-17 00:51:10.607 D/GeckoHealthRec( 3253): Looking for /data/data/org.mozilla.fennec/files/mozilla/c09kfhne.default/times.json\r\n"
+        "07-17 00:51:10.637 D/GeckoHealthRec( 3253): Using times.json for profile creation time.\r\n"
+        "07-17 00:51:10.707 D/GeckoHealthRec( 3253): Incorporating environment: times.json profile creation = 1374026758604\r\n"
+        "07-17 00:51:10.507 D/GeckoHealthRec( 3253): Requested prefs.\r\n"
+        "07-17 06:50:54.907 I/SUTAgentAndroid( 3876): \r\n"
+        "07-17 06:50:54.907 I/SUTAgentAndroid( 3876): Total Private Dirty Memory         3176 kb\r\n"
+        "07-17 06:50:54.907 I/SUTAgentAndroid( 3876): Total Proportional Set Size Memory 5679 kb\r\n"
+        "07-17 06:50:54.907 I/SUTAgentAndroid( 3876): Total Shared Dirty Memory          9216 kb\r\n"
         "07-17 06:55:21.627 I/SUTAgentAndroid( 3876): 127.0.0.1 : execsu /system/bin/logcat -v time -d dalvikvm:I "
-        "ConnectivityService:S WifiMonitor:S WifiStateTracker:S wpa_supplicant:S NetworkStateTracker:S\n\r"
-        "07-17 06:55:21.827 I/dalvikvm-heap( 3876): Grow heap (frag case) to 3.019MB for 102496-byte allocation\n\r"
+        "ConnectivityService:S WifiMonitor:S WifiStateTracker:S wpa_supplicant:S NetworkStateTracker:S\r\n"
+        "07-17 06:55:21.827 I/dalvikvm-heap( 3876): Grow heap (frag case) to 3.019MB for 102496-byte allocation\r\n"
         "return code [0]")
 
         inp = ("execsu /system/bin/logcat -v time -d "
         "dalvikvm:I ConnectivityService:S WifiMonitor:S "
         "WifiStateTracker:S wpa_supplicant:S NetworkStateTracker:S")
 
         commands = [(inp, logcat_output)]
         m = MockAgent(self, commands=commands)
         d = mozdevice.DroidSUT("127.0.0.1", port=m.port, logLevel=mozlog.DEBUG)
-        self.assertEqual(logcat_output[:-17].split('\r'), d.getLogcat())
+        self.assertEqual(logcat_output[:-17].replace('\r\n', '\n').splitlines(True), d.getLogcat())
 
     def test_recordLogcat(self):
 
         commands = [("execsu /system/bin/logcat -c", "return code [0]")]
 
         m = MockAgent(self, commands=commands)
         d = mozdevice.DroidSUT("127.0.0.1", port=m.port, logLevel=mozlog.DEBUG)
         # No error raised means success
--- a/testing/mozharness/mozharness.json
+++ b/testing/mozharness/mozharness.json
@@ -1,4 +1,4 @@
 {
     "repo": "https://hg.mozilla.org/build/mozharness",
-    "revision": "1da0d44cd6ab"
+    "revision": "c9633e9c344a"
 }
--- a/testing/talos/talos.json
+++ b/testing/talos/talos.json
@@ -1,16 +1,16 @@
 {
     "talos.zip": {
         "url": "http://talos-bundles.pvt.build.mozilla.org/zips/talos.3dc0c42bd761.zip",
         "path": ""
     },
     "global": {
         "talos_repo": "https://hg.mozilla.org/build/talos",
-        "talos_revision": "85d4f8ef4810"
+        "talos_revision": "c1da803b295e"
     },
     "extra_options": {
         "android": [ "--apkPath=%(apk_path)s" ]
     },
     "suites": {
         "chromez": {
             "tests": ["tresize", "tcanvasmark"]
         },
--- a/toolkit/xre/nsXREDirProvider.cpp
+++ b/toolkit/xre/nsXREDirProvider.cpp
@@ -630,17 +630,17 @@ nsXREDirProvider::LoadExtensionBundleDir
 
     nsINIParser parser;
     nsresult rv = parser.Init(extensionsINILF);
     if (NS_FAILED(rv))
       return;
 
     RegisterExtensionInterpositions(parser);
     LoadExtensionDirectories(parser, "ExtensionDirs", mExtensionDirectories,
-                             NS_COMPONENT_LOCATION);
+                             NS_EXTENSION_LOCATION);
     LoadExtensionDirectories(parser, "ThemeDirs", mThemeDirectories,
                              NS_SKIN_LOCATION);
   }
 }
 
 void
 nsXREDirProvider::LoadAppBundleDirs()
 {
@@ -662,17 +662,17 @@ nsXREDirProvider::LoadAppBundleDirs()
     return;
 
   nsCOMPtr<nsIFile> subdir;
   while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(subdir))) && subdir) {
     mAppBundleDirectories.AppendObject(subdir);
 
     nsCOMPtr<nsIFile> manifest =
       CloneAndAppend(subdir, "chrome.manifest");
-    XRE_AddManifestLocation(NS_COMPONENT_LOCATION, manifest);
+    XRE_AddManifestLocation(NS_EXTENSION_LOCATION, manifest);
   }
 }
 
 static const char *const kAppendPrefDir[] = { "defaults", "preferences", nullptr };
 
 #ifdef DEBUG_bsmedberg
 static void
 DumpFileArray(const char *key,
--- a/view/nsView.cpp
+++ b/view/nsView.cpp
@@ -668,16 +668,26 @@ nsView::InitializeWindow(bool aEnableDra
 
   //make sure visibility state is accurate
 
   if (aResetVisibility) {
     SetVisibility(GetVisibility());
   }
 }
 
+void
+nsView::SetNeedsWindowPropertiesSync()
+{
+  mNeedsWindowPropertiesSync = true;
+  if (mViewManager) {
+    mViewManager->PostPendingUpdate();
+  }
+}
+
+
 // Attach to a top level widget and start receiving mirrored events.
 nsresult nsView::AttachToTopLevelWidget(nsIWidget* aWidget)
 {
   NS_PRECONDITION(nullptr != aWidget, "null widget ptr");
   /// XXXjimm This is a temporary workaround to an issue w/document
   // viewer (bug 513162).
   nsIWidgetListener* listener = aWidget->GetAttachedWidgetListener();
   if (listener) {
--- a/view/nsView.h
+++ b/view/nsView.h
@@ -285,16 +285,18 @@ public:
    * Returns true if the view has a widget associated with it.
    */
   bool HasWidget() const { return mWindow != nullptr; }
   
   void SetForcedRepaint(bool aForceRepaint) { 
     mForcedRepaint = aForceRepaint; 
   }
 
+  void SetNeedsWindowPropertiesSync();
+
   /**
    * Make aWidget direct its events to this view.
    * The caller must call DetachWidgetEventHandler before this view
    * is destroyed.
    */
   void AttachWidgetEventHandler(nsIWidget* aWidget);
   /**
    * Stop aWidget directing its events to this view.
@@ -458,11 +460,12 @@ private:
   nscoord           mPosX, mPosY;
   // relative to parent, but in our appunits
   nsRect            mDimBounds;
   // in our appunits
   nsPoint           mViewToWidgetOffset;
   uint32_t          mVFlags;
   bool              mWidgetIsTopLevel;
   bool              mForcedRepaint;
+  bool              mNeedsWindowPropertiesSync;
 };
 
 #endif
--- a/view/nsViewManager.cpp
+++ b/view/nsViewManager.cpp
@@ -363,16 +363,27 @@ nsViewManager::ProcessPendingUpdatesForV
   }
 
   nsCOMPtr<nsIPresShell> rootShell(mPresShell);
   nsTArray<nsCOMPtr<nsIWidget> > widgets;
   aView->GetViewManager()->ProcessPendingUpdatesRecurse(aView, widgets);
   for (uint32_t i = 0; i < widgets.Length(); ++i) {
     nsView* view = nsView::GetViewFor(widgets[i]);
     if (view) {
+      if (view->mNeedsWindowPropertiesSync) {
+        view->mNeedsWindowPropertiesSync = false;
+        if (nsViewManager* vm = view->GetViewManager()) {
+          if (nsIPresShell* ps = vm->GetPresShell()) {
+            ps->SyncWindowProperties(view);
+          }
+        }
+      }
+    }
+    view = nsView::GetViewFor(widgets[i]);
+    if (view) {
       view->ResetWidgetBounds(false, true);
     }
   }
   if (rootShell->GetViewManager() != this) {
     return; // 'this' might have been destroyed
   }
   if (aFlushDirtyRegion) {
     profiler_tracing("Paint", "DisplayList", TRACING_INTERVAL_START);
--- a/widget/gtk/mozgtk/mozgtk.c
+++ b/widget/gtk/mozgtk/mozgtk.c
@@ -37,16 +37,17 @@ STUB(gdk_event_put)
 STUB(gdk_flush)
 STUB(gdk_get_default_root_window)
 STUB(gdk_get_display)
 STUB(gdk_get_display_arg_name)
 STUB(gdk_get_program_class)
 STUB(gdk_keymap_get_default)
 STUB(gdk_keymap_get_direction)
 STUB(gdk_keymap_get_entries_for_keyval)
+STUB(gdk_keymap_get_for_display)
 STUB(gdk_keymap_have_bidi_layouts)
 STUB(gdk_keymap_translate_keyboard_state)
 STUB(gdk_keyval_name)
 STUB(gdk_keyval_to_unicode)
 STUB(gdk_pango_context_get)
 STUB(gdk_pointer_grab)
 STUB(gdk_pointer_ungrab)
 STUB(gdk_property_get)
--- a/widget/gtk/nsBidiKeyboard.cpp
+++ b/widget/gtk/nsBidiKeyboard.cpp
@@ -17,17 +17,23 @@ nsBidiKeyboard::nsBidiKeyboard()
     Reset();
 }
 
 NS_IMETHODIMP
 nsBidiKeyboard::Reset()
 {
     // NB: The default keymap can be null (e.g. in xpcshell). In that case,
     // simply assume that we don't have bidi keyboards.
-    GdkKeymap *keymap = gdk_keymap_get_default();
+    mHaveBidiKeyboards = false;
+
+    GdkDisplay *display = gdk_display_get_default();
+    if (!display)
+        return NS_OK;
+
+    GdkKeymap *keymap = gdk_keymap_get_for_display(display);
     mHaveBidiKeyboards = keymap && gdk_keymap_have_bidi_layouts(keymap);
     return NS_OK;
 }
 
 nsBidiKeyboard::~nsBidiKeyboard()
 {
 }
 
--- a/xpcom/build/nsXULAppAPI.h
+++ b/xpcom/build/nsXULAppAPI.h
@@ -235,25 +235,29 @@ XRE_API(nsresult,
  * Register XPCOM components found in an array of files/directories.
  * This method may be called at any time before or after XRE_main or
  * XRE_InitEmbedding.
  *
  * @param aFiles An array of files or directories.
  * @param aFileCount the number of items in the aFiles array.
  * @note appdir/components is registered automatically.
  *
- * NS_COMPONENT_LOCATION specifies a location to search for binary XPCOM
+ * NS_APP_LOCATION specifies a location to search for binary XPCOM
  * components as well as component/chrome manifest files.
  *
+ * NS_EXTENSION_LOCATION excludes binary XPCOM components but allows other
+ * manifest instructions.
+ *
  * NS_SKIN_LOCATION specifies a location to search for chrome manifest files
  * which are only allowed to register only skin packages and style overlays.
  */
 enum NSLocationType
 {
-  NS_COMPONENT_LOCATION,
+  NS_APP_LOCATION,
+  NS_EXTENSION_LOCATION,
   NS_SKIN_LOCATION,
   NS_BOOTSTRAPPED_LOCATION
 };
 
 XRE_API(nsresult,
         XRE_AddManifestLocation, (NSLocationType aType,
                                   nsIFile* aLocation))
 
--- a/xpcom/components/ManifestParser.cpp
+++ b/xpcom/components/ManifestParser.cpp
@@ -52,18 +52,20 @@
 
 using namespace mozilla;
 
 struct ManifestDirective
 {
   const char* directive;
   int argc;
 
-  // Some directives should only be delivered for NS_COMPONENT_LOCATION
-  // manifests.
+  // Binary components are only allowed for APP locations.
+  bool apponly;
+
+  // Some directives should only be delivered for APP or EXTENSION locations.
   bool componentonly;
 
   bool ischrome;
 
   bool allowbootstrap;
 
   // The platform/contentaccessible flags only apply to content directives.
   bool contentflags;
@@ -84,65 +86,65 @@ struct ManifestDirective
 #else
   void* xptonlyfunc;
 #endif
 
   bool isContract;
 };
 static const ManifestDirective kParsingTable[] = {
   {
-    "manifest",         1, false, true, true, false,
+    "manifest",         1, false, false, true, true, false,
     &nsComponentManagerImpl::ManifestManifest, nullptr, XPTONLY_MANIFEST
   },
   {
-    "binary-component", 1, true, false, false, false,
+    "binary-component", 1, true, true, false, false, false,
     &nsComponentManagerImpl::ManifestBinaryComponent, nullptr, nullptr
   },
   {
-    "interfaces",       1, true, false, false, false,
+    "interfaces",       1, false, true, false, false, false,
     &nsComponentManagerImpl::ManifestXPT, nullptr, XPTONLY_XPT
   },
   {
-    "component",        2, true, false, false, false,
+    "component",        2, false, true, false, false, false,
     &nsComponentManagerImpl::ManifestComponent, nullptr, nullptr
   },
   {
-    "contract",         2, true, false, false, false,
+    "contract",         2, false, true, false, false, false,
     &nsComponentManagerImpl::ManifestContract, nullptr, nullptr, true
   },
   {
-    "category",         3, true, false, false, false,
+    "category",         3, false, true, false, false, false,
     &nsComponentManagerImpl::ManifestCategory, nullptr, nullptr
   },
   {
-    "content",          2, true, true, true,  true,
+    "content",          2, false, true, true, true,  true,
     nullptr, &nsChromeRegistry::ManifestContent, nullptr
   },
   {
-    "locale",           3, true, true, true, false,
+    "locale",           3, false, true, true, true, false,
     nullptr, &nsChromeRegistry::ManifestLocale, nullptr
   },
   {
-    "skin",             3, false, true, true, false,
+    "skin",             3, false, false, true, true, false,
     nullptr, &nsChromeRegistry::ManifestSkin, nullptr
   },
   {
-    "overlay",          2, true, true, false, false,
+    "overlay",          2, false, true, true, false, false,
     nullptr, &nsChromeRegistry::ManifestOverlay, nullptr
   },
   {
-    "style",            2, false, true, false, false,
+    "style",            2, false, false, true, false, false,
     nullptr, &nsChromeRegistry::ManifestStyle, nullptr
   },
   {
-    "override",         2, true, true, true, false,
+    "override",         2, false, true, true, true, false,
     nullptr, &nsChromeRegistry::ManifestOverride, nullptr
   },
   {
-    "resource",         2, true, true, true, false,
+    "resource",         2, false, true, true, true, false,
     nullptr, &nsChromeRegistry::ManifestResource, nullptr
   }
 };
 
 static const char kWhitespace[] = "\t ";
 
 static bool
 IsNewline(char aChar)
@@ -650,16 +652,22 @@ ParseManifest(NSLocationType aType, File
 
     if (!directive->allowbootstrap && NS_BOOTSTRAPPED_LOCATION == aType) {
       LogMessageWithContext(aFile, line,
                             "Bootstrapped manifest not allowed to use '%s' directive.",
                             token);
       continue;
     }
 
+    if (directive->apponly && NS_APP_LOCATION != aType) {
+      LogMessageWithContext(aFile, line,
+                            "Only application manifests may use the '%s' directive.", token);
+      continue;
+    }
+
     if (directive->componentonly && NS_SKIN_LOCATION == aType) {
       LogMessageWithContext(aFile, line,
                             "Skin manifest not allowed to use '%s' directive.",
                             token);
       continue;
     }
 
     NS_ASSERTION(directive->argc < 4, "Need to reset argv array length");
--- a/xpcom/components/nsComponentManager.cpp
+++ b/xpcom/components/nsComponentManager.cpp
@@ -413,41 +413,41 @@ nsComponentManagerImpl::Init()
   // - greDir's omni.ja
   // - appDir
   // - appDir's omni.ja
 
   InitializeModuleLocations();
   ComponentLocation* cl = sModuleLocations->AppendElement();
   nsCOMPtr<nsIFile> lf = CloneAndAppend(greDir,
                                         NS_LITERAL_CSTRING("chrome.manifest"));
-  cl->type = NS_COMPONENT_LOCATION;
+  cl->type = NS_APP_LOCATION;
   cl->location.Init(lf);
 
   nsRefPtr<nsZipArchive> greOmnijar =
     mozilla::Omnijar::GetReader(mozilla::Omnijar::GRE);
   if (greOmnijar) {
     cl = sModuleLocations->AppendElement();
-    cl->type = NS_COMPONENT_LOCATION;
+    cl->type = NS_APP_LOCATION;
     cl->location.Init(greOmnijar, "chrome.manifest");
   }
 
   bool equals = false;
   appDir->Equals(greDir, &equals);
   if (!equals) {
     cl = sModuleLocations->AppendElement();
-    cl->type = NS_COMPONENT_LOCATION;
+    cl->type = NS_APP_LOCATION;
     lf = CloneAndAppend(appDir, NS_LITERAL_CSTRING("chrome.manifest"));
     cl->location.Init(lf);
   }
 
   nsRefPtr<nsZipArchive> appOmnijar =
     mozilla::Omnijar::GetReader(mozilla::Omnijar::APP);
   if (appOmnijar) {
     cl = sModuleLocations->AppendElement();
-    cl->type = NS_COMPONENT_LOCATION;
+    cl->type = NS_APP_LOCATION;
     cl->location.Init(appOmnijar, "chrome.manifest");
   }
 
   RereadChromeManifests(false);
 
   nsCategoryManager::GetSingleton()->SuppressNotifications(false);
 
   RegisterWeakMemoryReporter(this);
@@ -1707,17 +1707,17 @@ nsComponentManagerImpl::UnregisterFactor
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsComponentManagerImpl::AutoRegister(nsIFile* aLocation)
 {
 #if !defined(MOZILLA_XPCOMRT_API)
-  XRE_AddManifestLocation(NS_COMPONENT_LOCATION, aLocation);
+  XRE_AddManifestLocation(NS_EXTENSION_LOCATION, aLocation);
   return NS_OK;
 #else
   return NS_ERROR_NOT_IMPLEMENTED;
 #endif // !defined(MOZILLA_XPCOMRT_API)
 }
 
 NS_IMETHODIMP
 nsComponentManagerImpl::AutoUnregister(nsIFile* aLocation)
@@ -2146,17 +2146,17 @@ nsComponentManagerImpl::GetManifestLocat
 /* static */
 void
 nsComponentManagerImpl::XPTOnlyManifestManifest(
     XPTOnlyManifestProcessingContext&  aCx, int aLineNo, char* const* aArgv)
 {
   char* file = aArgv[0];
   FileLocation f(aCx.mFile, file);
 
-  DoRegisterManifest(NS_COMPONENT_LOCATION, f, false, true);
+  DoRegisterManifest(NS_APP_LOCATION, f, false, true);
 }
 
 /* static */
 void
 nsComponentManagerImpl::XPTOnlyManifestXPT(
     XPTOnlyManifestProcessingContext& aCx, int aLineNo, char* const* aArgv)
 {
   FileLocation f(aCx.mFile, aArgv[0]);
@@ -2170,17 +2170,17 @@ nsComponentManagerImpl::XPTOnlyManifestX
  * as possible to gain benefit of shared memory model of the kernel.
  */
 /* static */ void
 nsComponentManagerImpl::PreloadXPT(nsIFile* aFile)
 {
   MOZ_ASSERT(!nsComponentManagerImpl::gComponentManager);
   FileLocation location(aFile, "chrome.manifest");
 
-  DoRegisterManifest(NS_COMPONENT_LOCATION, location,
+  DoRegisterManifest(NS_APP_LOCATION, location,
                      false, true /* aXPTOnly */);
 }
 
 void
 PreloadXPT(nsIFile* aOmnijarFile)
 {
   nsComponentManagerImpl::PreloadXPT(aOmnijarFile);
 }
--- a/xpcom/tests/TestRegistrationOrder.cpp
+++ b/xpcom/tests/TestRegistrationOrder.cpp
@@ -165,21 +165,21 @@ int main(int argc, char** argv)
     char curr = argv[1][i];
     regPath[i] = (curr == '/') ? '\\' : curr;
   }
   regPath[regPathLen] = '\0';
 #else
   const char *regPath = argv[1];
 #endif
 
-  XRE_AddManifestLocation(NS_COMPONENT_LOCATION,
+  XRE_AddManifestLocation(NS_EXTENSION_LOCATION,
                           nsCOMPtr<nsIFile>(GetRegDirectory(regPath, "core", "component.manifest")));
-  XRE_AddManifestLocation(NS_COMPONENT_LOCATION,
+  XRE_AddManifestLocation(NS_EXTENSION_LOCATION,
                           nsCOMPtr<nsIFile>(GetRegDirectory(regPath, "extension", "extComponent.manifest")));
-  XRE_AddJarManifestLocation(NS_COMPONENT_LOCATION,
+  XRE_AddJarManifestLocation(NS_EXTENSION_LOCATION,
                           nsCOMPtr<nsIFile>(GetRegDirectory(regPath, "extension2.jar", nullptr)));
   ScopedXPCOM xpcom("RegistrationOrder");
   if (xpcom.failed())
     return 1;
 
   int rv = 0;
   if (NS_FAILED(TestRegular()))
     rv = 1;
--- a/xpcom/tests/unit/test_bug656331.js
+++ b/xpcom/tests/unit/test_bug656331.js
@@ -20,17 +20,17 @@ const kConsoleListener = {
 };
 
 function run_test() {
   let cs = Components.classes["@mozilla.org/consoleservice;1"].
     getService(Ci.nsIConsoleService);
   cs.registerListener(kConsoleListener);
 
   let manifest = do_get_file('bug656331.manifest');
-  Components.manager.autoRegister(manifest);
+  registerAppManifest(manifest);
 
   do_check_false("{f18fb09b-28b4-4435-bc5b-8027f18df743}" in Components.classesByID);
 
   do_test_pending();
   Components.classes["@mozilla.org/thread-manager;1"].
     getService(Ci.nsIThreadManager).mainThread.dispatch(function() {
       cs.unregisterListener(kConsoleListener);
       do_check_true(gFound);
--- a/xpcom/tests/unit/test_comp_no_aslr.js
+++ b/xpcom/tests/unit/test_comp_no_aslr.js
@@ -1,16 +1,16 @@
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 function run_test() {
   let manifest = do_get_file('testcompnoaslr.manifest');
-  Components.manager.autoRegister(manifest);
+  registerAppManifest(manifest);
   var sysInfo = Cc["@mozilla.org/system-info;1"].
                 getService(Ci.nsIPropertyBag2);
   var ver = parseFloat(sysInfo.getProperty("version"));
   if (ver < 6.0) {
     // This is disabled on pre-Vista OSs.
     do_check_true("{335fb596-e52d-418f-b01c-1bf16ce5e7e4}" in Components.classesByID);
   } else {
     do_check_false("{335fb596-e52d-418f-b01c-1bf16ce5e7e4}" in Components.classesByID);
--- a/xpcom/tests/unit/test_compmgr_warnings.js
+++ b/xpcom/tests/unit/test_compmgr_warnings.js
@@ -51,20 +51,19 @@ function run_deferred_event(fn) {
 
 function run_test()
 {
   let cs = Components.classes["@mozilla.org/consoleservice;1"].
     getService(Ci.nsIConsoleService);
   cs.registerListener(kConsoleListener);
 
   var manifest = do_get_file('compmgr_warnings.manifest');
-  Components.manager.QueryInterface(Ci.nsIComponentRegistrar).
-    autoRegister(manifest);
+  registerAppManifest(manifest);
   manifest = do_get_file('testcomponent.manifest');
-  Components.manager.autoRegister(manifest);
+  registerAppManifest(manifest);
 
   run_deferred_event(function() {
     cs.unregisterListener(kConsoleListener);
 
     for each (let expected in gMessagesExpected) {
       info("checking " + expected.message);
       do_check_true(expected.found);
     }