Merge m-c to fx-team a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 01 Jul 2014 17:41:10 -0700
changeset 191863 15188cb383b41dd8b2adb12a75bf09fcf8816b52
parent 191862 a0b340c5704d9de45c6b35caeb87671ff4276887 (current diff)
parent 191811 7075808c330682f47f1bec7468767b0731c5c86a (diff)
child 191864 185b1876c7ffb9a47bcb93147af005fe2ff1b7ae
push id45685
push usercbook@mozilla.com
push dateWed, 02 Jul 2014 13:09:48 +0000
treeherdermozilla-inbound@60133a85f8ae [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to fx-team a=merge
mobile/android/base/tests/robocop.ini
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="34a52e7f024cc3d0e3aade94970773d2555f5ccb"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="85e97290431ce6aa0a965421e84d6070cd899129"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="3326b51017252e09ccdd715dec6c5e12a7d1ecfe"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="34a52e7f024cc3d0e3aade94970773d2555f5ccb"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="85e97290431ce6aa0a965421e84d6070cd899129"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="276ce45e78b09c4a4ee643646f691d22804754c1">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="34a52e7f024cc3d0e3aade94970773d2555f5ccb"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="85e97290431ce6aa0a965421e84d6070cd899129"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="34a52e7f024cc3d0e3aade94970773d2555f5ccb"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="85e97290431ce6aa0a965421e84d6070cd899129"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="3326b51017252e09ccdd715dec6c5e12a7d1ecfe"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="34a52e7f024cc3d0e3aade94970773d2555f5ccb"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="85e97290431ce6aa0a965421e84d6070cd899129"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "ef20236bd60e81103e4d469bd29fd90834dee380", 
+    "revision": "ec3cab15f129926a6cfa5e95df71a0c913f34aee", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="34a52e7f024cc3d0e3aade94970773d2555f5ccb"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="85e97290431ce6aa0a965421e84d6070cd899129"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="34a52e7f024cc3d0e3aade94970773d2555f5ccb"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="85e97290431ce6aa0a965421e84d6070cd899129"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="cc67f31dc638c0b7edba3cf7e3d87cadf0ed52bf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="34a52e7f024cc3d0e3aade94970773d2555f5ccb"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="85e97290431ce6aa0a965421e84d6070cd899129"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="2a165bebfa19b11b697837409f9550dd2917c46c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="34a52e7f024cc3d0e3aade94970773d2555f5ccb"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="85e97290431ce6aa0a965421e84d6070cd899129"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="bbb7659d8ea2afb396f99b3dc971ab3c42da3778"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="96cdde4b5b5d8d3785b36c3c68cd746aff3005cc"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="7f9ec13a30f1b2cc8bdb1a199b7da54b9ab8860f"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/browser/components/loop/MozLoopAPI.jsm
+++ b/browser/components/loop/MozLoopAPI.jsm
@@ -128,16 +128,34 @@ function injectLoopAPI(targetWindow) {
       configurable: true,
       writable: true,
       value: function(expiryTimeSeconds) {
         MozLoopService.noteCallUrlExpiry(expiryTimeSeconds);
       }
     },
 
     /**
+     * Set any character preference under "loop."
+     *
+     * @param {String} prefName The name of the pref without the preceding "loop."
+     * @param {String} stringValue The value to set.
+     *
+     * Any errors thrown by the Mozilla pref API are logged to the console
+     * and cause false to be returned.
+     */
+    setLoopCharPref: {
+      enumerable: true,
+      configurable: true,
+      writable: true,
+      value: function(prefName, value) {
+        MozLoopService.setLoopCharPref(prefName, value);
+      }
+    },
+
+    /**
      * Return any preference under "loop." that's coercible to a character
      * preference.
      *
      * @param {String} prefName The name of the pref without the preceding
      * "loop."
      *
      * Any errors thrown by the Mozilla pref API are logged to the console
      * and cause null to be returned. This includes the case of the preference
--- a/browser/components/loop/MozLoopService.jsm
+++ b/browser/components/loop/MozLoopService.jsm
@@ -439,16 +439,33 @@ this.MozLoopService = {
       return Services.prefs.getComplexValue("general.useragent.locale",
         Ci.nsISupportsString).data;
     } catch (ex) {
       return "en-US";
     }
   },
 
   /**
+   * Set any character preference under "loop.".
+   *
+   * @param {String} prefName The name of the pref without the preceding "loop."
+   * @param {String} value The value to set.
+   *
+   * Any errors thrown by the Mozilla pref API are logged to the console.
+   */
+  setLoopCharPref: function(prefName, value) {
+    try {
+      Services.prefs.setCharPref("loop." + prefName, value);
+    } catch (ex) {
+      console.log("setLoopCharPref had trouble setting " + prefName +
+        "; exception: " + ex);
+    }
+  },
+
+  /**
    * Return any preference under "loop." that's coercible to a character
    * preference.
    *
    * @param {String} prefName The name of the pref without the preceding
    * "loop."
    *
    * Any errors thrown by the Mozilla pref API are logged to the console
    * and cause null to be returned. This includes the case of the preference
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/test/xpcshell/test_loopservice_set_loop_char_pref.js
@@ -0,0 +1,49 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+/*global XPCOMUtils, Services, Assert */
+
+var fakePrefName = "color";
+var fakePrefValue = "green";
+
+function test_setLoopCharPref()
+{
+  Services.prefs.setCharPref("loop." + fakePrefName, "red");
+  MozLoopService.setLoopCharPref(fakePrefName, fakePrefValue);
+
+  var returnedPref = Services.prefs.getCharPref("loop." + fakePrefName);
+
+  Assert.equal(returnedPref, fakePrefValue,
+    "Should set a char pref under the loop. branch");
+  Services.prefs.clearUserPref("loop." + fakePrefName);
+}
+
+function test_setLoopCharPref_new()
+{
+  Services.prefs.clearUserPref("loop." + fakePrefName);
+  MozLoopService.setLoopCharPref(fakePrefName, fakePrefValue);
+
+  var returnedPref = Services.prefs.getCharPref("loop." + fakePrefName);
+
+  Assert.equal(returnedPref, fakePrefValue,
+               "Should set a new char pref under the loop. branch");
+  Services.prefs.clearUserPref("loop." + fakePrefName);
+}
+
+function test_setLoopCharPref_non_coercible_type()
+{
+  MozLoopService.setLoopCharPref(fakePrefName, true);
+
+  ok(true, "Setting non-coercible type should not fail");
+}
+
+
+function run_test()
+{
+  test_setLoopCharPref();
+  test_setLoopCharPref_new();
+  test_setLoopCharPref_non_coercible_type();
+
+  do_register_cleanup(function() {
+    Services.prefs.clearUserPref("loop." + fakePrefName);
+  });
+}
--- a/browser/components/loop/test/xpcshell/xpcshell.ini
+++ b/browser/components/loop/test/xpcshell/xpcshell.ini
@@ -2,14 +2,15 @@
 head = head.js
 tail =
 firefox-appdir = browser
 
 [test_looppush_initialize.js]
 [test_loopservice_dnd.js]
 [test_loopservice_expiry.js]
 [test_loopservice_get_loop_char_pref.js]
+[test_loopservice_set_loop_char_pref.js]
 [test_loopservice_initialize.js]
 [test_loopservice_locales.js]
 [test_loopservice_registration.js]
 [test_loopservice_token_save.js]
 [test_loopservice_token_send.js]
 [test_loopservice_token_validation.js]
--- a/browser/components/moz.build
+++ b/browser/components/moz.build
@@ -5,29 +5,31 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 PARALLEL_DIRS += [
     'about',
     'customizableui',
     'dirprovider',
     'downloads',
     'feeds',
-    'loop',
     'places',
     'preferences',
     'privatebrowsing',
     'search',
     'sessionstore',
     'shell',
     'sidebar',
     'tabview',
     'translation',
     'migration',
 ]
 
+if CONFIG['MOZ_LOOP']:
+    PARALLEL_DIRS += ['loop']
+
 DIRS += ['build']
 
 XPIDL_SOURCES += [
     'nsIBrowserGlue.idl',
     'nsIBrowserHandler.idl',
 ]
 
 XPIDL_MODULE = 'browsercompsbase'
--- a/build/gyp.mozbuild
+++ b/build/gyp.mozbuild
@@ -60,16 +60,17 @@ if os == 'WINNT':
     gyp_vars.update(
         MSVS_VERSION=CONFIG['_MSVS_VERSION'],
         MSVS_OS_BITS=64 if CONFIG['HAVE_64BIT_BUILD'] else 32,
     )
 elif os == 'Android':
     if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
         gyp_vars['build_with_gonk'] = 1
         gyp_vars['moz_widget_toolkit_gonk'] = 1
+        gyp_vars['opus_complexity'] = 1
         if int(CONFIG['ANDROID_VERSION']) >= 18:
           gyp_vars['moz_webrtc_omx'] = 1
     else:
         gyp_vars.update(
             gtest_target_type='executable',
             android_toolchain=CONFIG['ANDROID_TOOLCHAIN'],
         )
 
--- a/content/base/src/Element.cpp
+++ b/content/base/src/Element.cpp
@@ -473,26 +473,29 @@ Element::WrapObject(JSContext *aCx)
   bool dummy;
 
   nsXBLService* xblService = nsXBLService::GetInstance();
   if (!xblService) {
     dom::Throw(aCx, NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
-  nsRefPtr<nsXBLBinding> binding;
-  xblService->LoadBindings(this, uri, principal, getter_AddRefs(binding), &dummy);
-  
-  if (binding) {
-    if (nsContentUtils::IsSafeToRunScript()) {
-      binding->ExecuteAttachedHandler();
-    }
-    else {
-      nsContentUtils::AddScriptRunner(
-        NS_NewRunnableMethod(binding, &nsXBLBinding::ExecuteAttachedHandler));
+  {
+    // Make a scope so that ~nsRefPtr can GC before returning obj.
+    nsRefPtr<nsXBLBinding> binding;
+    xblService->LoadBindings(this, uri, principal, getter_AddRefs(binding), &dummy);
+
+    if (binding) {
+      if (nsContentUtils::IsSafeToRunScript()) {
+        binding->ExecuteAttachedHandler();
+      }
+      else {
+        nsContentUtils::AddScriptRunner(
+          NS_NewRunnableMethod(binding, &nsXBLBinding::ExecuteAttachedHandler));
+      }
     }
   }
 
   return obj;
 }
 
 nsDOMTokenList*
 Element::ClassList()
--- a/content/base/src/FileIOObject.cpp
+++ b/content/base/src/FileIOObject.cpp
@@ -152,17 +152,17 @@ FileIOObject::OnInputStreamReady(nsIAsyn
   if (NS_SUCCEEDED(rv) && aCount) {
     rv = DoReadData(aStream, aCount);
   }
 
   if (NS_SUCCEEDED(rv)) {
     rv = DoAsyncWait(aStream);
   }
 
-  if (!aCount || NS_FAILED(rv)) {
+  if (NS_FAILED(rv) || !aCount) {
     if (rv == NS_BASE_STREAM_CLOSED) {
       rv = NS_OK;
     }
     return OnLoadEnd(rv);
   }
 
   mTransferred += aCount;
 
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -1530,23 +1530,25 @@ nsFrameScriptExecutor::TryCacheLoadAndCo
     if (global) {
       JSAutoCompartment ac(cx, global);
       JS::CompileOptions options(cx);
       options.setFileAndLine(url.get(), 1);
       JS::Rooted<JSScript*> script(cx);
       JS::Rooted<JSObject*> funobj(cx);
       if (aRunInGlobalScope) {
         options.setNoScriptRval(true);
-        script = JS::Compile(cx, JS::NullPtr(), options, srcBuf);
+        if (!JS::Compile(cx, JS::NullPtr(), options, srcBuf, &script)) {
+          return;
+        }
       } else {
         JS::Rooted<JSFunction *> fun(cx);
-        fun = JS::CompileFunction(cx, JS::NullPtr(), options,
-                                  nullptr, 0, nullptr, /* name, nargs, args */
-                                  srcBuf);
-        if (!fun) {
+        if (!JS::CompileFunction(cx, JS::NullPtr(), options,
+                                 nullptr, 0, nullptr, /* name, nargs, args */
+                                 srcBuf, &fun))
+        {
           return;
         }
         funobj = JS_GetFunctionObject(fun);
       }
 
       if (!script && !funobj) {
         return;
       }
--- a/content/canvas/src/ImageEncoder.cpp
+++ b/content/canvas/src/ImageEncoder.cpp
@@ -16,53 +16,52 @@ namespace dom {
 
 class EncodingCompleteEvent : public nsRunnable
 {
   virtual ~EncodingCompleteEvent() {}
 
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
 
-  EncodingCompleteEvent(nsIScriptContext* aScriptContext,
+  EncodingCompleteEvent(nsIGlobalObject* aGlobal,
                         nsIThread* aEncoderThread,
                         FileCallback& aCallback)
     : mImgSize(0)
     , mType()
     , mImgData(nullptr)
-    , mScriptContext(aScriptContext)
+    , mGlobal(aGlobal)
     , mEncoderThread(aEncoderThread)
     , mCallback(&aCallback)
     , mFailed(false)
   {}
 
   NS_IMETHOD Run()
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     mozilla::ErrorResult rv;
 
     if (!mFailed) {
       nsRefPtr<DOMFile> blob =
         DOMFile::CreateMemoryFile(mImgData, mImgSize, mType);
 
-      if (mScriptContext) {
-        JSContext* jsContext = mScriptContext->GetNativeContext();
-        if (jsContext) {
-          JS_updateMallocCounter(jsContext, mImgSize);
-        }
+      {
+        AutoJSAPI jsapi;
+        jsapi.Init(mGlobal);
+        JS_updateMallocCounter(jsapi.cx(), mImgSize);
       }
 
       mCallback->Call(blob, rv);
     }
 
     // These members aren't thread-safe. We're making sure that they're being
     // released on the main thread here. Otherwise, they could be getting
     // released by EncodingRunnable's destructor on the encoding thread
     // (bug 916128).
-    mScriptContext = nullptr;
+    mGlobal = nullptr;
     mCallback = nullptr;
 
     mEncoderThread->Shutdown();
     return rv.ErrorCode();
   }
 
   void SetMembers(void* aImgData, uint64_t aImgSize, const nsAutoString& aType)
   {
@@ -75,17 +74,17 @@ public:
   {
     mFailed = true;
   }
 
 private:
   uint64_t mImgSize;
   nsAutoString mType;
   void* mImgData;
-  nsCOMPtr<nsIScriptContext> mScriptContext;
+  nsCOMPtr<nsIGlobalObject> mGlobal;
   nsCOMPtr<nsIThread> mEncoderThread;
   nsRefPtr<FileCallback> mCallback;
   bool mFailed;
 };
 
 NS_IMPL_ISUPPORTS(EncodingCompleteEvent, nsIRunnable);
 
 class EncodingRunnable : public nsRunnable
@@ -204,30 +203,32 @@ ImageEncoder::ExtractData(nsAString& aTy
 nsresult
 ImageEncoder::ExtractDataAsync(nsAString& aType,
                                const nsAString& aOptions,
                                bool aUsingCustomOptions,
                                uint8_t* aImageBuffer,
                                int32_t aFormat,
                                const nsIntSize aSize,
                                nsICanvasRenderingContextInternal* aContext,
-                               nsIScriptContext* aScriptContext,
+                               nsIGlobalObject* aGlobal,
                                FileCallback& aCallback)
 {
+  MOZ_ASSERT(aGlobal);
+
   nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
   if (!encoder) {
     return NS_IMAGELIB_ERROR_NO_ENCODER;
   }
 
   nsCOMPtr<nsIThread> encoderThread;
   nsresult rv = NS_NewThread(getter_AddRefs(encoderThread), nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsRefPtr<EncodingCompleteEvent> completeEvent =
-    new EncodingCompleteEvent(aScriptContext, encoderThread, aCallback);
+    new EncodingCompleteEvent(aGlobal, encoderThread, aCallback);
 
   nsCOMPtr<nsIRunnable> event = new EncodingRunnable(aType,
                                                      aOptions,
                                                      aImageBuffer,
                                                      encoder,
                                                      completeEvent,
                                                      aFormat,
                                                      aSize,
--- a/content/canvas/src/ImageEncoder.h
+++ b/content/canvas/src/ImageEncoder.h
@@ -10,16 +10,17 @@
 #include "nsDOMFile.h"
 #include "nsError.h"
 #include "mozilla/dom/HTMLCanvasElementBinding.h"
 #include "nsLayoutUtils.h"
 #include "nsNetUtil.h"
 #include "nsSize.h"
 
 class nsICanvasRenderingContextInternal;
+class nsIGlobalObject;
 
 namespace mozilla {
 namespace dom {
 
 class EncodingRunnable;
 
 class ImageEncoder
 {
@@ -45,17 +46,17 @@ public:
   // successful dispatching of the extraction step to the encoding thread.
   static nsresult ExtractDataAsync(nsAString& aType,
                                    const nsAString& aOptions,
                                    bool aUsingCustomOptions,
                                    uint8_t* aImageBuffer,
                                    int32_t aFormat,
                                    const nsIntSize aSize,
                                    nsICanvasRenderingContextInternal* aContext,
-                                   nsIScriptContext* aScriptContext,
+                                   nsIGlobalObject* aGlobal,
                                    FileCallback& aCallback);
 
   // Gives you a stream containing the image represented by aImageBuffer.
   // The format is given in aFormat, for example
   // imgIEncoder::INPUT_FORMAT_HOSTARGB.
   static nsresult GetInputStream(int32_t aWidth,
                                  int32_t aHeight,
                                  uint8_t* aImageBuffer,
@@ -84,9 +85,9 @@ private:
   static already_AddRefed<imgIEncoder> GetImageEncoder(nsAString& aType);
 
   friend class EncodingRunnable;
 };
 
 } // namespace dom
 } // namespace mozilla
 
-#endif // ImageEncoder_h
\ No newline at end of file
+#endif // ImageEncoder_h
--- a/content/html/content/src/HTMLCanvasElement.cpp
+++ b/content/html/content/src/HTMLCanvasElement.cpp
@@ -538,33 +538,32 @@ HTMLCanvasElement::ToBlob(JSContext* aCx
     nsIntSize elementSize = GetWidthHeight();
     MOZ_ASSERT(elementSize.width == mCurrentContext->GetWidth() ||
                (elementSize.width == 0 && mCurrentContext->GetWidth() == 1));
     MOZ_ASSERT(elementSize.height == mCurrentContext->GetHeight() ||
                (elementSize.height == 0 && mCurrentContext->GetHeight() == 1));
   }
 #endif
 
-  nsCOMPtr<nsIScriptContext> scriptContext =
-    GetScriptContextFromJSContext(nsContentUtils::GetCurrentJSContext());
-
   uint8_t* imageBuffer = nullptr;
   int32_t format = 0;
   if (mCurrentContext) {
     mCurrentContext->GetImageBuffer(&imageBuffer, &format);
   }
 
+  nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
+  MOZ_ASSERT(global);
   aRv = ImageEncoder::ExtractDataAsync(type,
                                        params,
                                        usingCustomParseOptions,
                                        imageBuffer,
                                        format,
                                        GetSize(),
                                        mCurrentContext,
-                                       scriptContext,
+                                       global,
                                        aCallback);
 }
 
 already_AddRefed<nsIDOMFile>
 HTMLCanvasElement::MozGetAsFile(const nsAString& aName,
                                 const nsAString& aType,
                                 ErrorResult& aRv)
 {
--- a/content/media/webrtc/MediaEngineTabVideoSource.cpp
+++ b/content/media/webrtc/MediaEngineTabVideoSource.cpp
@@ -48,17 +48,17 @@ MediaEngineTabVideoSource::StartRunnable
   mVideoSource->mTabSource->NotifyStreamStart(mVideoSource->mWindow);
   return NS_OK;
 }
 
 nsresult
 MediaEngineTabVideoSource::StopRunnable::Run()
 {
   nsCOMPtr<nsPIDOMWindow> privateDOMWindow = do_QueryInterface(mVideoSource->mWindow);
-  if (privateDOMWindow && mVideoSource && privateDOMWindow->GetChromeEventHandler()) {
+  if (privateDOMWindow && privateDOMWindow->GetChromeEventHandler()) {
     privateDOMWindow->GetChromeEventHandler()->RemoveEventListener(NS_LITERAL_STRING("MozAfterPaint"), mVideoSource, false);
   }
 
   if (mVideoSource->mTimer) {
     mVideoSource->mTimer->Cancel();
     mVideoSource->mTimer = nullptr;
   }
   mVideoSource->mTabSource->NotifyStreamStop(mVideoSource->mWindow);
@@ -279,16 +279,19 @@ MediaEngineTabVideoSource::Draw() {
 
   MonitorAutoLock mon(mMonitor);
   mImage = image;
 }
 
 nsresult
 MediaEngineTabVideoSource::Stop(mozilla::SourceMediaStream*, mozilla::TrackID)
 {
+  if (!mWindow)
+    return NS_OK;
+
   NS_DispatchToMainThread(new StopRunnable(this));
   return NS_OK;
 }
 
 nsresult
 MediaEngineTabVideoSource::Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t)
 {
   return NS_OK;
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -2757,18 +2757,18 @@ nsXULPrototypeScript::Compile(JS::Source
                                   aSrcBuf.get(), aSrcBuf.length(),
                                   OffThreadScriptReceiverCallback,
                                   static_cast<void*>(aOffThreadReceiver))) {
             return NS_ERROR_OUT_OF_MEMORY;
         }
         // This reference will be consumed by the NotifyOffThreadScriptCompletedRunnable.
         NS_ADDREF(aOffThreadReceiver);
     } else {
-        JSScript* script = JS::Compile(cx, scope, options, aSrcBuf);
-        if (!script)
+        JS::Rooted<JSScript*> script(cx);
+        if (!JS::Compile(cx, scope, options, aSrcBuf, &script))
             return NS_ERROR_OUT_OF_MEMORY;
         Set(script);
     }
     return NS_OK;
 }
 
 nsresult
 nsXULPrototypeScript::Compile(const char16_t* aText,
--- a/dom/base/ScriptSettings.cpp
+++ b/dom/base/ScriptSettings.cpp
@@ -227,20 +227,26 @@ AutoJSAPI::AutoJSAPI()
 {
 }
 
 void
 AutoJSAPI::InitInternal(JSObject* aGlobal, JSContext* aCx, bool aIsMainThread)
 {
   mCx = aCx;
   if (aIsMainThread) {
+    // This Rooted<> is necessary only as long as AutoCxPusher::AutoCxPusher
+    // can GC, which is only possible because XPCJSContextStack::Push calls
+    // nsIPrincipal.Equals. Once that is removed, the Rooted<> will no longer
+    // be necessary.
+    JS::Rooted<JSObject*> global(JS_GetRuntime(aCx), aGlobal);
     mCxPusher.construct(mCx);
+    mAutoNullableCompartment.construct(mCx, global);
+  } else {
+    mAutoNullableCompartment.construct(mCx, aGlobal);
   }
-
-  mAutoNullableCompartment.construct(mCx, aGlobal);
 }
 
 AutoJSAPI::AutoJSAPI(nsIGlobalObject* aGlobalObject,
                      bool aIsMainThread,
                      JSContext* aCx)
 {
   MOZ_ASSERT(aGlobalObject);
   MOZ_ASSERT(aGlobalObject->GetGlobalJSObject(), "Must have a JS global");
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -2797,33 +2797,43 @@ NS_DOMReadStructuredClone(JSContext* cx,
     uint32_t width, height;
     JS::Rooted<JS::Value> dataArray(cx);
     if (!JS_ReadUint32Pair(reader, &width, &height) ||
         !JS_ReadTypedArray(reader, &dataArray)) {
       return nullptr;
     }
     MOZ_ASSERT(dataArray.isObject());
 
-    // Construct the ImageData.
-    nsRefPtr<ImageData> imageData = new ImageData(width, height,
-                                                  dataArray.toObject());
-    // Wrap it in a JS::Value.
-    return imageData->WrapObject(cx);
+    // Protect the result from a moving GC in ~nsRefPtr.
+    JS::Rooted<JSObject*> result(cx);
+    {
+      // Construct the ImageData.
+      nsRefPtr<ImageData> imageData = new ImageData(width, height,
+                                                    dataArray.toObject());
+      // Wrap it in a JS::Value.
+      result = imageData->WrapObject(cx);
+    }
+    return result;
   } else if (tag == SCTAG_DOM_WEBCRYPTO_KEY) {
     nsIGlobalObject *global = xpc::GetNativeForGlobal(JS::CurrentGlobalOrNull(cx));
     if (!global) {
       return nullptr;
     }
 
-    nsRefPtr<Key> key = new Key(global);
-    if (!key->ReadStructuredClone(reader)) {
-      return nullptr;
+    // Prevent the return value from being trashed by a GC during ~nsRefPtr.
+    JS::Rooted<JSObject*> result(cx);
+    {
+      nsRefPtr<Key> key = new Key(global);
+      if (!key->ReadStructuredClone(reader)) {
+        result = nullptr;
+      } else {
+        result = key->WrapObject(cx);
+      }
     }
-
-    return key->WrapObject(cx);
+    return result;
   }
 
   // Don't know what this is. Bail.
   xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
   return nullptr;
 }
 
 bool
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -152,22 +152,23 @@ nsJSUtils::CompileFunction(JSContext* aC
   MOZ_ASSERT_IF(ctx, ctx->IsContextInitialized());
 
   // Do the junk Gecko is supposed to do before calling into JSAPI.
   if (aTarget) {
     JS::ExposeObjectToActiveJS(aTarget);
   }
 
   // Compile.
-  JSFunction* fun = JS::CompileFunction(aCx, aTarget, aOptions,
-                                        PromiseFlatCString(aName).get(),
-                                        aArgCount, aArgArray,
-                                        PromiseFlatString(aBody).get(),
-                                        aBody.Length());
-  if (!fun) {
+  JS::Rooted<JSFunction*> fun(aCx);
+  if (!JS::CompileFunction(aCx, aTarget, aOptions,
+                           PromiseFlatCString(aName).get(),
+                           aArgCount, aArgArray,
+                           PromiseFlatString(aBody).get(),
+                           aBody.Length(), &fun))
+  {
     ReportPendingException(aCx);
     return NS_ERROR_FAILURE;
   }
 
   *aFunctionObject = JS_GetFunctionObject(fun);
   return NS_OK;
 }
 
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1593,27 +1593,29 @@ class CGConstructNavigatorObject(CGAbstr
                             "on navigator. See bug 856820.")
         return fill(
             """
             GlobalObject global(aCx, aObj);
             if (global.Failed()) {
               return nullptr;
             }
             ErrorResult rv;
-            nsRefPtr<mozilla::dom::${descriptorName}> result = ConstructNavigatorObjectHelper(aCx, global, rv);
-            rv.WouldReportJSException();
-            if (rv.Failed()) {
-              ThrowMethodFailedWithDetails(aCx, rv, "${descriptorName}", "navigatorConstructor");
-              return nullptr;
-            }
             JS::Rooted<JS::Value> v(aCx);
-            if (!WrapNewBindingObject(aCx, result, &v)) {
-              //XXX Assertion disabled for now, see bug 991271.
-              MOZ_ASSERT(true || JS_IsExceptionPending(aCx));
-              return nullptr;
+            {  // Scope to make sure |result| goes out of scope while |v| is rooted
+              nsRefPtr<mozilla::dom::${descriptorName}> result = ConstructNavigatorObjectHelper(aCx, global, rv);
+              rv.WouldReportJSException();
+              if (rv.Failed()) {
+                ThrowMethodFailedWithDetails(aCx, rv, "${descriptorName}", "navigatorConstructor");
+                return nullptr;
+              }
+              if (!WrapNewBindingObject(aCx, result, &v)) {
+                //XXX Assertion disabled for now, see bug 991271.
+                MOZ_ASSERT(true || JS_IsExceptionPending(aCx));
+                return nullptr;
+              }
             }
             return &v.toObject();
             """,
             descriptorName=self.descriptor.name)
 
 
 class CGClassConstructHookHolder(CGGeneric):
     def __init__(self, descriptor):
--- a/dom/bluetooth2/bluedroid/BluetoothA2dpManager.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothA2dpManager.cpp
@@ -32,19 +32,19 @@ USING_BLUETOOTH_NAMESPACE
 #define AVRC_ID_REWIND  0x48
 #define AVRC_ID_FAST_FOR 0x49
 #define AVRC_KEY_PRESS_STATE  1
 #define AVRC_KEY_RELEASE_STATE  0
 
 namespace {
   StaticRefPtr<BluetoothA2dpManager> sBluetoothA2dpManager;
   bool sInShutdown = false;
-  static const btav_interface_t* sBtA2dpInterface;
+  static BluetoothA2dpInterface* sBtA2dpInterface;
 #if ANDROID_VERSION > 17
-  static const btrc_interface_t* sBtAvrcpInterface;
+  static BluetoothAvrcpInterface* sBtAvrcpInterface;
 #endif
 } // anonymous namespace
 
 class SinkPropertyChangedHandler : public nsRunnable
 {
 public:
   SinkPropertyChangedHandler(const BluetoothSignal& aSignal)
     : mSignal(aSignal)
@@ -170,17 +170,17 @@ public:
     for (int i = 0; i < mNumAttr; i++) {
       nsAutoString attrText;
       attrs[i].attr_id = mPlayerAttrs[i];
       ConvertAttributeString(mPlayerAttrs[i], attrText);
       strcpy((char *)attrs[i].text, NS_ConvertUTF16toUTF8(attrText).get());
     }
 
     NS_ENSURE_TRUE(sBtAvrcpInterface, NS_OK);
-    sBtAvrcpInterface->get_element_attr_rsp(mNumAttr, attrs);
+    sBtAvrcpInterface->GetElementAttrRsp(mNumAttr, attrs);
 
     return NS_OK;
   }
 private:
   uint8_t mNumAttr;
   btrc_media_attr_t* mPlayerAttrs;
 };
 
@@ -503,34 +503,32 @@ static btrc_callbacks_t sBtAvrcpCallback
  * It is important to register a2dp callbacks before enable() gets called.
  * It is required to register a2dp callbacks before a2dp media task
  * starts up.
  */
 // static
 void
 BluetoothA2dpManager::InitA2dpInterface()
 {
-  const bt_interface_t* btInf = GetBluetoothInterface();
+  BluetoothInterface* btInf = BluetoothInterface::GetInstance();
   NS_ENSURE_TRUE_VOID(btInf);
 
-  sBtA2dpInterface = (btav_interface_t *)btInf->
-    get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID);
+  sBtA2dpInterface = btInf->GetBluetoothA2dpInterface();
   NS_ENSURE_TRUE_VOID(sBtA2dpInterface);
 
-  int ret = sBtA2dpInterface->init(&sBtA2dpCallbacks);
+  int ret = sBtA2dpInterface->Init(&sBtA2dpCallbacks);
   if (ret != BT_STATUS_SUCCESS) {
     BT_LOGR("Warning: failed to init a2dp module");
   }
 
 #if ANDROID_VERSION > 17
-  sBtAvrcpInterface = (btrc_interface_t *)btInf->
-    get_profile_interface(BT_PROFILE_AV_RC_ID);
+  sBtAvrcpInterface = btInf->GetBluetoothAvrcpInterface();
   NS_ENSURE_TRUE_VOID(sBtAvrcpInterface);
 
-  ret = sBtAvrcpInterface->init(&sBtAvrcpCallbacks);
+  ret = sBtAvrcpInterface->Init(&sBtAvrcpCallbacks);
   if (ret != BT_STATUS_SUCCESS) {
     BT_LOGR("Warning: failed to init avrcp module");
   }
 #endif
 }
 
 BluetoothA2dpManager::~BluetoothA2dpManager()
 {
@@ -605,22 +603,22 @@ BluetoothA2dpManager::Get()
 
 // static
 void
 BluetoothA2dpManager::DeinitA2dpInterface()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (sBtA2dpInterface) {
-    sBtA2dpInterface->cleanup();
+    sBtA2dpInterface->Cleanup();
     sBtA2dpInterface = nullptr;
   }
 #if ANDROID_VERSION > 17
   if (sBtAvrcpInterface) {
-    sBtAvrcpInterface->cleanup();
+    sBtAvrcpInterface->Cleanup();
     sBtAvrcpInterface = nullptr;
   }
 #endif
 }
 
 void
 BluetoothA2dpManager::HandleShutdown()
 {
@@ -656,17 +654,17 @@ BluetoothA2dpManager::Connect(const nsAS
     BT_LOGR("sBluetoothA2dpInterface is null");
     aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
     return;
   }
 
   bt_bdaddr_t remoteAddress;
   StringToBdAddressType(aDeviceAddress, &remoteAddress);
 
-  bt_status_t result = sBtA2dpInterface->connect(&remoteAddress);
+  bt_status_t result = sBtA2dpInterface->Connect(&remoteAddress);
   if (BT_STATUS_SUCCESS != result) {
     BT_LOGR("Failed to connect: %x", result);
     aController->NotifyCompletion(NS_LITERAL_STRING(ERR_CONNECTION_FAILED));
     return;
   }
 }
 
 void
@@ -698,17 +696,17 @@ BluetoothA2dpManager::Disconnect(Bluetoo
     BT_LOGR("sBluetoothA2dpInterface is null");
     aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
     return;
   }
 
   bt_bdaddr_t remoteAddress;
   StringToBdAddressType(mDeviceAddress, &remoteAddress);
 
-  bt_status_t result = sBtA2dpInterface->disconnect(&remoteAddress);
+  bt_status_t result = sBtA2dpInterface->Disconnect(&remoteAddress);
   if (BT_STATUS_SUCCESS != result) {
     BT_LOGR("Failed to disconnect: %x", result);
     aController->NotifyCompletion(NS_LITERAL_STRING(ERR_DISCONNECTION_FAILED));
     return;
   }
 }
 
 void
@@ -933,24 +931,24 @@ BluetoothA2dpManager::UpdateMetaData(con
     btrc_register_notification_t param;
     // convert to network big endian format
     // since track stores as uint8[8]
     // 56 = 8 * (BTRC_UID_SIZE -1)
     for (int i = 0; i < BTRC_UID_SIZE; ++i) {
       param.track[i] = (aMediaNumber >> (56 - 8 * i));
     }
     mTrackChangedNotifyType = BTRC_NOTIFICATION_TYPE_CHANGED;
-    sBtAvrcpInterface->register_notification_rsp(BTRC_EVT_TRACK_CHANGE,
-                                                 BTRC_NOTIFICATION_TYPE_CHANGED,
-                                                 &param);
+    sBtAvrcpInterface->RegisterNotificationRsp(BTRC_EVT_TRACK_CHANGE,
+                                               BTRC_NOTIFICATION_TYPE_CHANGED,
+                                               &param);
     if (mPlayPosChangedNotifyType == BTRC_NOTIFICATION_TYPE_INTERIM) {
       param.song_pos = mPosition;
       // EVENT_PLAYBACK_POS_CHANGED shall be notified if changed current track
       mPlayPosChangedNotifyType = BTRC_NOTIFICATION_TYPE_CHANGED;
-      sBtAvrcpInterface->register_notification_rsp(
+      sBtAvrcpInterface->RegisterNotificationRsp(
         BTRC_EVT_PLAY_POS_CHANGED,
         BTRC_NOTIFICATION_TYPE_CHANGED,
         &param);
     }
   }
 
   mTitle.Assign(aTitle);
   mArtist.Assign(aArtist);
@@ -970,37 +968,37 @@ BluetoothA2dpManager::UpdatePlayStatus(u
                                        uint32_t aPosition,
                                        ControlPlayStatus aPlayStatus)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
 #if ANDROID_VERSION > 17
   NS_ENSURE_TRUE_VOID(sBtAvrcpInterface);
   // always update playstatus first
-  sBtAvrcpInterface->get_play_status_rsp((btrc_play_status_t)aPlayStatus,
-                                         aDuration, aPosition);
+  sBtAvrcpInterface->GetPlayStatusRsp((btrc_play_status_t)aPlayStatus,
+                                      aDuration, aPosition);
   // when play status changed, send both play status and position
   if (mPlayStatus != aPlayStatus &&
       mPlayStatusChangedNotifyType == BTRC_NOTIFICATION_TYPE_INTERIM) {
     btrc_register_notification_t param;
     param.play_status = (btrc_play_status_t)aPlayStatus;
     mPlayStatusChangedNotifyType = BTRC_NOTIFICATION_TYPE_CHANGED;
-    sBtAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_STATUS_CHANGED,
-                                                 BTRC_NOTIFICATION_TYPE_CHANGED,
-                                                 &param);
+    sBtAvrcpInterface->RegisterNotificationRsp(BTRC_EVT_PLAY_STATUS_CHANGED,
+                                               BTRC_NOTIFICATION_TYPE_CHANGED,
+                                               &param);
   }
 
   if (mPosition != aPosition &&
       mPlayPosChangedNotifyType == BTRC_NOTIFICATION_TYPE_INTERIM) {
     btrc_register_notification_t param;
     param.song_pos = aPosition;
     mPlayPosChangedNotifyType = BTRC_NOTIFICATION_TYPE_CHANGED;
-    sBtAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_POS_CHANGED,
-                                                 BTRC_NOTIFICATION_TYPE_CHANGED,
-                                                 &param);
+    sBtAvrcpInterface->RegisterNotificationRsp(BTRC_EVT_PLAY_POS_CHANGED,
+                                               BTRC_NOTIFICATION_TYPE_CHANGED,
+                                               &param);
   }
 
   mDuration = aDuration;
   mPosition = aPosition;
   mPlayStatus = aPlayStatus;
 #endif
 }
 
@@ -1056,19 +1054,19 @@ BluetoothA2dpManager::UpdateRegisterNoti
         param.song_pos = 0xFFFFFFFF;
       }
       mPlaybackInterval = aParam;
       break;
     default:
       break;
   }
 
-  sBtAvrcpInterface->register_notification_rsp((btrc_event_id_t)aEventId,
-                                               BTRC_NOTIFICATION_TYPE_INTERIM,
-                                               &param);
+  sBtAvrcpInterface->RegisterNotificationRsp((btrc_event_id_t)aEventId,
+                                              BTRC_NOTIFICATION_TYPE_INTERIM,
+                                              &param);
 #endif
 }
 
 void
 BluetoothA2dpManager::GetAlbum(nsAString& aAlbum)
 {
   aAlbum.Assign(mAlbum);
 }
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth2/bluedroid/BluetoothInterface.cpp
@@ -0,0 +1,659 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BluetoothInterface.h"
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+template<class T>
+struct interface_traits
+{ };
+
+//
+// Socket Interface
+//
+
+template<>
+struct interface_traits<BluetoothSocketInterface>
+{
+  typedef const btsock_interface_t const_interface_type;
+
+  static const char* profile_id()
+  {
+    return BT_PROFILE_SOCKETS_ID;
+  }
+};
+
+bt_status_t
+BluetoothSocketInterface::Listen(btsock_type_t aType,
+                                 const char* aServiceName,
+                                 const uint8_t* aServiceUuid, int aChannel,
+                                 int& aSockFd, int aFlags)
+{
+  return mInterface->listen(aType, aServiceName, aServiceUuid, aChannel,
+                           &aSockFd, aFlags);
+}
+
+bt_status_t
+BluetoothSocketInterface::Connect(const bt_bdaddr_t* aBdAddr,
+                                  btsock_type_t aType, const uint8_t* aUuid,
+                                  int aChannel, int& aSockFd, int aFlags)
+{
+  return mInterface->connect(aBdAddr, aType, aUuid, aChannel, &aSockFd,
+                             aFlags);
+}
+
+BluetoothSocketInterface::BluetoothSocketInterface(
+  const btsock_interface_t* aInterface)
+: mInterface(aInterface)
+{
+  MOZ_ASSERT(mInterface);
+}
+
+BluetoothSocketInterface::~BluetoothSocketInterface()
+{ }
+
+//
+// Handsfree Interface
+//
+
+template<>
+struct interface_traits<BluetoothHandsfreeInterface>
+{
+  typedef const bthf_interface_t const_interface_type;
+
+  static const char* profile_id()
+  {
+    return BT_PROFILE_HANDSFREE_ID;
+  }
+};
+
+BluetoothHandsfreeInterface::BluetoothHandsfreeInterface(
+  const bthf_interface_t* aInterface)
+: mInterface(aInterface)
+{
+  MOZ_ASSERT(mInterface);
+}
+
+BluetoothHandsfreeInterface::~BluetoothHandsfreeInterface()
+{ }
+
+bt_status_t
+BluetoothHandsfreeInterface::Init(bthf_callbacks_t* aCallbacks)
+{
+  return mInterface->init(aCallbacks);
+}
+
+void
+BluetoothHandsfreeInterface::Cleanup()
+{
+  mInterface->cleanup();
+}
+
+/* Connect / Disconnect */
+
+bt_status_t
+BluetoothHandsfreeInterface::Connect(bt_bdaddr_t* aBdAddr)
+{
+  return mInterface->connect(aBdAddr);
+}
+
+bt_status_t
+BluetoothHandsfreeInterface::Disconnect(bt_bdaddr_t* aBdAddr)
+{
+  return mInterface->disconnect(aBdAddr);
+}
+
+bt_status_t
+BluetoothHandsfreeInterface::ConnectAudio(bt_bdaddr_t* aBdAddr)
+{
+  return mInterface->connect_audio(aBdAddr);
+}
+
+bt_status_t
+BluetoothHandsfreeInterface::DisconnectAudio(bt_bdaddr_t* aBdAddr)
+{
+  return mInterface->disconnect_audio(aBdAddr);
+}
+
+/* Voice Recognition */
+
+bt_status_t
+BluetoothHandsfreeInterface::StartVoiceRecognition()
+{
+  return mInterface->start_voice_recognition();
+}
+
+bt_status_t
+BluetoothHandsfreeInterface::StopVoiceRecognition()
+{
+  return mInterface->stop_voice_recognition();
+}
+
+/* Volume */
+
+bt_status_t
+BluetoothHandsfreeInterface::VolumeControl(bthf_volume_type_t aType,
+                                           int aVolume)
+{
+  return mInterface->volume_control(aType, aVolume);
+}
+
+/* Device status */
+
+bt_status_t
+BluetoothHandsfreeInterface::DeviceStatusNotification(
+  bthf_network_state_t aNtkState, bthf_service_type_t aSvcType, int aSignal,
+  int aBattChg)
+{
+  return mInterface->device_status_notification(aNtkState, aSvcType, aSignal,
+                                                aBattChg);
+}
+
+/* Responses */
+
+bt_status_t
+BluetoothHandsfreeInterface::CopsResponse(const char* aCops)
+{
+  return mInterface->cops_response(aCops);
+}
+
+bt_status_t
+BluetoothHandsfreeInterface::CindResponse(int aSvc, int aNumActive,
+                                          int aNumHeld,
+                                          bthf_call_state_t aCallSetupState,
+                                          int aSignal, int aRoam, int aBattChg)
+{
+  return mInterface->cind_response(aSvc, aNumActive, aNumHeld,
+                                   aCallSetupState, aSignal, aRoam,
+                                   aBattChg);
+}
+
+bt_status_t
+BluetoothHandsfreeInterface::FormattedAtResponse(const char* aRsp)
+{
+  return mInterface->formatted_at_response(aRsp);
+}
+
+bt_status_t
+BluetoothHandsfreeInterface::AtResponse(bthf_at_response_t aResponseCode,
+                                        int aErrorCode)
+{
+  return mInterface->at_response(aResponseCode, aErrorCode);
+}
+
+bt_status_t
+BluetoothHandsfreeInterface::ClccResponse(int aIndex,
+                                          bthf_call_direction_t aDir,
+                                          bthf_call_state_t aState,
+                                          bthf_call_mode_t aMode,
+                                          bthf_call_mpty_type_t aMpty,
+                                          const char* aNumber,
+                                          bthf_call_addrtype_t aType)
+{
+  return mInterface->clcc_response(aIndex, aDir, aState, aMode, aMpty,
+                                   aNumber, aType);
+}
+
+/* Phone State */
+
+bt_status_t
+BluetoothHandsfreeInterface::PhoneStateChange(int aNumActive, int aNumHeld,
+  bthf_call_state_t aCallSetupState, const char* aNumber,
+  bthf_call_addrtype_t aType)
+{
+  return mInterface->phone_state_change(aNumActive, aNumHeld, aCallSetupState,
+                                        aNumber, aType);
+}
+
+//
+// Bluetooth Advanced Audio Interface
+//
+
+template<>
+struct interface_traits<BluetoothA2dpInterface>
+{
+  typedef const btav_interface_t const_interface_type;
+
+  static const char* profile_id()
+  {
+    return BT_PROFILE_ADVANCED_AUDIO_ID;
+  }
+};
+
+BluetoothA2dpInterface::BluetoothA2dpInterface(
+  const btav_interface_t* aInterface)
+: mInterface(aInterface)
+{
+  MOZ_ASSERT(mInterface);
+}
+
+BluetoothA2dpInterface::~BluetoothA2dpInterface()
+{ }
+
+bt_status_t
+BluetoothA2dpInterface::Init(btav_callbacks_t* aCallbacks)
+{
+  return mInterface->init(aCallbacks);
+}
+
+void
+BluetoothA2dpInterface::Cleanup()
+{
+  mInterface->cleanup();
+}
+
+bt_status_t
+BluetoothA2dpInterface::Connect(bt_bdaddr_t *aBdAddr)
+{
+  return mInterface->connect(aBdAddr);
+}
+
+bt_status_t
+BluetoothA2dpInterface::Disconnect(bt_bdaddr_t *aBdAddr)
+{
+  return mInterface->disconnect(aBdAddr);
+}
+
+//
+// Bluetooth AVRCP Interface
+//
+
+#if ANDROID_VERSION >= 18
+template<>
+struct interface_traits<BluetoothAvrcpInterface>
+{
+  typedef const btrc_interface_t const_interface_type;
+
+  static const char* profile_id()
+  {
+    return BT_PROFILE_AV_RC_ID;
+  }
+};
+
+BluetoothAvrcpInterface::BluetoothAvrcpInterface(
+  const btrc_interface_t* aInterface)
+: mInterface(aInterface)
+{
+  MOZ_ASSERT(mInterface);
+}
+
+BluetoothAvrcpInterface::~BluetoothAvrcpInterface()
+{ }
+
+bt_status_t
+BluetoothAvrcpInterface::Init(btrc_callbacks_t* aCallbacks)
+{
+  return mInterface->init(aCallbacks);
+}
+
+void
+BluetoothAvrcpInterface::Cleanup()
+{
+  mInterface->cleanup();
+}
+
+bt_status_t
+BluetoothAvrcpInterface::GetPlayStatusRsp(btrc_play_status_t aPlayStatus,
+                                          uint32_t aSongLen, uint32_t aSongPos)
+{
+  return mInterface->get_play_status_rsp(aPlayStatus, aSongLen, aSongPos);
+}
+
+bt_status_t
+BluetoothAvrcpInterface::ListPlayerAppAttrRsp(int aNumAttr,
+                                              btrc_player_attr_t* aPAttrs)
+{
+  return mInterface->list_player_app_attr_rsp(aNumAttr, aPAttrs);
+}
+
+bt_status_t
+BluetoothAvrcpInterface::ListPlayerAppValueRsp(int aNumVal, uint8_t* aPVals)
+{
+  return mInterface->list_player_app_value_rsp(aNumVal, aPVals);
+}
+
+bt_status_t
+BluetoothAvrcpInterface::GetPlayerAppValueRsp(btrc_player_settings_t* aPVals)
+{
+  return mInterface->get_player_app_value_rsp(aPVals);
+}
+
+bt_status_t
+BluetoothAvrcpInterface::GetPlayerAppAttrTextRsp(int aNumAttr,
+  btrc_player_setting_text_t* aPAttrs)
+{
+  return mInterface->get_player_app_attr_text_rsp(aNumAttr, aPAttrs);
+}
+
+bt_status_t
+BluetoothAvrcpInterface::GetPlayerAppValueTextRsp(int aNumVal,
+  btrc_player_setting_text_t* aPVals)
+{
+  return mInterface->get_player_app_value_text_rsp(aNumVal, aPVals);
+}
+
+bt_status_t
+BluetoothAvrcpInterface::GetElementAttrRsp(uint8_t aNumAttr,
+                                           btrc_element_attr_val_t* aPAttrs)
+{
+  return mInterface->get_element_attr_rsp(aNumAttr, aPAttrs);
+}
+
+bt_status_t
+BluetoothAvrcpInterface::SetPlayerAppValueRsp(btrc_status_t aRspStatus)
+{
+  return mInterface->set_player_app_value_rsp(aRspStatus);
+}
+
+bt_status_t
+BluetoothAvrcpInterface::RegisterNotificationRsp(btrc_event_id_t aEventId,
+  btrc_notification_type_t aType, btrc_register_notification_t* aPParam)
+{
+  return mInterface->register_notification_rsp(aEventId, aType, aPParam);
+}
+
+bt_status_t
+BluetoothAvrcpInterface::SetVolume(uint8_t aVolume)
+{
+#if ANDROID_VERSION >= 19
+  return mInterface->set_volume(aVolume);
+#else
+  return BT_STATUS_UNSUPPORTED;
+#endif
+}
+#endif // ANDROID_VERSION >= 18
+
+//
+// Bluetooth Core Interface
+//
+
+/* returns the container structure of a variable; _t is the container's
+ * type, _v the name of the variable, and _m is _v's field within _t
+ */
+#define container(_t, _v, _m) \
+  ( (_t*)( ((const unsigned char*)(_v)) - offsetof(_t, _m) ) )
+
+BluetoothInterface*
+BluetoothInterface::GetInstance()
+{
+  static BluetoothInterface* sBluetoothInterface;
+
+  if (sBluetoothInterface) {
+    return sBluetoothInterface;
+  }
+
+  /* get driver module */
+
+  const hw_module_t* module;
+  int err = hw_get_module(BT_HARDWARE_MODULE_ID, &module);
+  if (err) {
+    BT_WARNING("hw_get_module failed: %s", strerror(err));
+    return nullptr;
+  }
+
+  /* get device */
+
+  hw_device_t* device;
+  err = module->methods->open(module, BT_HARDWARE_MODULE_ID, &device);
+  if (err) {
+    BT_WARNING("open failed: %s", strerror(err));
+    return nullptr;
+  }
+
+  const bluetooth_device_t* bt_device =
+    container(bluetooth_device_t, device, common);
+
+  /* get interface */
+
+  const bt_interface_t* bt_interface = bt_device->get_bluetooth_interface();
+  if (!bt_interface) {
+    BT_WARNING("get_bluetooth_interface failed");
+    goto err_get_bluetooth_interface;
+  }
+
+  if (bt_interface->size != sizeof(*bt_interface)) {
+    BT_WARNING("interface of incorrect size");
+    goto err_bt_interface_size;
+  }
+
+  sBluetoothInterface = new BluetoothInterface(bt_interface);
+
+  return sBluetoothInterface;
+
+err_bt_interface_size:
+err_get_bluetooth_interface:
+  err = device->close(device);
+  if (err) {
+    BT_WARNING("close failed: %s", strerror(err));
+  }
+  return nullptr;
+}
+
+BluetoothInterface::BluetoothInterface(const bt_interface_t* aInterface)
+: mInterface(aInterface)
+{
+  MOZ_ASSERT(mInterface);
+}
+
+BluetoothInterface::~BluetoothInterface()
+{ }
+
+int
+BluetoothInterface::Init(bt_callbacks_t* aCallbacks)
+{
+  return mInterface->init(aCallbacks);
+}
+
+void
+BluetoothInterface::Cleanup()
+{
+  mInterface->cleanup();
+}
+
+int
+BluetoothInterface::Enable()
+{
+  return mInterface->enable();
+}
+
+int
+BluetoothInterface::Disable()
+{
+  return mInterface->disable();
+}
+
+/* Adapter Properties */
+
+int
+BluetoothInterface::GetAdapterProperties()
+{
+  return mInterface->get_adapter_properties();
+}
+
+int
+BluetoothInterface::GetAdapterProperty(bt_property_type_t aType)
+{
+  return mInterface->get_adapter_property(aType);
+}
+
+int
+BluetoothInterface::SetAdapterProperty(const bt_property_t* aProperty)
+{
+  return mInterface->set_adapter_property(aProperty);
+}
+
+/* Remote Device Properties */
+
+int
+BluetoothInterface::GetRemoteDeviceProperties(bt_bdaddr_t *aRemoteAddr)
+{
+  return mInterface->get_remote_device_properties(aRemoteAddr);
+}
+
+int
+BluetoothInterface::GetRemoteDeviceProperty(bt_bdaddr_t* aRemoteAddr,
+                                            bt_property_type_t aType)
+{
+  return mInterface->get_remote_device_property(aRemoteAddr, aType);
+}
+
+int
+BluetoothInterface::SetRemoteDeviceProperty(bt_bdaddr_t* aRemoteAddr,
+                                            const bt_property_t* aProperty)
+{
+  return mInterface->set_remote_device_property(aRemoteAddr, aProperty);
+}
+
+/* Remote Services */
+
+int
+BluetoothInterface::GetRemoteServiceRecord(bt_bdaddr_t* aRemoteAddr,
+                                           bt_uuid_t* aUuid)
+{
+  return mInterface->get_remote_service_record(aRemoteAddr, aUuid);
+}
+
+int
+BluetoothInterface::GetRemoteServices(bt_bdaddr_t* aRemoteAddr)
+{
+  return mInterface->get_remote_services(aRemoteAddr);
+}
+
+/* Discovery */
+
+int
+BluetoothInterface::StartDiscovery()
+{
+  return mInterface->start_discovery();
+}
+
+int
+BluetoothInterface::CancelDiscovery()
+{
+  return mInterface->cancel_discovery();
+}
+
+/* Bonds */
+
+int
+BluetoothInterface::CreateBond(const bt_bdaddr_t* aBdAddr)
+{
+  return mInterface->create_bond(aBdAddr);
+}
+
+int
+BluetoothInterface::RemoveBond(const bt_bdaddr_t* aBdAddr)
+{
+  return mInterface->remove_bond(aBdAddr);
+}
+
+int
+BluetoothInterface::CancelBond(const bt_bdaddr_t* aBdAddr)
+{
+  return mInterface->cancel_bond(aBdAddr);
+}
+
+/* Authentication */
+
+int
+BluetoothInterface::PinReply(const bt_bdaddr_t* aBdAddr, uint8_t aAccept,
+                             uint8_t aPinLen, bt_pin_code_t* aPinCode)
+{
+  return mInterface->pin_reply(aBdAddr, aAccept, aPinLen, aPinCode);
+}
+
+int
+BluetoothInterface::SspReply(const bt_bdaddr_t* aBdAddr,
+                             bt_ssp_variant_t aVariant,
+                             uint8_t aAccept, uint32_t aPasskey)
+{
+  return mInterface->ssp_reply(aBdAddr, aVariant, aAccept, aPasskey);
+}
+
+/* DUT Mode */
+
+int
+BluetoothInterface::DutModeConfigure(uint8_t aEnable)
+{
+  return mInterface->dut_mode_configure(aEnable);
+}
+
+int
+BluetoothInterface::DutModeSend(uint16_t aOpcode, uint8_t* aBuf, uint8_t aLen)
+{
+  return mInterface->dut_mode_send(aOpcode, aBuf, aLen);
+}
+
+/* LE Mode */
+
+int
+BluetoothInterface::LeTestMode(uint16_t aOpcode, uint8_t* aBuf, uint8_t aLen)
+{
+  return mInterface->le_test_mode(aOpcode, aBuf, aLen);
+}
+
+/* Profile Interfaces */
+
+template <class T>
+T*
+BluetoothInterface::GetProfileInterface()
+{
+  static T* sBluetoothProfileInterface;
+
+  if (sBluetoothProfileInterface) {
+    return sBluetoothProfileInterface;
+  }
+
+  typename interface_traits<T>::const_interface_type* interface =
+    reinterpret_cast<typename interface_traits<T>::const_interface_type*>(
+      mInterface->get_profile_interface(interface_traits<T>::profile_id()));
+
+  if (!interface) {
+    BT_WARNING("Bluetooth profile '%s' is not supported",
+               interface_traits<T>::profile_id());
+    return nullptr;
+  }
+
+  if (interface->size != sizeof(*interface)) {
+    BT_WARNING("interface of incorrect size");
+    return nullptr;
+  }
+
+  sBluetoothProfileInterface = new T(interface);
+
+  return sBluetoothProfileInterface;
+}
+
+BluetoothSocketInterface*
+BluetoothInterface::GetBluetoothSocketInterface()
+{
+  return GetProfileInterface<BluetoothSocketInterface>();
+}
+
+BluetoothHandsfreeInterface*
+BluetoothInterface::GetBluetoothHandsfreeInterface()
+{
+  return GetProfileInterface<BluetoothHandsfreeInterface>();
+}
+
+BluetoothA2dpInterface*
+BluetoothInterface::GetBluetoothA2dpInterface()
+{
+  return GetProfileInterface<BluetoothA2dpInterface>();
+}
+
+BluetoothAvrcpInterface*
+BluetoothInterface::GetBluetoothAvrcpInterface()
+{
+#if ANDROID_VERSION >= 18
+  return GetProfileInterface<BluetoothAvrcpInterface>();
+#else
+  return nullptr;
+#endif
+}
+
+END_BLUETOOTH_NAMESPACE
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth2/bluedroid/BluetoothInterface.h
@@ -0,0 +1,263 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_bluetooth_bluedroid_bluetoothinterface_h__
+#define mozilla_dom_bluetooth_bluedroid_bluetoothinterface_h__
+
+#include <hardware/bluetooth.h>
+#include <hardware/bt_sock.h>
+#include <hardware/bt_hf.h>
+#include <hardware/bt_av.h>
+#if ANDROID_VERSION >= 18
+#include <hardware/bt_rc.h>
+#endif
+#include "BluetoothCommon.h"
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+class BluetoothInterface;
+
+//
+// Socket Interface
+//
+
+class BluetoothSocketInterface
+{
+public:
+  friend BluetoothInterface;
+
+  // Init and Cleanup is handled by BluetoothInterface
+
+  bt_status_t Listen(btsock_type_t aType,
+                     const char* aServiceName, const uint8_t* aServiceUuid,
+                     int aChannel, int& aSockFd, int aFlags);
+
+  bt_status_t Connect(const bt_bdaddr_t* aBdAddr, btsock_type_t aType,
+                      const uint8_t* aUuid, int aChannel, int& aSockFd,
+                      int aFlags);
+
+protected:
+  BluetoothSocketInterface(const btsock_interface_t* aInterface);
+  ~BluetoothSocketInterface();
+
+private:
+  const btsock_interface_t* mInterface;
+};
+
+//
+// Handsfree Interface
+//
+
+class BluetoothHandsfreeInterface
+{
+public:
+  friend BluetoothInterface;
+
+  bt_status_t Init(bthf_callbacks_t* aCallbacks);
+  void        Cleanup();
+
+  /* Connect / Disconnect */
+
+  bt_status_t Connect(bt_bdaddr_t* aBdAddr);
+  bt_status_t Disconnect(bt_bdaddr_t* aBdAddr);
+  bt_status_t ConnectAudio(bt_bdaddr_t* aBdAddr);
+  bt_status_t DisconnectAudio(bt_bdaddr_t* aBdAddr);
+
+  /* Voice Recognition */
+
+  bt_status_t StartVoiceRecognition();
+  bt_status_t StopVoiceRecognition();
+
+  /* Volume */
+
+  bt_status_t VolumeControl(bthf_volume_type_t aType, int aVolume);
+
+  /* Device status */
+
+  bt_status_t DeviceStatusNotification(bthf_network_state_t aNtkState,
+                                       bthf_service_type_t aSvcType,
+                                       int aSignal, int aBattChg);
+
+  /* Responses */
+
+  bt_status_t CopsResponse(const char* aCops);
+  bt_status_t CindResponse(int aSvc, int aNumActive, int aNumHeld,
+                           bthf_call_state_t aCallSetupState, int aSignal,
+                           int aRoam, int aBattChg);
+  bt_status_t FormattedAtResponse(const char* aRsp);
+  bt_status_t AtResponse(bthf_at_response_t aResponseCode, int aErrorCode);
+  bt_status_t ClccResponse(int aIndex, bthf_call_direction_t aDir,
+                           bthf_call_state_t aState, bthf_call_mode_t aMode,
+                           bthf_call_mpty_type_t aMpty, const char* aNumber,
+                           bthf_call_addrtype_t aType);
+
+  /* Phone State */
+
+  bt_status_t PhoneStateChange(int aNumActive, int aNumHeld,
+                               bthf_call_state_t aCallSetupState,
+                               const char* aNumber,
+                               bthf_call_addrtype_t aType);
+
+protected:
+  BluetoothHandsfreeInterface(const bthf_interface_t* aInterface);
+  ~BluetoothHandsfreeInterface();
+
+private:
+  const bthf_interface_t* mInterface;
+};
+
+//
+// Bluetooth Advanced Audio Interface
+//
+
+class BluetoothA2dpInterface
+{
+public:
+  friend BluetoothInterface;
+
+  bt_status_t Init(btav_callbacks_t *aCallbacks);
+  void        Cleanup();
+
+  bt_status_t Connect(bt_bdaddr_t *aBdAddr);
+  bt_status_t Disconnect(bt_bdaddr_t *aBdAddr);
+
+protected:
+  BluetoothA2dpInterface(const btav_interface_t* aInterface);
+  ~BluetoothA2dpInterface();
+
+private:
+  const btav_interface_t* mInterface;
+};
+
+//
+// Bluetooth AVRCP Interface
+//
+
+class BluetoothAvrcpInterface
+{
+#if ANDROID_VERSION >= 18
+public:
+  friend BluetoothInterface;
+
+  bt_status_t Init(btrc_callbacks_t* aCallbacks);
+  void        Cleanup();
+
+  bt_status_t GetPlayStatusRsp(btrc_play_status_t aPlayStatus,
+                               uint32_t aSongLen, uint32_t aSongPos);
+
+  bt_status_t ListPlayerAppAttrRsp(int aNumAttr, btrc_player_attr_t* aPAttrs);
+  bt_status_t ListPlayerAppValueRsp(int aNumVal, uint8_t* aPVals);
+
+  bt_status_t GetPlayerAppValueRsp(btrc_player_settings_t* aPVals);
+  bt_status_t GetPlayerAppAttrTextRsp(int aNumAttr,
+                                      btrc_player_setting_text_t* aPAttrs);
+  bt_status_t GetPlayerAppValueTextRsp(int aNumVal,
+                                       btrc_player_setting_text_t* aPVals);
+
+  bt_status_t GetElementAttrRsp(uint8_t aNumAttr,
+                                btrc_element_attr_val_t* aPAttrs);
+
+  bt_status_t SetPlayerAppValueRsp(btrc_status_t aRspStatus);
+
+  bt_status_t RegisterNotificationRsp(btrc_event_id_t aEventId,
+                                      btrc_notification_type_t aType,
+                                      btrc_register_notification_t* aPParam);
+
+  bt_status_t SetVolume(uint8_t aVolume);
+
+protected:
+  BluetoothAvrcpInterface(const btrc_interface_t* aInterface);
+  ~BluetoothAvrcpInterface();
+
+private:
+  const btrc_interface_t* mInterface;
+#endif
+};
+
+//
+// Bluetooth Core Interface
+//
+
+class BluetoothInterface
+{
+public:
+  static BluetoothInterface* GetInstance();
+
+  int  Init(bt_callbacks_t* aCallbacks);
+  void Cleanup();
+
+  int Enable();
+  int Disable();
+
+  /* Adapter Properties */
+
+  int GetAdapterProperties();
+  int GetAdapterProperty(bt_property_type_t aType);
+  int SetAdapterProperty(const bt_property_t* aProperty);
+
+  /* Remote Device Properties */
+
+  int GetRemoteDeviceProperties(bt_bdaddr_t *aRemoteAddr);
+  int GetRemoteDeviceProperty(bt_bdaddr_t* aRemoteAddr,
+                              bt_property_type_t aType);
+  int SetRemoteDeviceProperty(bt_bdaddr_t* aRemoteAddr,
+                              const bt_property_t* aProperty);
+
+  /* Remote Services */
+
+  int GetRemoteServiceRecord(bt_bdaddr_t* aRemoteAddr,
+                             bt_uuid_t* aUuid);
+  int GetRemoteServices(bt_bdaddr_t* aRemoteAddr);
+
+  /* Discovery */
+
+  int StartDiscovery();
+  int CancelDiscovery();
+
+  /* Bonds */
+
+  int CreateBond(const bt_bdaddr_t* aBdAddr);
+  int RemoveBond(const bt_bdaddr_t* aBdAddr);
+  int CancelBond(const bt_bdaddr_t* aBdAddr);
+
+  /* Authentication */
+
+  int PinReply(const bt_bdaddr_t* aBdAddr, uint8_t aAccept,
+               uint8_t aPinLen, bt_pin_code_t* aPinCode);
+
+  int SspReply(const bt_bdaddr_t* aBdAddr, bt_ssp_variant_t aVariant,
+               uint8_t aAccept, uint32_t aPasskey);
+
+  /* DUT Mode */
+
+  int DutModeConfigure(uint8_t aEnable);
+  int DutModeSend(uint16_t aOpcode, uint8_t* aBuf, uint8_t aLen);
+
+  /* LE Mode */
+
+  int LeTestMode(uint16_t aOpcode, uint8_t* aBuf, uint8_t aLen);
+
+  /* Profile Interfaces */
+
+  BluetoothSocketInterface* GetBluetoothSocketInterface();
+  BluetoothHandsfreeInterface* GetBluetoothHandsfreeInterface();
+  BluetoothA2dpInterface* GetBluetoothA2dpInterface();
+  BluetoothAvrcpInterface* GetBluetoothAvrcpInterface();
+
+protected:
+  BluetoothInterface(const bt_interface_t* aInterface);
+  ~BluetoothInterface();
+
+private:
+  template <class T>
+  T* GetProfileInterface();
+
+  const bt_interface_t* mInterface;
+};
+
+END_BLUETOOTH_NAMESPACE
+
+#endif
--- a/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothServiceBluedroid.cpp
@@ -13,20 +13,19 @@
 ** distributed under the License is distributed on an "AS IS" BASIS,
 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
 
 #include "BluetoothServiceBluedroid.h"
 
-#include <hardware/hardware.h>
-
 #include "BluetoothA2dpManager.h"
 #include "BluetoothHfpManager.h"
+#include "BluetoothInterface.h"
 #include "BluetoothOppManager.h"
 #include "BluetoothProfileController.h"
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothUtils.h"
 #include "BluetoothUuid.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/ipc/UnixSocket.h"
 #include "mozilla/StaticMutex.h"
@@ -49,17 +48,17 @@ using namespace mozilla::ipc;
 USING_BLUETOOTH_NAMESPACE
 
 // TODO: Non thread-safe static variables
 static nsString sAdapterBdAddress;
 static nsString sAdapterBdName;
 static InfallibleTArray<nsString> sAdapterBondedAddressArray;
 
 // Static variables below should only be used on *main thread*
-static const bt_interface_t* sBtInterface;
+static BluetoothInterface* sBtInterface;
 static nsTArray<nsRefPtr<BluetoothProfileController> > sControllerArray;
 static nsTArray<int> sRequestedDeviceCountArray;
 static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sChangeAdapterStateRunnableArray;
 static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sChangeDiscoveryRunnableArray;
 static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sSetPropertyRunnableArray;
 static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sGetDeviceRunnableArray;
 static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sBondingRunnableArray;
 static nsTArray<nsRefPtr<BluetoothReplyRunnable> > sUnbondingRunnableArray;
@@ -121,17 +120,17 @@ public:
     bt_scan_mode_t mode = BT_SCAN_MODE_CONNECTABLE;
     bt_property_t prop;
     prop.type = BT_PROPERTY_ADAPTER_SCAN_MODE;
     prop.val = (void*)&mode;
     prop.len = sizeof(mode);
 
     NS_ENSURE_TRUE(sBtInterface, NS_ERROR_FAILURE);
 
-    int ret = sBtInterface->set_adapter_property(&prop);
+    int ret = sBtInterface->SetAdapterProperty(&prop);
     if (ret != BT_STATUS_SUCCESS) {
       BT_LOGR("Fail to set: BT_SCAN_MODE_CONNECTABLE");
     }
 
     // Trigger BluetoothOppManager to listen
     BluetoothOppManager* opp = BluetoothOppManager::Get();
     if (!opp || !opp->Listen()) {
       BT_LOGR("Fail to start BluetoothOppManager listening");
@@ -171,17 +170,17 @@ public:
 
     BluetoothSignal signal(NS_LITERAL_STRING("PropertyChanged"),
                            NS_LITERAL_STRING(KEY_ADAPTER), props);
     bs->DistributeSignal(signal);
 
     // Cleanup bluetooth interfaces after BT state becomes BT_STATE_OFF.
     BluetoothHfpManager::DeinitHfpInterface();
     BluetoothA2dpManager::DeinitA2dpInterface();
-    sBtInterface->cleanup();
+    sBtInterface->Cleanup();
 
     return NS_OK;
   }
 };
 
 /**
  *  Static callback functions
  */
@@ -800,50 +799,38 @@ bt_callbacks_t sBluetoothCallbacks =
 };
 
 /**
  *  Static functions
  */
 static bool
 EnsureBluetoothHalLoad()
 {
-  hw_module_t* module;
-  hw_device_t* device;
-
-  int err = hw_get_module(BT_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
-  if (err != 0) {
-    BT_LOGR("Error: %s", strerror(err));
-    return false;
-  }
-  module->methods->open(module, BT_HARDWARE_MODULE_ID, &device);
-  bluetooth_device_t* btDevice = (bluetooth_device_t *)device;
-  NS_ENSURE_TRUE(btDevice, false);
-
-  sBtInterface = btDevice->get_bluetooth_interface();
+  sBtInterface = BluetoothInterface::GetInstance();
   NS_ENSURE_TRUE(sBtInterface, false);
 
   return true;
 }
 
 static bool
 EnableInternal()
 {
-  int ret = sBtInterface->init(&sBluetoothCallbacks);
+  int ret = sBtInterface->Init(&sBluetoothCallbacks);
   if (ret != BT_STATUS_SUCCESS) {
     BT_LOGR("Error while setting the callbacks");
     sBtInterface = nullptr;
     return false;
   }
 
   // Register all the bluedroid callbacks before enable() get called
   // It is required to register a2dp callbacks before a2dp media task starts up.
   // If any interface cannot be initialized, turn on bluetooth core anyway.
   BluetoothHfpManager::InitHfpInterface();
   BluetoothA2dpManager::InitA2dpInterface();
-  return sBtInterface->enable();
+  return sBtInterface->Enable();
 }
 
 static nsresult
 StartStopGonkBluetooth(bool aShouldEnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   NS_ENSURE_TRUE(sBtInterface, NS_ERROR_FAILURE);
@@ -856,17 +843,17 @@ StartStopGonkBluetooth(bool aShouldEnabl
     nsRefPtr<nsRunnable> runnable =
       new BluetoothService::ToggleBtAck(aShouldEnable);
     if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
       BT_WARNING("Failed to dispatch to main thread!");
     }
     return NS_OK;
   }
 
-  int ret = aShouldEnable ? EnableInternal() : sBtInterface->disable();
+  int ret = aShouldEnable ? EnableInternal() : sBtInterface->Disable();
   NS_ENSURE_TRUE(ret == BT_STATUS_SUCCESS, NS_ERROR_FAILURE);
 
   return NS_OK;
 }
 
 static void
 ReplyStatusError(BluetoothReplyRunnable* aBluetoothReplyRunnable,
                  int aStatusCode, const nsAString& aCustomMsg)
@@ -1030,17 +1017,17 @@ BluetoothServiceBluedroid::GetConnectedD
     return NS_OK;
   }
 
   for (int i = 0; i < requestedDeviceCount; i++) {
     // Retrieve all properties of devices
     bt_bdaddr_t addressType;
     StringToBdAddressType(deviceAddresses[i], &addressType);
 
-    int ret = sBtInterface->get_remote_device_properties(&addressType);
+    int ret = sBtInterface->GetRemoteDeviceProperties(&addressType);
     if (ret != BT_STATUS_SUCCESS) {
       DispatchBluetoothReply(aRunnable, BluetoothValue(true),
                              NS_LITERAL_STRING("GetConnectedDeviceFailed"));
       return NS_OK;
     }
   }
 
   sRequestedDeviceCountArray.AppendElement(requestedDeviceCount);
@@ -1063,17 +1050,17 @@ BluetoothServiceBluedroid::GetPairedDevi
     DispatchBluetoothReply(aRunnable, BluetoothValue(emptyArr), EmptyString());
     return NS_OK;
   }
 
   for (int i = 0; i < requestedDeviceCount; i++) {
     // Retrieve all properties of devices
     bt_bdaddr_t addressType;
     StringToBdAddressType(aDeviceAddress[i], &addressType);
-    int ret = sBtInterface->get_remote_device_properties(&addressType);
+    int ret = sBtInterface->GetRemoteDeviceProperties(&addressType);
     if (ret != BT_STATUS_SUCCESS) {
       DispatchBluetoothReply(aRunnable, BluetoothValue(true),
                              NS_LITERAL_STRING("GetPairedDeviceFailed"));
       return NS_OK;
     }
   }
 
   sRequestedDeviceCountArray.AppendElement(requestedDeviceCount);
@@ -1085,17 +1072,17 @@ BluetoothServiceBluedroid::GetPairedDevi
 nsresult
 BluetoothServiceBluedroid::StartDiscoveryInternal(
   BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
 
-  int ret = sBtInterface->start_discovery();
+  int ret = sBtInterface->StartDiscovery();
   if (ret != BT_STATUS_SUCCESS) {
     ReplyStatusError(aRunnable, ret, NS_LITERAL_STRING("StartDiscovery"));
 
     return NS_OK;
   }
 
   sChangeDiscoveryRunnableArray.AppendElement(aRunnable);
   return NS_OK;
@@ -1104,17 +1091,17 @@ BluetoothServiceBluedroid::StartDiscover
 nsresult
 BluetoothServiceBluedroid::StopDiscoveryInternal(
   BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
 
-  int ret = sBtInterface->cancel_discovery();
+  int ret = sBtInterface->CancelDiscovery();
   if (ret != BT_STATUS_SUCCESS) {
     ReplyStatusError(aRunnable, ret, NS_LITERAL_STRING("StopDiscovery"));
     return NS_OK;
   }
 
   sChangeDiscoveryRunnableArray.AppendElement(aRunnable);
 
   return NS_OK;
@@ -1169,17 +1156,17 @@ BluetoothServiceBluedroid::SetProperty(B
     BT_LOGR("SetProperty but the property cannot be recognized correctly.");
     DispatchBluetoothReply(aRunnable, BluetoothValue(),
                            NS_LITERAL_STRING(ERR_SET_PROPERTY));
     return NS_OK;
   }
 
   sSetPropertyRunnableArray.AppendElement(aRunnable);
 
-  int ret = sBtInterface->set_adapter_property(&prop);
+  int ret = sBtInterface->SetAdapterProperty(&prop);
   if (ret != BT_STATUS_SUCCESS) {
     ReplyStatusError(aRunnable, ret, NS_LITERAL_STRING(ERR_SET_PROPERTY));
     sSetPropertyRunnableArray.RemoveElement(aRunnable);
   }
 
   return NS_OK;
 }
 
@@ -1207,17 +1194,17 @@ BluetoothServiceBluedroid::CreatePairedD
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
 
   bt_bdaddr_t remoteAddress;
   StringToBdAddressType(aDeviceAddress, &remoteAddress);
 
-  int ret = sBtInterface->create_bond(&remoteAddress);
+  int ret = sBtInterface->CreateBond(&remoteAddress);
   if (ret != BT_STATUS_SUCCESS) {
     ReplyStatusError(aRunnable, ret, NS_LITERAL_STRING("CreatedPairedDevice"));
   } else {
     sBondingRunnableArray.AppendElement(aRunnable);
   }
 
   return NS_OK;
 }
@@ -1228,17 +1215,17 @@ BluetoothServiceBluedroid::RemoveDeviceI
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   ENSURE_BLUETOOTH_IS_READY(aRunnable, NS_OK);
 
   bt_bdaddr_t remoteAddress;
   StringToBdAddressType(aDeviceAddress, &remoteAddress);
 
-  int ret = sBtInterface->remove_bond(&remoteAddress);
+  int ret = sBtInterface->RemoveBond(&remoteAddress);
   if (ret != BT_STATUS_SUCCESS) {
     ReplyStatusError(aRunnable, ret,
                      NS_LITERAL_STRING("RemoveDevice"));
   } else {
     sUnbondingRunnableArray.AppendElement(aRunnable);
   }
 
   return NS_OK;
@@ -1251,17 +1238,17 @@ BluetoothServiceBluedroid::SetPinCodeInt
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   ENSURE_BLUETOOTH_IS_READY(aRunnable, false);
 
   bt_bdaddr_t remoteAddress;
   StringToBdAddressType(aDeviceAddress, &remoteAddress);
 
-  int ret = sBtInterface->pin_reply(
+  int ret = sBtInterface->PinReply(
       &remoteAddress, true, aPinCode.Length(),
       (bt_pin_code_t*)NS_ConvertUTF16toUTF8(aPinCode).get());
 
   if (ret != BT_STATUS_SUCCESS) {
     ReplyStatusError(aRunnable, ret, NS_LITERAL_STRING("SetPinCode"));
   } else {
     DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString());
   }
@@ -1284,18 +1271,18 @@ BluetoothServiceBluedroid::SetPairingCon
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   ENSURE_BLUETOOTH_IS_READY(aRunnable, false);
 
   bt_bdaddr_t remoteAddress;
   StringToBdAddressType(aDeviceAddress, &remoteAddress);
 
-  int ret = sBtInterface->ssp_reply(&remoteAddress, (bt_ssp_variant_t)0,
-                                    aConfirm, 0);
+  int ret = sBtInterface->SspReply(&remoteAddress, (bt_ssp_variant_t)0,
+                                   aConfirm, 0);
   if (ret != BT_STATUS_SUCCESS) {
     ReplyStatusError(aRunnable, ret,
                      NS_LITERAL_STRING("SetPairingConfirmation"));
   } else {
     DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString());
   }
 
   return true;
@@ -1335,22 +1322,16 @@ ConnectDisconnect(bool aConnect, const n
    * that other request is pushed into the quene and is popped out after the
    * first one is completed. See NextBluetoothProfileController() for details.
    */
   if (sControllerArray.Length() == 1) {
     sControllerArray[0]->StartSession();
   }
 }
 
-const bt_interface_t*
-BluetoothServiceBluedroid::GetBluetoothInterface()
-{
-  return sBtInterface;
-}
-
 void
 BluetoothServiceBluedroid::Connect(const nsAString& aDeviceAddress,
                                    uint32_t aCod,
                                    uint16_t aServiceUuid,
                                    BluetoothReplyRunnable* aRunnable)
 {
   ConnectDisconnect(true, aDeviceAddress, aRunnable, aServiceUuid, aCod);
 }
--- a/dom/bluetooth2/bluedroid/BluetoothSocket.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothSocket.cpp
@@ -2,21 +2,20 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BluetoothSocket.h"
 
 #include <fcntl.h>
-#include <hardware/bluetooth.h>
-#include <hardware/bt_sock.h>
 #include <sys/socket.h>
 
 #include "base/message_loop.h"
+#include "BluetoothInterface.h"
 #include "BluetoothSocketObserver.h"
 #include "BluetoothUtils.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/RefPtr.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 
 #define FIRST_SOCKET_INFO_MSG_LENGTH 4
@@ -25,31 +24,30 @@
 using namespace mozilla::ipc;
 USING_BLUETOOTH_NAMESPACE
 
 static const size_t MAX_READ_SIZE = 1 << 16;
 static const uint8_t UUID_OBEX_OBJECT_PUSH[] = {
   0x00, 0x00, 0x11, 0x05, 0x00, 0x00, 0x10, 0x00,
   0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB
 };
-static const btsock_interface_t* sBluetoothSocketInterface = nullptr;
+static BluetoothSocketInterface* sBluetoothSocketInterface;
 
 // helper functions
 static bool
 EnsureBluetoothSocketHalLoad()
 {
   if (sBluetoothSocketInterface) {
     return true;
   }
 
-  const bt_interface_t* btInf = GetBluetoothInterface();
+  BluetoothInterface* btInf = BluetoothInterface::GetInstance();
   NS_ENSURE_TRUE(btInf, false);
 
-  sBluetoothSocketInterface =
-    (btsock_interface_t *) btInf->get_profile_interface(BT_PROFILE_SOCKETS_ID);
+  sBluetoothSocketInterface = btInf->GetBluetoothSocketInterface();
   NS_ENSURE_TRUE(sBluetoothSocketInterface, false);
 
   return true;
 }
 
 static int16_t
 ReadInt16(const uint8_t* aData, size_t* aOffset)
 {
@@ -441,21 +439,21 @@ DroidSocketImpl::Connect()
   MOZ_ASSERT(sBluetoothSocketInterface);
 
   bt_bdaddr_t remoteBdAddress;
   StringToBdAddressType(mDeviceAddress, &remoteBdAddress);
 
   // TODO: uuid as argument
   int fd = -1;
   bt_status_t status =
-    sBluetoothSocketInterface->connect(&remoteBdAddress,
+    sBluetoothSocketInterface->Connect(&remoteBdAddress,
                                        BTSOCK_RFCOMM,
                                        UUID_OBEX_OBJECT_PUSH,
                                        mChannel,
-                                       &fd,
+                                       fd,
                                        (BTSOCK_FLAG_ENCRYPT * mEncrypt) |
                                        (BTSOCK_FLAG_AUTH * mAuth));
   NS_ENSURE_TRUE_VOID(status == BT_STATUS_SUCCESS);
   NS_ENSURE_TRUE_VOID(fd >= 0);
 
   int flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
   NS_ENSURE_TRUE_VOID(flags >= 0);
 
@@ -474,21 +472,21 @@ void
 DroidSocketImpl::Listen()
 {
   MOZ_ASSERT(sBluetoothSocketInterface);
 
   // TODO: uuid and service name as arguments
 
   int fd = -1;
   bt_status_t status =
-    sBluetoothSocketInterface->listen(BTSOCK_RFCOMM,
+    sBluetoothSocketInterface->Listen(BTSOCK_RFCOMM,
                                       "OBEX Object Push",
                                       UUID_OBEX_OBJECT_PUSH,
                                       mChannel,
-                                      &fd,
+                                      fd,
                                       (BTSOCK_FLAG_ENCRYPT * mEncrypt) |
                                       (BTSOCK_FLAG_AUTH * mAuth));
   NS_ENSURE_TRUE_VOID(status == BT_STATUS_SUCCESS);
   NS_ENSURE_TRUE_VOID(fd >= 0);
 
   int flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
   NS_ENSURE_TRUE_VOID(flags >= 0);
 
--- a/dom/bluetooth2/bluedroid/BluetoothUtils.cpp
+++ b/dom/bluetooth2/bluedroid/BluetoothUtils.cpp
@@ -18,22 +18,16 @@
 #include "nsIScriptContext.h"
 #include "nsISystemMessagesInternal.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsServiceManagerUtils.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
-const bt_interface_t*
-GetBluetoothInterface()
-{
-  return BluetoothServiceBluedroid::GetBluetoothInterface();
-}
-
 void
 StringToBdAddressType(const nsAString& aBdAddress,
                       bt_bdaddr_t *aRetBdAddressType)
 {
   NS_ConvertUTF16toUTF8 bdAddressUTF8(aBdAddress);
   const char* str = bdAddressUTF8.get();
 
   for (int i = 0; i < 6; i++) {
--- a/dom/bluetooth2/bluedroid/BluetoothUtils.h
+++ b/dom/bluetooth2/bluedroid/BluetoothUtils.h
@@ -13,19 +13,16 @@
 #include "js/TypeDecls.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothNamedValue;
 class BluetoothValue;
 class BluetoothReplyRunnable;
 
-const bt_interface_t*
-GetBluetoothInterface();
-
 void
 StringToBdAddressType(const nsAString& aBdAddress,
                       bt_bdaddr_t *aRetBdAddressType);
 
 void
 BdAddressTypeToString(bt_bdaddr_t* aBdAddressType,
                       nsAString& aRetBdAddress);
 
--- a/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.cpp
+++ b/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.cpp
@@ -47,17 +47,17 @@
   } while(0)
 
 using namespace mozilla;
 using namespace mozilla::ipc;
 USING_BLUETOOTH_NAMESPACE
 
 namespace {
   StaticRefPtr<BluetoothHfpManager> sBluetoothHfpManager;
-  static const bthf_interface_t* sBluetoothHfpInterface = nullptr;
+  static BluetoothHandsfreeInterface* sBluetoothHfpInterface = nullptr;
 
   bool sInShutdown = false;
 
   // Wait for 2 seconds for Dialer processing event 'BLDN'. '2' seconds is a
   // magic number. The mechanism should be revised once we can get call history.
   static int sWaitingForDialingInterval = 2000; //unit: ms
 
   // Wait 3.7 seconds until Dialer stops playing busy tone. '3' seconds is the
@@ -427,30 +427,30 @@ BluetoothHfpManager::Init()
 
   return true;
 }
 
 // static
 void
 BluetoothHfpManager::InitHfpInterface()
 {
-  const bt_interface_t* btInf = GetBluetoothInterface();
+  BluetoothInterface* btInf = BluetoothInterface::GetInstance();
   NS_ENSURE_TRUE_VOID(btInf);
 
   if (sBluetoothHfpInterface) {
-    sBluetoothHfpInterface->cleanup();
+    sBluetoothHfpInterface->Cleanup();
     sBluetoothHfpInterface = nullptr;
   }
 
-  bthf_interface_t *interface = (bthf_interface_t *)
-    btInf->get_profile_interface(BT_PROFILE_HANDSFREE_ID);
+  BluetoothHandsfreeInterface *interface =
+    btInf->GetBluetoothHandsfreeInterface();
   NS_ENSURE_TRUE_VOID(interface);
 
   NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
-    interface->init(&sBluetoothHfpCallbacks));
+    interface->Init(&sBluetoothHfpCallbacks));
   sBluetoothHfpInterface = interface;
 }
 
 BluetoothHfpManager::~BluetoothHfpManager()
 {
   if (!mListener->Listen(false)) {
     BT_WARNING("Failed to stop listening RIL");
   }
@@ -466,20 +466,18 @@ BluetoothHfpManager::~BluetoothHfpManage
 
   hal::UnregisterBatteryObserver(this);
 }
 
 // static
 void
 BluetoothHfpManager::DeinitHfpInterface()
 {
-  NS_ENSURE_TRUE_VOID(GetBluetoothInterface());
-
   if (sBluetoothHfpInterface) {
-    sBluetoothHfpInterface->cleanup();
+    sBluetoothHfpInterface->Cleanup();
     sBluetoothHfpInterface = nullptr;
   }
 }
 
 //static
 BluetoothHfpManager*
 BluetoothHfpManager::Get()
 {
@@ -672,33 +670,33 @@ BluetoothHfpManager::ProcessAtCnum()
 void
 BluetoothHfpManager::ProcessAtCind()
 {
   NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
 
   int numActive = GetNumberOfCalls(nsITelephonyService::CALL_STATE_CONNECTED);
   int numHeld = GetNumberOfCalls(nsITelephonyService::CALL_STATE_HELD);
 
-  bt_status_t status = sBluetoothHfpInterface->cind_response(
+  bt_status_t status = sBluetoothHfpInterface->CindResponse(
                           mService,
                           numActive,
                           numHeld,
                           ConvertToBthfCallState(GetCallSetupState()),
                           mSignal,
                           mRoam,
                           mBattChg);
   NS_ENSURE_TRUE_VOID(status == BT_STATUS_SUCCESS);
 }
 
 void
 BluetoothHfpManager::ProcessAtCops()
 {
   NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
   NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
-    sBluetoothHfpInterface->cops_response(
+    sBluetoothHfpInterface->CopsResponse(
       NS_ConvertUTF16toUTF8(mOperatorName).get()));
 }
 
 void
 BluetoothHfpManager::ProcessAtClcc()
 {
   uint32_t callNumbers = mCurrentCallArray.Length();
   uint32_t i;
@@ -718,17 +716,17 @@ BluetoothHfpManager::ProcessAtClcc()
 
 void
 BluetoothHfpManager::ProcessUnknownAt(char *aAtString)
 {
   BT_LOGR("[%s]", aAtString);
 
   NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
   NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
-    sBluetoothHfpInterface->at_response(BTHF_AT_RESPONSE_ERROR, 0));
+    sBluetoothHfpInterface->AtResponse(BTHF_AT_RESPONSE_ERROR, 0));
 }
 
 void
 BluetoothHfpManager::ProcessKeyPressed()
 {
   bool hasActiveCall =
     (FindFirstCall(nsITelephonyService::CALL_STATE_CONNECTED) > 0);
 
@@ -874,18 +872,18 @@ BluetoothHfpManager::HandleVolumeChanged
     mReceiveVgsFlag = false;
     return;
   }
 
   // Only send volume back when there's a connected headset
   if (IsConnected()) {
     NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
     NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
-      sBluetoothHfpInterface->volume_control(BTHF_VOLUME_TYPE_SPK,
-                                             mCurrentVgs));
+      sBluetoothHfpInterface->VolumeControl(BTHF_VOLUME_TYPE_SPK,
+                                            mCurrentVgs));
   }
 }
 
 void
 BluetoothHfpManager::HandleVoiceConnectionChanged(uint32_t aClientId)
 {
   nsCOMPtr<nsIMobileConnectionProvider> connection =
     do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
@@ -985,41 +983,41 @@ BluetoothHfpManager::SendCLCC(Call& aCal
                                                BTHF_CALL_STATE_ACTIVE;
   }
 
   if (callState == BTHF_CALL_STATE_INCOMING &&
       FindFirstCall(nsITelephonyService::CALL_STATE_CONNECTED)) {
     callState = BTHF_CALL_STATE_WAITING;
   }
 
-  bt_status_t status = sBluetoothHfpInterface->clcc_response(
+  bt_status_t status = sBluetoothHfpInterface->ClccResponse(
                           aIndex,
                           aCall.mDirection,
                           callState,
                           BTHF_CALL_TYPE_VOICE,
                           BTHF_CALL_MPTY_TYPE_SINGLE,
                           NS_ConvertUTF16toUTF8(aCall.mNumber).get(),
                           aCall.mType);
   NS_ENSURE_TRUE_VOID(status == BT_STATUS_SUCCESS);
 }
 
 void
 BluetoothHfpManager::SendLine(const char* aMessage)
 {
   NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
   NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
-    sBluetoothHfpInterface->formatted_at_response(aMessage));
+    sBluetoothHfpInterface->FormattedAtResponse(aMessage));
 }
 
 void
 BluetoothHfpManager::SendResponse(bthf_at_response_t aResponseCode)
 {
   NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
   NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
-    sBluetoothHfpInterface->at_response(aResponseCode, 0));
+    sBluetoothHfpInterface->AtResponse(aResponseCode, 0));
 }
 
 void
 BluetoothHfpManager::UpdatePhoneCIND(uint32_t aCallIndex)
 {
   NS_ENSURE_TRUE_VOID(sBluetoothHfpInterface);
 
   int numActive = GetNumberOfCalls(nsITelephonyService::CALL_STATE_CONNECTED);
@@ -1030,26 +1028,26 @@ BluetoothHfpManager::UpdatePhoneCIND(uin
     NS_ConvertUTF16toUTF8(mCurrentCallArray[aCallIndex].mNumber);
   bthf_call_addrtype_t type = mCurrentCallArray[aCallIndex].mType;
 
   BT_LOGR("[%d] state %d => BTHF: active[%d] held[%d] setupstate[%d]",
           aCallIndex, mCurrentCallArray[aCallIndex].mState,
           numActive, numHeld, callSetupState);
 
   NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
-    sBluetoothHfpInterface->phone_state_change(
+    sBluetoothHfpInterface->PhoneStateChange(
       numActive, numHeld, callSetupState, number.get(), type));
 }
 
 void
 BluetoothHfpManager::UpdateDeviceCIND()
 {
   if (sBluetoothHfpInterface) {
     NS_ENSURE_TRUE_VOID(BT_STATUS_SUCCESS ==
-      sBluetoothHfpInterface->device_status_notification(
+      sBluetoothHfpInterface->DeviceStatusNotification(
         (bthf_network_state_t) mService,
         (bthf_service_type_t) mRoam,
         mSignal,
         mBattChg));
   }
 }
 
 uint32_t
@@ -1291,31 +1289,31 @@ BluetoothHfpManager::ConnectSco()
 
   NS_ENSURE_TRUE(!sInShutdown, false);
   NS_ENSURE_TRUE(IsConnected() && !IsScoConnected(), false);
   NS_ENSURE_TRUE(sBluetoothHfpInterface, false);
 
   bt_bdaddr_t deviceBdAddress;
   StringToBdAddressType(mDeviceAddress, &deviceBdAddress);
   NS_ENSURE_TRUE(BT_STATUS_SUCCESS ==
-    sBluetoothHfpInterface->connect_audio(&deviceBdAddress), false);
+    sBluetoothHfpInterface->ConnectAudio(&deviceBdAddress), false);
 
   return true;
 }
 
 bool
 BluetoothHfpManager::DisconnectSco()
 {
   NS_ENSURE_TRUE(IsScoConnected(), false);
   NS_ENSURE_TRUE(sBluetoothHfpInterface, false);
 
   bt_bdaddr_t deviceBdAddress;
   StringToBdAddressType(mDeviceAddress, &deviceBdAddress);
   NS_ENSURE_TRUE(BT_STATUS_SUCCESS ==
-    sBluetoothHfpInterface->disconnect_audio(&deviceBdAddress), false);
+    sBluetoothHfpInterface->DisconnectAudio(&deviceBdAddress), false);
 
   return true;
 }
 
 bool
 BluetoothHfpManager::IsScoConnected()
 {
   return (mAudioState == BTHF_AUDIO_STATE_CONNECTED);
@@ -1343,17 +1341,17 @@ BluetoothHfpManager::Connect(const nsASt
     BT_LOGR("sBluetoothHfpInterface is null");
     aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
     return;
   }
 
   bt_bdaddr_t deviceBdAddress;
   StringToBdAddressType(aDeviceAddress, &deviceBdAddress);
 
-  bt_status_t result = sBluetoothHfpInterface->connect(&deviceBdAddress);
+  bt_status_t result = sBluetoothHfpInterface->Connect(&deviceBdAddress);
   if (BT_STATUS_SUCCESS != result) {
     BT_LOGR("Failed to connect: %x", result);
     aController->NotifyCompletion(NS_LITERAL_STRING(ERR_CONNECTION_FAILED));
     return;
   }
 
   mDeviceAddress = aDeviceAddress;
   mController = aController;
@@ -1369,17 +1367,17 @@ BluetoothHfpManager::Disconnect(Bluetoot
     BT_LOGR("sBluetoothHfpInterface is null");
     aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
     return;
   }
 
   bt_bdaddr_t deviceBdAddress;
   StringToBdAddressType(mDeviceAddress, &deviceBdAddress);
 
-  bt_status_t result = sBluetoothHfpInterface->disconnect(&deviceBdAddress);
+  bt_status_t result = sBluetoothHfpInterface->Disconnect(&deviceBdAddress);
   if (BT_STATUS_SUCCESS != result) {
     BT_LOGR("Failed to disconnect: %x", result);
     aController->NotifyCompletion(NS_LITERAL_STRING(ERR_DISCONNECTION_FAILED));
     return;
   }
 
   mController = aController;
 }
--- a/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.h
+++ b/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.h
@@ -2,19 +2,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_bluetooth_bluetoothhfpmanager_h__
 #define mozilla_dom_bluetooth_bluetoothhfpmanager_h__
 
-#include <hardware/bluetooth.h>
-#include <hardware/bt_hf.h>
-
+#include "BluetoothInterface.h"
 #include "BluetoothCommon.h"
 #include "BluetoothHfpManagerBase.h"
 #include "BluetoothRilListener.h"
 #include "BluetoothSocketObserver.h"
 #include "mozilla/ipc/UnixSocket.h"
 #include "mozilla/Hal.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
--- a/dom/bluetooth2/moz.build
+++ b/dom/bluetooth2/moz.build
@@ -38,16 +38,17 @@ if CONFIG['MOZ_B2G_BT']:
             ]
             LOCAL_INCLUDES += [
                 'bluez',
             ]
             DEFINES['MOZ_B2G_BT_BLUEZ'] = True
         elif CONFIG['MOZ_B2G_BT_BLUEDROID']:
             SOURCES += [
                 'bluedroid/BluetoothA2dpManager.cpp',
+                'bluedroid/BluetoothInterface.cpp',
                 'bluedroid/BluetoothOppManager.cpp',
                 'bluedroid/BluetoothServiceBluedroid.cpp',
                 'bluedroid/BluetoothSocket.cpp',
                 'bluedroid/BluetoothUtils.cpp',
             ]
             LOCAL_INCLUDES += [
                 'bluedroid',
             ]
--- a/dom/devicestorage/DeviceStorageRequestChild.cpp
+++ b/dom/devicestorage/DeviceStorageRequestChild.cpp
@@ -67,31 +67,31 @@ DeviceStorageRequestChild::
       break;
     }
 
     case DeviceStorageResponseValue::TSuccessResponse:
     {
       nsString fullPath;
       mDSFile->GetFullPath(fullPath);
       AutoJSContext cx;
-      JS::Rooted<JS::Value> result(cx,
-        StringToJsval(mRequest->GetOwner(), fullPath));
+      JS::Rooted<JS::Value> result(cx);
+      StringToJsval(mRequest->GetOwner(), fullPath, &result);
       mRequest->FireSuccess(result);
       break;
     }
 
     case DeviceStorageResponseValue::TFileDescriptorResponse:
     {
       FileDescriptorResponse r = aValue;
 
       nsString fullPath;
       mDSFile->GetFullPath(fullPath);
       AutoJSContext cx;
-      JS::Rooted<JS::Value> result(cx,
-        StringToJsval(mRequest->GetOwner(), fullPath));
+      JS::Rooted<JS::Value> result(cx);
+      StringToJsval(mRequest->GetOwner(), fullPath, &result);
 
       mDSFileDescriptor->mDSFile = mDSFile;
       mDSFileDescriptor->mFileDescriptor = r.fileDescriptor();
       mRequest->FireSuccess(result);
       break;
     }
 
     case DeviceStorageResponseValue::TBlobResponse:
@@ -125,58 +125,58 @@ DeviceStorageRequestChild::
       mRequest->FireSuccess(result);
       break;
     }
 
     case DeviceStorageResponseValue::TAvailableStorageResponse:
     {
       AvailableStorageResponse r = aValue;
       AutoJSContext cx;
-      JS::Rooted<JS::Value> result(
-        cx, StringToJsval(mRequest->GetOwner(), r.mountState()));
+      JS::Rooted<JS::Value> result(cx);
+      StringToJsval(mRequest->GetOwner(), r.mountState(), &result);
       mRequest->FireSuccess(result);
       break;
     }
 
     case DeviceStorageResponseValue::TStorageStatusResponse:
     {
       StorageStatusResponse r = aValue;
       AutoJSContext cx;
-      JS::Rooted<JS::Value> result(
-        cx, StringToJsval(mRequest->GetOwner(), r.storageStatus()));
+      JS::Rooted<JS::Value> result(cx);
+      StringToJsval(mRequest->GetOwner(), r.storageStatus(), &result);
       mRequest->FireSuccess(result);
       break;
     }
 
     case DeviceStorageResponseValue::TFormatStorageResponse:
     {
       FormatStorageResponse r = aValue;
       AutoJSContext cx;
-      JS::Rooted<JS::Value> result(
-        cx, StringToJsval(mRequest->GetOwner(), r.mountState()));
+      JS::Rooted<JS::Value> result(cx);
+      StringToJsval(mRequest->GetOwner(), r.mountState(), &result);
       mRequest->FireSuccess(result);
       break;
     }
 
     case DeviceStorageResponseValue::TMountStorageResponse:
     {
       MountStorageResponse r = aValue;
       AutoJSContext cx;
-      JS::Rooted<JS::Value> result(
-        cx, StringToJsval(mRequest->GetOwner(), r.storageStatus()));
+      JS::Rooted<JS::Value> result(cx);
+      StringToJsval(mRequest->GetOwner(), r.storageStatus(), &result);
       mRequest->FireSuccess(result);
       break;
     }
 
     case DeviceStorageResponseValue::TUnmountStorageResponse:
     {
       UnmountStorageResponse r = aValue;
       AutoJSContext cx;
-      JS::Rooted<JS::Value> result(
-        cx, StringToJsval(mRequest->GetOwner(), r.storageStatus()));
+      JS::Rooted<JS::Value> result(cx);
+      StringToJsval(mRequest->GetOwner(), r.storageStatus(), &result);
       mRequest->FireSuccess(result);
       break;
     }
 
     case DeviceStorageResponseValue::TEnumerationResponse:
     {
       EnumerationResponse r = aValue;
       nsDOMDeviceStorageCursor* cursor
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -1664,31 +1664,37 @@ nsDOMDeviceStorage::SetRootDirectoryForT
   mStorageName = aStorageName;
 }
 
 JS::Value
 InterfaceToJsval(nsPIDOMWindow* aWindow,
                  nsISupports* aObject,
                  const nsIID* aIID)
 {
-  AutoJSContext cx;
   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aWindow);
   if (!sgo) {
-    return JSVAL_NULL;
-  }
-
-  JS::Rooted<JSObject*> scopeObj(cx, sgo->GetGlobalJSObject());
-  NS_ENSURE_TRUE(scopeObj, JSVAL_NULL);
-  JSAutoCompartment ac(cx, scopeObj);
-
-
-  JS::Rooted<JS::Value> someJsVal(cx);
-  nsresult rv = nsContentUtils::WrapNative(cx, aObject, aIID, &someJsVal);
+    return JS::NullValue();
+  }
+
+  JSObject *unrootedScopeObj = sgo->GetGlobalJSObject();
+  NS_ENSURE_TRUE(unrootedScopeObj, JS::NullValue());
+  JSRuntime *runtime = JS_GetObjectRuntime(unrootedScopeObj);
+  JS::Rooted<JS::Value> someJsVal(runtime);
+  nsresult rv;
+
+  { // Protect someJsVal from moving GC in ~JSAutoCompartment
+    AutoJSContext cx;
+
+    JS::Rooted<JSObject*> scopeObj(cx, unrootedScopeObj);
+    JSAutoCompartment ac(cx, scopeObj);
+
+    rv = nsContentUtils::WrapNative(cx, aObject, aIID, &someJsVal);
+  }
   if (NS_FAILED(rv)) {
-    return JSVAL_NULL;
+    return JS::NullValue();
   }
 
   return someJsVal;
 }
 
 JS::Value
 nsIFileToJsval(nsPIDOMWindow* aWindow, DeviceStorageFile* aFile)
 {
@@ -1715,33 +1721,34 @@ nsIFileToJsval(nsPIDOMWindow* aWindow, D
 
   nsCOMPtr<nsIDOMBlob> blob = new DOMFile(
     new DOMFileImplFile(fullPath, aFile->mMimeType,
                         aFile->mLength, aFile->mFile,
                         aFile->mLastModifiedDate));
   return InterfaceToJsval(aWindow, blob, &NS_GET_IID(nsIDOMBlob));
 }
 
-JS::Value StringToJsval(nsPIDOMWindow* aWindow, nsAString& aString)
+bool
+StringToJsval(nsPIDOMWindow* aWindow, nsAString& aString,
+              JS::MutableHandle<JS::Value> result)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aWindow);
 
   AutoJSAPI jsapi;
   if (NS_WARN_IF(!jsapi.Init(aWindow))) {
-    return JSVAL_NULL;
+    return false;
   }
   JSContext* cx = jsapi.cx();
 
-  JS::Rooted<JS::Value> result(cx);
-  if (!xpc::StringToJsval(cx, aString, &result)) {
-    return JSVAL_NULL;
-  }
-
-  return result;
+  if (!xpc::StringToJsval(cx, aString, result)) {
+    return false;
+  }
+
+  return true;
 }
 
 class DeviceStorageCursorRequest MOZ_FINAL
   : public nsIContentPermissionRequest
   , public PCOMContentPermissionRequestChild
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
@@ -2130,18 +2137,18 @@ public:
     MOZ_ASSERT(NS_IsMainThread());
 
     nsString state = NS_LITERAL_STRING("unavailable");
     if (mFile) {
       mFile->GetStatus(state);
     }
 
     AutoJSContext cx;
-    JS::Rooted<JS::Value> result(cx,
-                                 StringToJsval(mRequest->GetOwner(), state));
+    JS::Rooted<JS::Value> result(cx);
+    StringToJsval(mRequest->GetOwner(), state, &result);
     mRequest->FireSuccess(result);
     mRequest = nullptr;
     return NS_OK;
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsRefPtr<DOMRequest> mRequest;
@@ -2164,18 +2171,18 @@ public:
     MOZ_ASSERT(NS_IsMainThread());
 
     nsString state = NS_LITERAL_STRING("undefined");
     if (mFile) {
       mFile->GetStorageStatus(state);
     }
 
     AutoJSContext cx;
-    JS::Rooted<JS::Value> result(cx,
-                                 StringToJsval(mRequest->GetOwner(), state));
+    JS::Rooted<JS::Value> result(cx);
+    StringToJsval(mRequest->GetOwner(), state, &result);
     mRequest->FireSuccess(result);
     mRequest = nullptr;
     return NS_OK;
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsRefPtr<DOMRequest> mRequest;
@@ -2198,18 +2205,18 @@ public:
     MOZ_ASSERT(NS_IsMainThread());
 
     nsString state = NS_LITERAL_STRING("unavailable");
     if (mFile) {
       mFile->DoFormat(state);
     }
 
     AutoJSContext cx;
-    JS::Rooted<JS::Value> result(cx,
-                                 StringToJsval(mRequest->GetOwner(), state));
+    JS::Rooted<JS::Value> result(cx);
+    StringToJsval(mRequest->GetOwner(), state, &result);
     mRequest->FireSuccess(result);
     mRequest = nullptr;
     return NS_OK;
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsRefPtr<DOMRequest> mRequest;
@@ -2232,18 +2239,18 @@ public:
     MOZ_ASSERT(NS_IsMainThread());
 
     nsString state = NS_LITERAL_STRING("unavailable");
     if (mFile) {
       mFile->DoMount(state);
     }
 
     AutoJSContext cx;
-    JS::Rooted<JS::Value> result(cx,
-                                 StringToJsval(mRequest->GetOwner(), state));
+    JS::Rooted<JS::Value> result(cx);
+    StringToJsval(mRequest->GetOwner(), state, &result);
     mRequest->FireSuccess(result);
     mRequest = nullptr;
     return NS_OK;
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsRefPtr<DOMRequest> mRequest;
@@ -2266,18 +2273,18 @@ public:
     MOZ_ASSERT(NS_IsMainThread());
 
     nsString state = NS_LITERAL_STRING("unavailable");
     if (mFile) {
       mFile->DoUnmount(state);
     }
 
     AutoJSContext cx;
-    JS::Rooted<JS::Value> result(cx,
-                                 StringToJsval(mRequest->GetOwner(), state));
+    JS::Rooted<JS::Value> result(cx);
+    StringToJsval(mRequest->GetOwner(), state, &result);
     mRequest->FireSuccess(result);
     mRequest = nullptr;
     return NS_OK;
   }
 
 private:
   nsRefPtr<DeviceStorageFile> mFile;
   nsRefPtr<DOMRequest> mRequest;
@@ -2318,17 +2325,17 @@ public:
 
     AutoJSContext cx;
     JS::Rooted<JS::Value> result(cx, JSVAL_NULL);
     nsPIDOMWindow* window = mRequest->GetOwner();
 
     if (mFile) {
       result = nsIFileToJsval(window, mFile);
     } else if (mPath.Length()) {
-      result = StringToJsval(window, mPath);
+      StringToJsval(window, mPath, &result);
     }
     else {
       result = JS_NumberValue(double(mValue));
     }
 
     mRequest->FireSuccess(result);
     mRequest = nullptr;
     return NS_OK;
--- a/dom/devicestorage/nsDeviceStorage.h
+++ b/dom/devicestorage/nsDeviceStorage.h
@@ -223,18 +223,19 @@ public:
 private:
   ~nsDOMDeviceStorageCursor();
 
   nsRefPtr<DeviceStorageFile> mFile;
   nsCOMPtr<nsIPrincipal> mPrincipal;
 };
 
 //helpers
-JS::Value
-StringToJsval(nsPIDOMWindow* aWindow, nsAString& aString);
+bool
+StringToJsval(nsPIDOMWindow* aWindow, nsAString& aString,
+              JS::MutableHandle<JS::Value> result);
 
 JS::Value
 nsIFileToJsval(nsPIDOMWindow* aWindow, DeviceStorageFile* aFile);
 
 JS::Value
 InterfaceToJsval(nsPIDOMWindow* aWindow, nsISupports* aObject, const nsIID* aIID);
 
 #endif
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -7,16 +7,17 @@
 #include "IDBRequest.h"
 
 #include "nsIScriptContext.h"
 
 #include "mozilla/ContentEvents.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/dom/ErrorEventBinding.h"
 #include "mozilla/dom/IDBOpenDBRequestBinding.h"
+#include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/UnionTypes.h"
 #include "nsComponentManagerUtils.h"
 #include "nsDOMClassInfoID.h"
 #include "nsDOMJSUtils.h"
 #include "nsContentUtils.h"
 #include "nsCxPusher.h"
 #include "nsJSUtils.h"
 #include "nsPIDOMWindow.h"
@@ -183,28 +184,34 @@ IDBRequest::NotifyHelperCompleted(Helper
 
   // See if our window is still valid. If not then we're going to pretend that
   // we never completed.
   if (NS_FAILED(CheckInnerWindowCorrectness())) {
     return NS_OK;
   }
 
   // Otherwise we need to get the result from the helper.
-  AutoPushJSContext cx(GetJSContext());
-  if (!cx) {
-    IDB_WARNING("Failed to get safe JSContext!");
-    rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
-    SetError(rv);
-    return rv;
+  AutoJSAPI jsapi;
+  Maybe<JSAutoCompartment> ac;
+  if (GetScriptOwner()) {
+    // If we have a script owner we want the SafeJSContext and then to enter
+    // the script owner's compartment.
+    jsapi.Init();
+    ac.construct(jsapi.cx(), GetScriptOwner());
+  } else {
+    // Otherwise our owner is a window and we use that to initialize.
+    if (!jsapi.InitWithLegacyErrorReporting(GetOwner())) {
+      IDB_WARNING("Failed to initialise AutoJSAPI!");
+      rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+      SetError(rv);
+      return rv;
+    }
   }
+  JSContext* cx = jsapi.cx();
 
-  JS::Rooted<JSObject*> global(cx, IDBWrapperCache::GetParentObject());
-  NS_ASSERTION(global, "This should never be null!");
-
-  JSAutoCompartment ac(cx, global);
   AssertIsRooted();
 
   JS::Rooted<JS::Value> value(cx);
   rv = aHelper->GetSuccessResult(cx, &value);
   if (NS_FAILED(rv)) {
     NS_WARNING("GetSuccessResult failed!");
   }
 
@@ -258,38 +265,16 @@ nsresult
 IDBRequest::GetErrorCode() const
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(mHaveResultOrErrorCode, "Don't call me yet!");
   return mErrorCode;
 }
 #endif
 
-JSContext*
-IDBRequest::GetJSContext()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
-
-  JSContext* cx;
-
-  if (GetScriptOwner()) {
-    return nsContentUtils::GetSafeJSContext();
-  }
-
-  nsresult rv;
-  nsIScriptContext* sc = GetContextForEventHandlers(&rv);
-  NS_ENSURE_SUCCESS(rv, nullptr);
-  NS_ENSURE_TRUE(sc, nullptr);
-
-  cx = sc->GetNativeContext();
-  NS_ASSERTION(cx, "Failed to get a context!");
-
-  return cx;
-}
-
 void
 IDBRequest::CaptureCaller()
 {
   AutoJSContext cx;
 
   const char* filename = nullptr;
   uint32_t lineNo = 0;
   if (!nsJSUtils::GetCallingLocation(cx, &filename, &lineNo)) {
--- a/dom/indexedDB/IDBRequest.h
+++ b/dom/indexedDB/IDBRequest.h
@@ -81,18 +81,16 @@ public:
 #else
   {
     return mErrorCode;
   }
 #endif
 
   DOMError* GetError(ErrorResult& aRv);
 
-  JSContext* GetJSContext();
-
   void
   SetActor(IndexedDBRequestParentBase* aActorParent)
   {
     NS_ASSERTION(!aActorParent || !mActorParent,
                  "Shouldn't have more than one!");
     mActorParent = aActorParent;
   }
 
--- a/dom/indexedDB/IDBWrapperCache.h
+++ b/dom/indexedDB/IDBWrapperCache.h
@@ -21,29 +21,16 @@ public:
                                                    DOMEventTargetHelper)
 
   JSObject* GetScriptOwner() const
   {
     return mScriptOwner;
   }
   void SetScriptOwner(JSObject* aScriptOwner);
 
-  JSObject* GetParentObject()
-  {
-    if (mScriptOwner) {
-      return mScriptOwner;
-    }
-
-    // Do what nsEventTargetSH::PreCreate does.
-    nsCOMPtr<nsIScriptGlobalObject> parent;
-    DOMEventTargetHelper::GetParentObject(getter_AddRefs(parent));
-
-    return parent ? parent->GetGlobalJSObject() : nullptr;
-  }
-
 #ifdef DEBUG
   void AssertIsRooted() const;
 #else
   inline void AssertIsRooted() const
   {
   }
 #endif
 
--- a/dom/mobilemessage/src/MobileMessageCallback.cpp
+++ b/dom/mobilemessage/src/MobileMessageCallback.cpp
@@ -7,16 +7,17 @@
 #include "nsContentUtils.h"
 #include "nsCxPusher.h"
 #include "nsIDOMMozSmsMessage.h"
 #include "nsIDOMMozMmsMessage.h"
 #include "nsIDOMSmsSegmentInfo.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsPIDOMWindow.h"
 #include "MmsMessage.h"
+#include "mozilla/dom/ScriptSettings.h"
 #include "jsapi.h"
 #include "xpcpublic.h"
 #include "nsServiceManagerUtils.h"
 #include "nsTArrayHelpers.h"
 #include "DOMMobileMessageError.h"
 
 namespace mozilla {
 namespace dom {
@@ -98,31 +99,24 @@ MobileMessageCallback::NotifySuccess(JS:
 
   mDOMRequest->FireSuccess(aResult);
   return NS_OK;
 }
 
 nsresult
 MobileMessageCallback::NotifySuccess(nsISupports *aMessage, bool aAsync)
 {
-  nsresult rv;
-  nsIScriptContext* scriptContext = mDOMRequest->GetContextForEventHandlers(&rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_ENSURE_TRUE(scriptContext, NS_ERROR_FAILURE);
-
-  AutoPushJSContext cx(scriptContext->GetNativeContext());
-  NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
-
-  JS::Rooted<JSObject*> global(cx, scriptContext->GetWindowProxy());
-  NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
-
-  JSAutoCompartment ac(cx, global);
+  AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.Init(mDOMRequest->GetOwner()))) {
+    return NS_ERROR_FAILURE;
+  }
+  JSContext* cx = jsapi.cx();
 
   JS::Rooted<JS::Value> wrappedMessage(cx);
-  rv = nsContentUtils::WrapNative(cx, aMessage, &wrappedMessage);
+  nsresult rv = nsContentUtils::WrapNative(cx, aMessage, &wrappedMessage);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NotifySuccess(wrappedMessage, aAsync);
 }
 
 nsresult
 MobileMessageCallback::NotifyError(int32_t aError, DOMError *aDetailedError, bool aAsync)
 {
@@ -191,26 +185,23 @@ NS_IMETHODIMP
 MobileMessageCallback::NotifyMessageDeleted(bool *aDeleted, uint32_t aSize)
 {
   if (aSize == 1) {
     AutoJSContext cx;
     JS::Rooted<JS::Value> val(cx, aDeleted[0] ? JSVAL_TRUE : JSVAL_FALSE);
     return NotifySuccess(val);
   }
 
-  nsresult rv;
-  nsIScriptContext* sc = mDOMRequest->GetContextForEventHandlers(&rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_ENSURE_TRUE(sc, NS_ERROR_FAILURE);
+  AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.Init(mDOMRequest->GetOwner()))) {
+    return NS_ERROR_FAILURE;
+  }
+  JSContext* cx = jsapi.cx();
 
-  AutoPushJSContext cx(sc->GetNativeContext());
-  NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
-
-  JS::Rooted<JSObject*> deleteArrayObj(cx,
-                                       JS_NewArrayObject(cx, aSize));
+  JS::Rooted<JSObject*> deleteArrayObj(cx, JS_NewArrayObject(cx, aSize));
   for (uint32_t i = 0; i < aSize; i++) {
     JS_SetElement(cx, deleteArrayObj, i, aDeleted[i]);
   }
 
   JS::Rooted<JS::Value> deleteArrayVal(cx, JS::ObjectValue(*deleteArrayObj));
   return NotifySuccess(deleteArrayVal);
 }
 
--- a/dom/mobilemessage/src/MobileMessageCursorCallback.cpp
+++ b/dom/mobilemessage/src/MobileMessageCursorCallback.cpp
@@ -1,19 +1,19 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MobileMessageCursorCallback.h"
+#include "mozilla/dom/ScriptSettings.h"
 #include "nsIDOMDOMRequest.h"
 #include "nsIDOMMozSmsMessage.h"
 #include "nsIMobileMessageCallback.h"
 #include "DOMCursor.h"
-#include "nsCxPusher.h"
 #include "nsServiceManagerUtils.h"      // for do_GetService
 
 namespace mozilla {
 namespace dom {
 namespace mobilemessage {
 
 NS_IMPL_CYCLE_COLLECTION(MobileMessageCursorCallback, mDOMCursor)
 
@@ -54,31 +54,24 @@ MobileMessageCursorCallback::NotifyCurso
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MobileMessageCursorCallback::NotifyCursorResult(nsISupports* aResult)
 {
   MOZ_ASSERT(mDOMCursor);
 
-  nsresult rv;
-  nsIScriptContext* scriptContext = mDOMCursor->GetContextForEventHandlers(&rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_ENSURE_TRUE(scriptContext, NS_ERROR_FAILURE);
-
-  AutoPushJSContext cx(scriptContext->GetNativeContext());
-  NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
-
-  JS::Rooted<JSObject*> global(cx, scriptContext->GetWindowProxy());
-  NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
-
-  JSAutoCompartment ac(cx, global);
+  AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.Init(mDOMCursor->GetOwner()))) {
+    return NS_ERROR_FAILURE;
+  }
+  JSContext* cx = jsapi.cx();
 
   JS::Rooted<JS::Value> wrappedResult(cx);
-  rv = nsContentUtils::WrapNative(cx, aResult, &wrappedResult);
+  nsresult rv = nsContentUtils::WrapNative(cx, aResult, &wrappedResult);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mDOMCursor->FireSuccess(wrappedResult);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MobileMessageCursorCallback::NotifyCursorDone()
--- a/dom/nfc/tests/marionette/test_nfc_tag.js
+++ b/dom/nfc/tests/marionette/test_nfc_tag.js
@@ -4,16 +4,17 @@
 MARIONETTE_TIMEOUT = 30000;
 MARIONETTE_HEAD_JS = "head.js";
 
 let url = "http://www.mozilla.org";
 
 // TODO : Get this from emulator console command.
 const T1T_RE_INDEX = 2;
 const T2T_RE_INDEX = 3;
+const T3T_RE_INDEX = 4;
 
 function testUrlTagDiscover(re) {
   log("Running \'testUrlTagDiscover\'");
   // TODO : Make flag value readable.
   let flag = 0xd0;
   let tnf = NDEF.TNF_WELL_KNOWN;
   let type = "U";
   let payload = url;
@@ -42,15 +43,20 @@ function testUrlTagDiscover(re) {
 function testUrlT1TDiscover() {
   testUrlTagDiscover(T1T_RE_INDEX);
 }
 
 function testUrlT2TDiscover() {
   testUrlTagDiscover(T2T_RE_INDEX);
 }
 
+function testUrlT3TDiscover() {
+  testUrlTagDiscover(T3T_RE_INDEX);
+}
+
 let tests = [
   testUrlT1TDiscover,
-  testUrlT2TDiscover
+  testUrlT2TDiscover,
+  testUrlT3TDiscover
 ];
 
 SpecialPowers.pushPermissions(
   [{'type': 'nfc-manager', 'allow': true, context: document}], runTests);
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -36,16 +36,17 @@
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptContext.h"
 #include "nsIUnicodeNormalizer.h"
 #include "nsDOMJSUtils.h"
 #include "nsIPrincipal.h"
 #include "nsWildCard.h"
 #include "nsContentUtils.h"
 #include "nsCxPusher.h"
+#include "mozilla/dom/ScriptSettings.h"
 
 #include "nsIXPConnect.h"
 
 #include "nsIObserverService.h"
 #include <prinrval.h>
 
 #ifdef MOZ_WIDGET_COCOA
 #include <Carbon/Carbon.h>
@@ -638,37 +639,16 @@ GetDocumentFromNPP(NPP npp)
   NS_ENSURE_TRUE(owner, nullptr);
 
   nsCOMPtr<nsIDocument> doc;
   owner->GetDocument(getter_AddRefs(doc));
 
   return doc;
 }
 
-static JSContext *
-GetJSContextFromDoc(nsIDocument *doc)
-{
-  nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(doc->GetWindow());
-  NS_ENSURE_TRUE(sgo, nullptr);
-
-  nsIScriptContext *scx = sgo->GetContext();
-  NS_ENSURE_TRUE(scx, nullptr);
-
-  return scx->GetNativeContext();
-}
-
-static JSContext *
-GetJSContextFromNPP(NPP npp)
-{
-  nsIDocument *doc = GetDocumentFromNPP(npp);
-  NS_ENSURE_TRUE(doc, nullptr);
-
-  return GetJSContextFromDoc(doc);
-}
-
 static already_AddRefed<nsIChannel>
 GetChannelFromNPP(NPP npp)
 {
   nsCOMPtr<nsIDocument> doc = GetDocumentFromNPP(npp);
   if (!doc)
     return nullptr;
   nsCOMPtr<nsPIDOMWindow> domwindow = doc->GetWindow();
   nsCOMPtr<nsIChannel> channel;
@@ -1234,19 +1214,26 @@ NPObject*
     return nullptr;
 
   nsCOMPtr<nsIDOMElement> element;
   inst->GetDOMElement(getter_AddRefs(element));
 
   if (!element)
     return nullptr;
 
-  AutoPushJSContext cx(GetJSContextFromNPP(npp));
-  NS_ENSURE_TRUE(cx, nullptr);
-  JSAutoRequest ar(cx); // Unnecessary once bug 868130 lands.
+  nsIDocument *doc = GetDocumentFromNPP(npp);
+  if (NS_WARN_IF(!doc)) {
+    return nullptr;
+  }
+
+  dom::AutoJSAPI jsapi;
+  if (NS_WARN_IF(!jsapi.Init(doc->GetInnerWindow()))) {
+    return nullptr;
+  }
+  JSContext* cx = jsapi.cx();
 
   nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID()));
   NS_ENSURE_TRUE(xpc, nullptr);
 
   nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
   xpc->WrapNative(cx, ::JS::CurrentGlobalOrNull(cx), element,
                   NS_GET_IID(nsIDOMElement),
                   getter_AddRefs(holder));
--- a/dom/webidl/TextTrack.webidl
+++ b/dom/webidl/TextTrack.webidl
@@ -34,16 +34,16 @@ interface TextTrack : EventTarget {
 
   readonly attribute TextTrackCueList? cues;
   readonly attribute TextTrackCueList? activeCues;
 
   void addCue(VTTCue cue);
   [Throws]
   void removeCue(VTTCue cue);
 
-           attribute EventHandler oncuechange;
+           //(Not implemented)attribute EventHandler oncuechange;
 };
 
 // Mozilla Extensions
 partial interface TextTrack {
   [ChromeOnly]
   readonly attribute TextTrackList? textTrackList;
 };
--- a/dom/workers/Navigator.cpp
+++ b/dom/workers/Navigator.cpp
@@ -112,44 +112,47 @@ GetDataStoresStructuredCloneCallbacksRea
 
   // Read the holder from the buffer, which points to the data store.
   nsMainThreadPtrHolder<DataStore>* dataStoreholder;
   if (!JS_ReadBytes(aReader, &dataStoreholder, sizeof(dataStoreholder))) {
     MOZ_ASSERT(false, "cannot read bytes for dataStoreholder!");
     return nullptr;
   }
 
-  nsRefPtr<WorkerDataStore> workerStore =
-    new WorkerDataStore(workerPrivate->GlobalScope());
-  nsMainThreadPtrHandle<DataStore> backingStore = dataStoreholder;
+  // Protect workerStoreObj from moving GC during ~nsRefPtr.
+  JS::Rooted<JSObject*> workerStoreObj(aCx, nullptr);
+  {
+    nsRefPtr<WorkerDataStore> workerStore =
+      new WorkerDataStore(workerPrivate->GlobalScope());
+    nsMainThreadPtrHandle<DataStore> backingStore = dataStoreholder;
 
-  // When we're on the worker thread, prepare a DataStoreChangeEventProxy.
-  nsRefPtr<DataStoreChangeEventProxy> eventProxy =
-    new DataStoreChangeEventProxy(workerPrivate, workerStore);
+    // When we're on the worker thread, prepare a DataStoreChangeEventProxy.
+    nsRefPtr<DataStoreChangeEventProxy> eventProxy =
+      new DataStoreChangeEventProxy(workerPrivate, workerStore);
 
-  // Add the DataStoreChangeEventProxy as an event listener on the main thread.
-  nsRefPtr<DataStoreAddEventListenerRunnable> runnable =
-    new DataStoreAddEventListenerRunnable(workerPrivate,
-                                          backingStore,
-                                          eventProxy);
-  runnable->Dispatch(aCx);
+    // Add the DataStoreChangeEventProxy as an event listener on the main thread.
+    nsRefPtr<DataStoreAddEventListenerRunnable> runnable =
+      new DataStoreAddEventListenerRunnable(workerPrivate,
+                                            backingStore,
+                                            eventProxy);
+    runnable->Dispatch(aCx);
 
-  // Point WorkerDataStore to DataStore.
-  workerStore->SetBackingDataStore(backingStore);
+    // Point WorkerDataStore to DataStore.
+    workerStore->SetBackingDataStore(backingStore);
 
-  JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
-  if (!global) {
-    MOZ_ASSERT(false, "cannot get global!");
-    return nullptr;
-  }
-
-  JS::Rooted<JSObject*> workerStoreObj(aCx, workerStore->WrapObject(aCx));
-  if (!JS_WrapObject(aCx, &workerStoreObj)) {
-    MOZ_ASSERT(false, "cannot wrap object for workerStoreObj!");
-    return nullptr;
+    JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
+    if (!global) {
+      MOZ_ASSERT(false, "cannot get global!");
+    } else {
+      workerStoreObj = workerStore->WrapObject(aCx);
+      if (!JS_WrapObject(aCx, &workerStoreObj)) {
+        MOZ_ASSERT(false, "cannot wrap object for workerStoreObj!");
+        workerStoreObj = nullptr;
+      }
+    }
   }
 
   return workerStoreObj;
 }
 
 static bool
 GetDataStoresStructuredCloneCallbacksWrite(JSContext* aCx,
                                            JSStructuredCloneWriter* aWriter,
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -295,16 +295,18 @@ LogErrorToConsole(const nsAString& aMess
 }
 
 struct WorkerStructuredCloneCallbacks
 {
   static JSObject*
   Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
        uint32_t aData, void* aClosure)
   {
+    JS::Rooted<JSObject*> result(aCx);
+
     // See if object is a nsIDOMFile pointer.
     if (aTag == DOMWORKER_SCTAG_FILE) {
       MOZ_ASSERT(!aData);
 
       DOMFileImpl* fileImpl;
       if (JS_ReadBytes(aReader, &fileImpl, sizeof(fileImpl))) {
         MOZ_ASSERT(fileImpl);
 
@@ -313,19 +315,22 @@ struct WorkerStructuredCloneCallbacks
           // File should not be mutable.
           bool isMutable;
           NS_ASSERTION(NS_SUCCEEDED(fileImpl->GetMutable(&isMutable)) &&
                        !isMutable,
                        "Only immutable file should be passed to worker");
         }
 #endif
 
-        nsRefPtr<DOMFile> file = new DOMFile(fileImpl);
-        JSObject* jsFile = file::CreateFile(aCx, file);
-        return jsFile;
+        {
+          // New scope to protect |result| from a moving GC during ~nsRefPtr.
+          nsRefPtr<DOMFile> file = new DOMFile(fileImpl);
+          result = file::CreateFile(aCx, file);
+        }
+        return result;
       }
     }
     // See if object is a nsIDOMBlob pointer.
     else if (aTag == DOMWORKER_SCTAG_BLOB) {
       MOZ_ASSERT(!aData);
 
       DOMFileImpl* blobImpl;
       if (JS_ReadBytes(aReader, &blobImpl, sizeof(blobImpl))) {
@@ -336,40 +341,46 @@ struct WorkerStructuredCloneCallbacks
           // Blob should not be mutable.
           bool isMutable;
           NS_ASSERTION(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)) &&
                        !isMutable,
                        "Only immutable blob should be passed to worker");
         }
 #endif
 
-        nsRefPtr<DOMFile> blob = new DOMFile(blobImpl);
-        JSObject* jsBlob = file::CreateBlob(aCx, blob);
-        return jsBlob;
+        {
+          // New scope to protect |result| from a moving GC during ~nsRefPtr.
+          nsRefPtr<DOMFile> blob = new DOMFile(blobImpl);
+          result = file::CreateBlob(aCx, blob);
+        }
+        return result;
       }
     }
     // See if the object is an ImageData.
     else if (aTag == SCTAG_DOM_IMAGEDATA) {
       MOZ_ASSERT(!aData);
 
       // Read the information out of the stream.
       uint32_t width, height;
       JS::Rooted<JS::Value> dataArray(aCx);
       if (!JS_ReadUint32Pair(aReader, &width, &height) ||
           !JS_ReadTypedArray(aReader, &dataArray))
       {
         return nullptr;
       }
       MOZ_ASSERT(dataArray.isObject());
 
-      // Construct the ImageData.
-      nsRefPtr<ImageData> imageData = new ImageData(width, height,
-                                                    dataArray.toObject());
-      // Wrap it in a JS::Value.
-      return imageData->WrapObject(aCx);
+      {
+        // Construct the ImageData.
+        nsRefPtr<ImageData> imageData = new ImageData(width, height,
+                                                      dataArray.toObject());
+        // Wrap it in a JS::Value, protected from a moving GC during ~nsRefPtr.
+        result = imageData->WrapObject(aCx);
+      }
+      return result;
     }
 
     Error(aCx, 0);
     return nullptr;
   }
 
   static bool
   Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -447,19 +447,23 @@ DrawTargetCairo::GetType() const
     case CAIRO_SURFACE_TYPE_WIN32:
     case CAIRO_SURFACE_TYPE_BEOS:
     case CAIRO_SURFACE_TYPE_OS2:
     case CAIRO_SURFACE_TYPE_QUARTZ_IMAGE:
     case CAIRO_SURFACE_TYPE_SCRIPT:
     case CAIRO_SURFACE_TYPE_RECORDING:
     case CAIRO_SURFACE_TYPE_DRM:
     case CAIRO_SURFACE_TYPE_SUBSURFACE:
+#ifdef CAIRO_HAS_D2D_SURFACE
     case CAIRO_SURFACE_TYPE_D2D:
+#endif
     case CAIRO_SURFACE_TYPE_TEE: // included to silence warning about unhandled enum value
       return DrawTargetType::SOFTWARE_RASTER;
+    default:
+      MOZ_CRASH("Unsupported cairo surface type");
     }
   }
   MOZ_ASSERT(false, "Could not determine DrawTargetType for DrawTargetCairo");
   return DrawTargetType::SOFTWARE_RASTER;
 }
 
 IntSize
 DrawTargetCairo::GetSize()
--- a/ipc/testshell/XPCShellEnvironment.cpp
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -160,19 +160,20 @@ Load(JSContext *cx,
         if (!file) {
             JS_ReportError(cx, "cannot open file '%s' for reading", filename.ptr());
             return false;
         }
         Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
         JS::CompileOptions options(cx);
         options.setUTF8(true)
                .setFileAndLine(filename.ptr(), 1);
-        JS::Rooted<JSScript*> script(cx, JS::Compile(cx, obj, options, file));
+        JS::Rooted<JSScript*> script(cx);
+        bool ok = JS::Compile(cx, obj, options, file, &script);
         fclose(file);
-        if (!script)
+        if (!ok)
             return false;
 
         if (!JS_ExecuteScript(cx, obj, script)) {
             return false;
         }
     }
     args.rval().setUndefined();
     return true;
@@ -329,18 +330,18 @@ XPCShellEnvironment::ProcessFile(JSConte
         ungetc(ch, file);
 
         JSAutoRequest ar(cx);
         JSAutoCompartment ac(cx, obj);
 
         JS::CompileOptions options(cx);
         options.setUTF8(true)
                .setFileAndLine(filename, 1);
-        JS::Rooted<JSScript*> script(cx, JS::Compile(cx, obj, options, file));
-        if (script)
+        JS::Rooted<JSScript*> script(cx);
+        if (JS::Compile(cx, obj, options, file, &script))
             (void)JS_ExecuteScript(cx, obj, script, &result);
 
         return;
     }
 
     /* It's an interactive filehandle; drop into read-eval-print loop. */
     lineno = 1;
     hitEOF = false;
@@ -366,19 +367,18 @@ XPCShellEnvironment::ProcessFile(JSConte
             bufp += strlen(bufp);
             lineno++;
         } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, strlen(buffer)));
 
         /* Clear any pending exception from previous failed compiles.  */
         JS_ClearPendingException(cx);
         JS::CompileOptions options(cx);
         options.setFileAndLine("typein", startline);
-        JS::Rooted<JSScript*> script(cx,
-                                     JS_CompileScript(cx, obj, buffer, strlen(buffer), options));
-        if (script) {
+        JS::Rooted<JSScript*> script(cx);
+        if (JS_CompileScript(cx, obj, buffer, strlen(buffer), options, &script)) {
             JSErrorReporter older;
 
             ok = JS_ExecuteScript(cx, obj, script, &result);
             if (ok && result != JSVAL_VOID) {
                 /* Suppress error reports from JS::ToString(). */
                 older = JS_SetErrorReporter(cx, nullptr);
                 str = JS::ToString(cx, result);
                 JSAutoByteString bytes;
@@ -575,19 +575,20 @@ XPCShellEnvironment::EvaluateString(cons
                                     nsString* aResult)
 {
   AutoSafeJSContext cx;
   JS::Rooted<JSObject*> global(cx, GetGlobalObject());
   JSAutoCompartment ac(cx, global);
 
   JS::CompileOptions options(cx);
   options.setFileAndLine("typein", 0);
-  JS::Rooted<JSScript*> script(cx, JS_CompileUCScript(cx, global, aString.get(),
-                                                      aString.Length(), options));
-  if (!script) {
+  JS::Rooted<JSScript*> script(cx);
+  if (!JS_CompileUCScript(cx, global, aString.get(), aString.Length(), options,
+                          &script))
+  {
      return false;
   }
 
   if (aResult) {
       aResult->Truncate();
   }
 
   JS::Rooted<JS::Value> result(cx);
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -16,17 +16,16 @@
 #include "vm/GlobalObject.h"
 
 #include "vm/Interpreter-inl.h"
 
 using namespace js;
 
 using mozilla::AddToHash;
 using mozilla::HashString;
-using mozilla::Range;
 using mozilla::RangedPtr;
 
 using JS::AutoCheckCannotGC;
 
 // We should be able to assert this for *any* fp->scopeChain().
 static void
 AssertInnerizedScopeChain(JSContext *cx, JSObject &scopeobj)
 {
@@ -146,17 +145,17 @@ class EvalScriptGuard
 enum EvalJSONResult {
     EvalJSON_Failure,
     EvalJSON_Success,
     EvalJSON_NotJSON
 };
 
 template <typename CharT>
 static bool
-EvalStringMightBeJSON(const Range<const CharT> chars)
+EvalStringMightBeJSON(const mozilla::Range<const CharT> chars)
 {
     // If the eval string starts with '(' or '[' and ends with ')' or ']', it may be JSON.
     // Try the JSON parser first because it's much faster.  If the eval string
     // isn't JSON, JSON parsing will probably fail quickly, so little time
     // will be lost.
     size_t length = chars.length();
     if (length > 2 &&
         ((chars[0] == '[' && chars[length - 1] == ']') ||
@@ -181,25 +180,25 @@ EvalStringMightBeJSON(const Range<const 
 
         return true;
     }
     return false;
 }
 
 template <typename CharT>
 static EvalJSONResult
-ParseEvalStringAsJSON(JSContext *cx, const Range<const CharT> chars, MutableHandleValue rval)
+ParseEvalStringAsJSON(JSContext *cx, const mozilla::Range<const CharT> chars, MutableHandleValue rval)
 {
     size_t len = chars.length();
     MOZ_ASSERT((chars[0] == '(' && chars[len - 1] == ')') ||
                (chars[0] == '[' && chars[len - 1] == ']'));
 
     auto jsonChars = (chars[0] == '[')
                      ? chars
-                     : Range<const CharT>(chars.start().get() + 1U, len - 2);
+                     : mozilla::Range<const CharT>(chars.start().get() + 1U, len - 2);
 
     JSONParser<CharT> parser(cx, jsonChars, JSONParserBase::NoError);
     if (!parser.parse(rval))
         return EvalJSON_Failure;
 
     return rval.isUndefined() ? EvalJSON_NotJSON : EvalJSON_Success;
 }
 
@@ -307,17 +306,17 @@ EvalKernel(JSContext *cx, const CallArgs
         return ejr == EvalJSON_Success;
 
     EvalScriptGuard esg(cx);
 
     if (evalType == DIRECT_EVAL && caller.isNonEvalFunctionFrame())
         esg.lookupInEvalCache(flatStr, callerScript, pc);
 
     if (!esg.foundScript()) {
-        JSScript *maybeScript;
+        RootedScript maybeScript(cx);
         unsigned lineno;
         const char *filename;
         JSPrincipals *originPrincipals;
         uint32_t pcOffset;
         DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset,
                                              &originPrincipals,
                                              evalType == DIRECT_EVAL
                                              ? CALLED_FROM_JSOP_EVAL
@@ -383,17 +382,17 @@ js::DirectEvalStringFromIon(JSContext *c
     if (ejr != EvalJSON_NotJSON)
         return ejr == EvalJSON_Success;
 
     EvalScriptGuard esg(cx);
 
     esg.lookupInEvalCache(flatStr, callerScript, pc);
 
     if (!esg.foundScript()) {
-        JSScript *maybeScript;
+        RootedScript maybeScript(cx);
         const char *filename;
         unsigned lineno;
         JSPrincipals *originPrincipals;
         uint32_t pcOffset;
         DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset,
                                               &originPrincipals, CALLED_FROM_JSOP_EVAL);
 
         const char *introducerFilename = filename;
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -40,17 +40,16 @@
 #include "jsobjinlines.h"
 
 #include "vm/ObjectImpl-inl.h"
 
 using namespace js;
 
 using mozilla::IsFinite;
 using mozilla::IsNegativeZero;
-using mozilla::Range;
 
 #if ENABLE_INTL_API
 using icu::Locale;
 using icu::NumberingSystem;
 #endif
 
 
 /*
@@ -969,18 +968,18 @@ intl_CompareStrings(JSContext *cx, UColl
     AutoStableStringChars stableChars1(cx);
     if (!stableChars1.initTwoByte(cx, str1))
         return false;
 
     AutoStableStringChars stableChars2(cx);
     if (!stableChars2.initTwoByte(cx, str2))
         return false;
 
-    Range<const jschar> chars1 = stableChars1.twoByteRange();
-    Range<const jschar> chars2 = stableChars2.twoByteRange();
+    mozilla::Range<const jschar> chars1 = stableChars1.twoByteRange();
+    mozilla::Range<const jschar> chars2 = stableChars2.twoByteRange();
 
     UCollationResult uresult = ucol_strcoll(coll,
                                             JSCharToUChar(chars1.start().get()), chars1.length(),
                                             JSCharToUChar(chars2.start().get()), chars2.length());
     int32_t res;
     switch (uresult) {
         case UCOL_LESS: res = -1; break;
         case UCOL_EQUAL: res = 0; break;
@@ -1795,17 +1794,17 @@ js::intl_patternForSkeleton(JSContext *c
     JSFlatString *skeletonFlat = args[1].toString()->ensureFlat(cx);
     if (!skeletonFlat)
         return false;
 
     AutoStableStringChars stableChars(cx);
     if (!stableChars.initTwoByte(cx, skeletonFlat))
         return false;
 
-    Range<const jschar> skeletonChars = stableChars.twoByteRange();
+    mozilla::Range<const jschar> skeletonChars = stableChars.twoByteRange();
     uint32_t skeletonLen = u_strlen(JSCharToUChar(skeletonChars.start().get()));
 
     UErrorCode status = U_ZERO_ERROR;
     UDateTimePatternGenerator *gen = udatpg_open(icuLocale(locale.ptr()), &status);
     if (U_FAILURE(status)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INTERNAL_INTL_ERROR);
         return false;
     }
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -3208,16 +3208,27 @@ MOZ_ARG_DISABLE_BOOL(exact-rooting,
 [  --disable-exact-rooting  Enable use of conservative stack scanning for GC],
     JSGC_USE_EXACT_ROOTING= ,
     JSGC_USE_EXACT_ROOTING=1 )
 if test -n "$JSGC_USE_EXACT_ROOTING"; then
     AC_DEFINE(JSGC_USE_EXACT_ROOTING)
 fi
 
 dnl ========================================================
+dnl = Use GC tracing
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(gc-trace,
+[  --enable-gc-trace  Enable tracing of allocation and finalization],
+    JS_GC_TRACE=1,
+    JS_GC_TRACE= )
+if test -n "$JS_GC_TRACE"; then
+    AC_DEFINE(JS_GC_TRACE)
+fi
+
+dnl ========================================================
 dnl = Use Valgrind
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(valgrind,
 [  --enable-valgrind       Enable Valgrind integration hooks (default=no)],
     MOZ_VALGRIND=1,
     MOZ_VALGRIND= )
 if test -n "$MOZ_VALGRIND"; then
     MOZ_CHECK_HEADER([valgrind/valgrind.h], [],
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -37,25 +37,28 @@
 #include "jsprf.h"
 
 #include "builtin/TypedObject.h"
 #include "ctypes/Library.h"
 
 using namespace std;
 using mozilla::NumericLimits;
 
+using JS::AutoCheckCannotGC;
+
 namespace js {
 namespace ctypes {
 
+template <typename CharT>
 size_t
-GetDeflatedUTF8StringLength(JSContext *maybecx, const jschar *chars,
+GetDeflatedUTF8StringLength(JSContext *maybecx, const CharT *chars,
                             size_t nchars)
 {
     size_t nbytes;
-    const jschar *end;
+    const CharT *end;
     unsigned c, c2;
     char buffer[10];
 
     nbytes = nchars;
     for (end = chars + nchars; chars != end; chars++) {
         c = *chars;
         if (c < 0x80)
             continue;
@@ -78,26 +81,47 @@ GetDeflatedUTF8StringLength(JSContext *m
             c >>= 5;
             nbytes++;
         }
     }
     return nbytes;
 
   bad_surrogate:
     if (maybecx) {
+        js::gc::AutoSuppressGC suppress(maybecx);
         JS_snprintf(buffer, 10, "0x%x", c);
         JS_ReportErrorFlagsAndNumber(maybecx, JSREPORT_ERROR, js_GetErrorMessage,
                                      nullptr, JSMSG_BAD_SURROGATE_CHAR, buffer);
     }
     return (size_t) -1;
 }
 
+template size_t
+GetDeflatedUTF8StringLength(JSContext *maybecx, const Latin1Char *chars,
+                            size_t nchars);
+
+template size_t
+GetDeflatedUTF8StringLength(JSContext *maybecx, const jschar *chars,
+                            size_t nchars);
+
+static size_t
+GetDeflatedUTF8StringLength(JSContext *maybecx, JSLinearString *str)
+{
+    size_t length = str->length();
+
+    JS::AutoCheckCannotGC nogc;
+    return str->hasLatin1Chars()
+           ? GetDeflatedUTF8StringLength(maybecx, str->latin1Chars(nogc), length)
+           : GetDeflatedUTF8StringLength(maybecx, str->twoByteChars(nogc), length);
+}
+
+template <typename CharT>
 bool
-DeflateStringToUTF8Buffer(JSContext *maybecx, const jschar *src, size_t srclen,
-                              char *dst, size_t *dstlenp)
+DeflateStringToUTF8Buffer(JSContext *maybecx, const CharT *src, size_t srclen,
+                          char *dst, size_t *dstlenp)
 {
     size_t i, utf8Len;
     jschar c, c2;
     uint32_t v;
     uint8_t utf8buf[6];
 
     size_t dstlen = *dstlenp;
     size_t origDstlen = dstlen;
@@ -142,22 +166,43 @@ badSurrogate:
     /* Delegate error reporting to the measurement function. */
     if (maybecx)
         GetDeflatedUTF8StringLength(maybecx, src - 1, srclen + 1);
     return false;
 
 bufferTooSmall:
     *dstlenp = (origDstlen - dstlen);
     if (maybecx) {
+        js::gc::AutoSuppressGC suppress(maybecx);
         JS_ReportErrorNumber(maybecx, js_GetErrorMessage, nullptr,
                              JSMSG_BUFFER_TOO_SMALL);
     }
     return false;
 }
 
+template bool
+DeflateStringToUTF8Buffer(JSContext *maybecx, const Latin1Char *src, size_t srclen,
+                          char *dst, size_t *dstlenp);
+
+template bool
+DeflateStringToUTF8Buffer(JSContext *maybecx, const jschar *src, size_t srclen,
+                          char *dst, size_t *dstlenp);
+
+static bool
+DeflateStringToUTF8Buffer(JSContext *maybecx, JSLinearString *str, char *dst,
+                          size_t *dstlenp)
+{
+    size_t length = str->length();
+
+    JS::AutoCheckCannotGC nogc;
+    return str->hasLatin1Chars()
+           ? DeflateStringToUTF8Buffer(maybecx, str->latin1Chars(nogc), length, dst, dstlenp)
+           : DeflateStringToUTF8Buffer(maybecx, str->twoByteChars(nogc), length, dst, dstlenp);
+}
+
 /*******************************************************************************
 ** JSAPI function prototypes
 *******************************************************************************/
 
 // We use an enclosing struct here out of paranoia about the ability of gcc 4.4
 // (and maybe 4.5) to correctly compile this if it were a template function.
 // See also the comments in dom/workers/Events.cpp (and other adjacent files) by
 // the |struct Property| there.
@@ -1737,27 +1782,23 @@ jsvalToFloat(JSContext *cx, jsval val, F
       }
     }
   }
   // Don't silently convert true to 1.0 or false to 0.0, even though C/C++
   // does it. It's likely to be a mistake.
   return false;
 }
 
-template<class IntegerType>
+template <class IntegerType, class CharT>
 static bool
-StringToInteger(JSContext* cx, JSString* string, IntegerType* result)
+StringToInteger(JSContext* cx, CharT* cp, size_t length, IntegerType* result)
 {
   JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
 
-  const jschar* cp = string->getChars(nullptr);
-  if (!cp)
-    return false;
-
-  const jschar* end = cp + string->length();
+  const CharT* end = cp + length;
   if (cp == end)
     return false;
 
   IntegerType sign = 1;
   if (cp[0] == '-') {
     if (!NumericLimits<IntegerType>::is_signed)
       return false;
 
@@ -1791,16 +1832,31 @@ StringToInteger(JSContext* cx, JSString*
     if (i / base != ii) // overflow
       return false;
   }
 
   *result = i;
   return true;
 }
 
+template<class IntegerType>
+static bool
+StringToInteger(JSContext* cx, JSString* string, IntegerType* result)
+{
+  JSLinearString *linear = string->ensureLinear(cx);
+  if (!linear)
+    return false;
+
+  AutoCheckCannotGC nogc;
+  size_t length = linear->length();
+  return string->hasLatin1Chars()
+         ? StringToInteger<IntegerType>(cx, linear->latin1Chars(nogc), length, result)
+         : StringToInteger<IntegerType>(cx, linear->twoByteChars(nogc), length, result);
+}
+
 // Implicitly convert val to IntegerType, allowing int, double,
 // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
 // (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.)
 template<class IntegerType>
 static bool
 jsvalToBigInteger(JSContext* cx,
                   jsval val,
                   bool allowString,
@@ -2295,20 +2351,20 @@ ImplicitConvert(JSContext* cx,
   case TYPE_##name: {                                                          \
     /* Convert from a 1-character string, regardless of encoding, */           \
     /* or from an integer, provided the result fits in 'type'. */              \
     type result;                                                               \
     if (val.isString()) {                                                \
       JSString* str = val.toString();                                    \
       if (str->length() != 1)                                                  \
         return TypeError(cx, #name, val);                                      \
-      const jschar *chars = str->getChars(cx);                                 \
-      if (!chars)                                                              \
+      JSLinearString *linear = str->ensureLinear(cx);                          \
+      if (!linear)                                                             \
         return false;                                                          \
-      result = chars[0];                                                       \
+      result = linear->latin1OrTwoByteChar(0);                                 \
     } else if (!jsvalToInteger(cx, val, &result)) {                            \
       return TypeError(cx, #name, val);                                        \
     }                                                                          \
     *static_cast<type*>(buffer) = result;                                      \
     break;                                                                     \
   }
 #include "ctypes/typedefs.h"
   case TYPE_pointer: {
@@ -2341,56 +2397,60 @@ ImplicitConvert(JSContext* cx,
       }
 
     } else if (isArgument && val.isString()) {
       // Convert the string for the ffi call. This requires allocating space
       // which the caller assumes ownership of.
       // TODO: Extend this so we can safely convert strings at other times also.
       JSString* sourceString = val.toString();
       size_t sourceLength = sourceString->length();
-      const jschar* sourceChars = sourceString->getChars(cx);
-      if (!sourceChars)
+      JSLinearString* sourceLinear = sourceString->ensureLinear(cx);
+      if (!sourceLinear)
         return false;
 
       switch (CType::GetTypeCode(baseType)) {
       case TYPE_char:
       case TYPE_signed_char:
       case TYPE_unsigned_char: {
         // Convert from UTF-16 to UTF-8.
-        size_t nbytes =
-          GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength);
+        size_t nbytes = GetDeflatedUTF8StringLength(cx, sourceLinear);
         if (nbytes == (size_t) -1)
           return false;
 
         char** charBuffer = static_cast<char**>(buffer);
         *charBuffer = cx->pod_malloc<char>(nbytes + 1);
         if (!*charBuffer) {
           JS_ReportAllocationOverflow(cx);
           return false;
         }
 
-        ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength,
-                    *charBuffer, &nbytes));
+        ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceLinear, *charBuffer, &nbytes));
         (*charBuffer)[nbytes] = 0;
         *freePointer = true;
         break;
       }
       case TYPE_jschar: {
         // Copy the jschar string data. (We could provide direct access to the
         // JSString's buffer, but this approach is safer if the caller happens
         // to modify the string.)
         jschar** jscharBuffer = static_cast<jschar**>(buffer);
         *jscharBuffer = cx->pod_malloc<jschar>(sourceLength + 1);
         if (!*jscharBuffer) {
           JS_ReportAllocationOverflow(cx);
           return false;
         }
 
         *freePointer = true;
-        memcpy(*jscharBuffer, sourceChars, sourceLength * sizeof(jschar));
+        if (sourceLinear->hasLatin1Chars()) {
+            AutoCheckCannotGC nogc;
+            CopyAndInflateChars(*jscharBuffer, sourceLinear->latin1Chars(nogc), sourceLength);
+        } else {
+            AutoCheckCannotGC nogc;
+            mozilla::PodCopy(*jscharBuffer, sourceLinear->twoByteChars(nogc), sourceLength);
+        }
         (*jscharBuffer)[sourceLength] = 0;
         break;
       }
       default:
         return TypeError(cx, "string pointer", val);
       }
       break;
     } else if (val.isObject() && JS_IsArrayBufferObject(valObj)) {
@@ -2417,55 +2477,63 @@ ImplicitConvert(JSContext* cx,
   }
   case TYPE_array: {
     RootedObject baseType(cx, ArrayType::GetBaseType(targetType));
     size_t targetLength = ArrayType::GetLength(targetType);
 
     if (val.isString()) {
       JSString* sourceString = val.toString();
       size_t sourceLength = sourceString->length();
-      const jschar* sourceChars = sourceString->getChars(cx);
-      if (!sourceChars)
+      JSLinearString* sourceLinear = sourceString->ensureLinear(cx);
+      if (!sourceLinear)
         return false;
 
       switch (CType::GetTypeCode(baseType)) {
       case TYPE_char:
       case TYPE_signed_char:
       case TYPE_unsigned_char: {
-        // Convert from UTF-16 to UTF-8.
+        // Convert from UTF-16 or Latin1 to UTF-8.
         size_t nbytes =
-          GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength);
+          GetDeflatedUTF8StringLength(cx, sourceLinear);
         if (nbytes == (size_t) -1)
           return false;
 
         if (targetLength < nbytes) {
           JS_ReportError(cx, "ArrayType has insufficient length");
           return false;
         }
 
         char* charBuffer = static_cast<char*>(buffer);
-        ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceChars, sourceLength,
-                    charBuffer, &nbytes));
+        ASSERT_OK(DeflateStringToUTF8Buffer(cx, sourceLinear, charBuffer,
+                                            &nbytes));
 
         if (targetLength > nbytes)
           charBuffer[nbytes] = 0;
 
         break;
       }
       case TYPE_jschar: {
         // Copy the string data, jschar for jschar, including the terminator
         // if there's space.
         if (targetLength < sourceLength) {
           JS_ReportError(cx, "ArrayType has insufficient length");
           return false;
         }
 
-        memcpy(buffer, sourceChars, sourceLength * sizeof(jschar));
+        jschar *dest = static_cast<jschar*>(buffer);
+        if (sourceLinear->hasLatin1Chars()) {
+            AutoCheckCannotGC nogc;
+            CopyAndInflateChars(dest, sourceLinear->latin1Chars(nogc), sourceLength);
+        } else {
+            AutoCheckCannotGC nogc;
+            mozilla::PodCopy(dest, sourceLinear->twoByteChars(nogc), sourceLength);
+        }
+
         if (targetLength > sourceLength)
-          static_cast<jschar*>(buffer)[sourceLength] = 0;
+          dest[sourceLength] = 0;
 
         break;
       }
       default:
         return TypeError(cx, "array", val);
       }
 
     } else if (val.isObject() && JS_IsArrayObject(cx, valObj)) {
@@ -4285,26 +4353,26 @@ ArrayType::ConstructData(JSContext* cx,
         return false;
       }
 
     } else if (args[0].isString()) {
       // We were given a string. Size the array to the appropriate length,
       // including space for the terminator.
       JSString* sourceString = args[0].toString();
       size_t sourceLength = sourceString->length();
-      const jschar* sourceChars = sourceString->getChars(cx);
-      if (!sourceChars)
+      JSLinearString* sourceLinear = sourceString->ensureLinear(cx);
+      if (!sourceLinear)
         return false;
 
       switch (CType::GetTypeCode(baseType)) {
       case TYPE_char:
       case TYPE_signed_char:
       case TYPE_unsigned_char: {
         // Determine the UTF-8 length.
-        length = GetDeflatedUTF8StringLength(cx, sourceChars, sourceLength);
+        length = GetDeflatedUTF8StringLength(cx, sourceLinear);
         if (length == (size_t) -1)
           return false;
 
         ++length;
         break;
       }
       case TYPE_jschar:
         length = sourceLength + 1;
@@ -4811,18 +4879,22 @@ StructType::DefineInternal(JSContext* cx
       // Make sure each field name is unique
       FieldInfoHash::AddPtr entryPtr = fields->lookupForAdd(name);
       if (entryPtr) {
         JS_ReportError(cx, "struct fields must have unique names");
         return false;
       }
 
       // Add the field to the StructType's 'prototype' property.
+      AutoStableStringChars nameChars(cx);
+      if (!nameChars.initTwoByte(cx, name))
+          return false;
+
       if (!JS_DefineUCProperty(cx, prototype,
-             name->chars(), name->length(), UndefinedHandleValue,
+             nameChars.twoByteChars(), name->length(), UndefinedHandleValue,
              JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_PERMANENT,
              StructType::FieldGetter, StructType::FieldSetter))
         return false;
 
       size_t fieldSize = CType::GetSize(fieldType);
       size_t fieldAlign = CType::GetAlignment(fieldType);
       size_t fieldOffset = Align(structSize, fieldAlign);
       // Check for overflow. Since we hold invariant that fieldSize % fieldAlign
@@ -5387,23 +5459,23 @@ static MOZ_ALWAYS_INLINE bool
 IsEllipsis(JSContext* cx, jsval v, bool* isEllipsis)
 {
   *isEllipsis = false;
   if (!v.isString())
     return true;
   JSString* str = v.toString();
   if (str->length() != 3)
     return true;
-  const jschar* chars = str->getChars(cx);
-  if (!chars)
+  JSLinearString *linear = str->ensureLinear(cx);
+  if (!linear)
     return false;
   jschar dot = '.';
-  *isEllipsis = (chars[0] == dot &&
-                 chars[1] == dot &&
-                 chars[2] == dot);
+  *isEllipsis = (linear->latin1OrTwoByteChar(0) == dot &&
+                 linear->latin1OrTwoByteChar(1) == dot &&
+                 linear->latin1OrTwoByteChar(2) == dot);
   return true;
 }
 
 static bool
 PrepareCIF(JSContext* cx,
            FunctionInfo* fninfo)
 {
   ffi_abi abi;
--- a/js/src/ctypes/CTypes.h
+++ b/js/src/ctypes/CTypes.h
@@ -90,38 +90,50 @@ AppendString(Vector<T, N, AP> &v, Vector
   v.append(w.begin(), w.length());
 }
 
 template <size_t N, class AP>
 void
 AppendString(Vector<jschar, N, AP> &v, JSString* str)
 {
   JS_ASSERT(str);
-  const jschar *chars = str->getChars(nullptr);
-  if (!chars)
+  JSLinearString *linear = str->ensureLinear(nullptr);
+  if (!linear)
     return;
-  v.append(chars, str->length());
+  JS::AutoCheckCannotGC nogc;
+  if (linear->hasLatin1Chars())
+    v.append(linear->latin1Chars(nogc), linear->length());
+  else
+    v.append(linear->twoByteChars(nogc), linear->length());
 }
 
 template <size_t N, class AP>
 void
 AppendString(Vector<char, N, AP> &v, JSString* str)
 {
   JS_ASSERT(str);
   size_t vlen = v.length();
   size_t alen = str->length();
   if (!v.resize(vlen + alen))
     return;
 
-  const jschar *chars = str->getChars(nullptr);
-  if (!chars)
+  JSLinearString *linear = str->ensureLinear(nullptr);
+  if (!linear)
     return;
 
-  for (size_t i = 0; i < alen; ++i)
-    v[i + vlen] = char(chars[i]);
+  JS::AutoCheckCannotGC nogc;
+  if (linear->hasLatin1Chars()) {
+    const Latin1Char *chars = linear->latin1Chars(nogc);
+    for (size_t i = 0; i < alen; ++i)
+      v[i + vlen] = char(chars[i]);
+  } else {
+    const jschar *chars = linear->twoByteChars(nogc);
+    for (size_t i = 0; i < alen; ++i)
+      v[i + vlen] = char(chars[i]);
+  }
 }
 
 template <class T, size_t N, class AP, size_t ArrayLength>
 void
 PrependString(Vector<T, N, AP> &v, const char (&array)[ArrayLength])
 {
   // Don't include the trailing '\0'.
   size_t alen = ArrayLength - 1;
@@ -142,33 +154,42 @@ void
 PrependString(Vector<jschar, N, AP> &v, JSString* str)
 {
   JS_ASSERT(str);
   size_t vlen = v.length();
   size_t alen = str->length();
   if (!v.resize(vlen + alen))
     return;
 
-  const jschar *chars = str->getChars(nullptr);
-  if (!chars)
+  JSLinearString* linear = str->ensureLinear(nullptr);
+  if (!linear)
     return;
 
   // Move vector data forward. This is safe since we've already resized.
   memmove(v.begin() + alen, v.begin(), vlen * sizeof(jschar));
 
   // Copy data to insert.
-  memcpy(v.begin(), chars, alen * sizeof(jschar));
+  JS::AutoCheckCannotGC nogc;
+  if (linear->hasLatin1Chars()) {
+    const Latin1Char *chars = linear->latin1Chars(nogc);
+    for (size_t i = 0; i < alen; i++)
+      v[i] = chars[i];
+  } else {
+    memcpy(v.begin(), linear->twoByteChars(nogc), alen * sizeof(jschar));
+  }
 }
 
+template <typename CharT>
 extern size_t
-GetDeflatedUTF8StringLength(JSContext *maybecx, const jschar *chars,
+GetDeflatedUTF8StringLength(JSContext *maybecx, const CharT *chars,
                             size_t charsLength);
 
+template <typename CharT>
 bool
-DeflateStringToUTF8Buffer(JSContext *maybecx, const jschar *src, size_t srclen,
+DeflateStringToUTF8Buffer(JSContext *maybecx, const CharT *src, size_t srclen,
                           char *dst, size_t *dstlenp);
 
 
 /*******************************************************************************
 ** Function and struct API definitions
 *******************************************************************************/
 
 MOZ_ALWAYS_INLINE void
@@ -229,33 +250,39 @@ static_assert(sizeof(UnbarrieredFieldInf
               "UnbarrieredFieldInfo should be the same as FieldInfo but with unbarriered mType");
 
 // Hash policy for FieldInfos.
 struct FieldHashPolicy : DefaultHasher<JSFlatString*>
 {
   typedef JSFlatString* Key;
   typedef Key Lookup;
 
-  static uint32_t hash(const Lookup &l) {
-    const jschar* s = l->chars();
-    size_t n = l->length();
+  template <typename CharT>
+  static uint32_t hash(const CharT *s, size_t n) {
     uint32_t hash = 0;
     for (; n > 0; s++, n--)
       hash = hash * 33 + *s;
     return hash;
   }
 
+  static uint32_t hash(const Lookup &l) {
+    JS::AutoCheckCannotGC nogc;
+    return l->hasLatin1Chars()
+           ? hash(l->latin1Chars(nogc), l->length())
+           : hash(l->twoByteChars(nogc), l->length());
+  }
+
   static bool match(const Key &k, const Lookup &l) {
     if (k == l)
       return true;
 
     if (k->length() != l->length())
       return false;
 
-    return memcmp(k->chars(), l->chars(), k->length() * sizeof(jschar)) == 0;
+    return EqualChars(k, l);
   }
 };
 
 typedef HashMap<JSFlatString*, FieldInfo, FieldHashPolicy, SystemAllocPolicy> FieldInfoHash;
 
 // Descriptor of ABI, return type, argument types, and variadicity for a
 // FunctionType.
 struct FunctionInfo
--- a/js/src/ctypes/Library.cpp
+++ b/js/src/ctypes/Library.cpp
@@ -99,48 +99,48 @@ Library::Create(JSContext* cx, jsval pat
     JS_ReportError(cx, "open takes a string argument");
     return nullptr;
   }
 
   PRLibSpec libSpec;
   RootedFlatString pathStr(cx, JS_FlattenString(cx, path.toString()));
   if (!pathStr)
     return nullptr;
+  AutoStableStringChars pathStrChars(cx);
+  if (!pathStrChars.initTwoByte(cx, pathStr))
+    return nullptr;
 #ifdef XP_WIN
   // On Windows, converting to native charset may corrupt path string.
   // So, we have to use Unicode path directly.
-  char16ptr_t pathChars = JS_GetFlatStringChars(pathStr);
-  if (!pathChars)
-    return nullptr;
-
+  char16ptr_t pathChars = pathStrChars.twoByteChars();
   libSpec.value.pathname_u = pathChars;
   libSpec.type = PR_LibSpec_PathnameU;
 #else
   // Convert to platform native charset if the appropriate callback has been
   // provided.
   char* pathBytes;
   if (callbacks && callbacks->unicodeToNative) {
     pathBytes =
-      callbacks->unicodeToNative(cx, pathStr->chars(), pathStr->length());
+      callbacks->unicodeToNative(cx, pathStrChars.twoByteChars(), pathStr->length());
     if (!pathBytes)
       return nullptr;
 
   } else {
     // Fallback: assume the platform native charset is UTF-8. This is true
     // for Mac OS X, Android, and probably Linux.
     size_t nbytes =
-      GetDeflatedUTF8StringLength(cx, pathStr->chars(), pathStr->length());
+      GetDeflatedUTF8StringLength(cx, pathStrChars.twoByteChars(), pathStr->length());
     if (nbytes == (size_t) -1)
       return nullptr;
 
     pathBytes = static_cast<char*>(JS_malloc(cx, nbytes + 1));
     if (!pathBytes)
       return nullptr;
 
-    ASSERT_OK(DeflateStringToUTF8Buffer(cx, pathStr->chars(),
+    ASSERT_OK(DeflateStringToUTF8Buffer(cx, pathStrChars.twoByteChars(),
                 pathStr->length(), pathBytes, &nbytes));
     pathBytes[nbytes] = 0;
   }
 
   libSpec.value.pathname = pathBytes;
   libSpec.type = PR_LibSpec_Pathname;
 #endif
 
new file mode 100644
--- /dev/null
+++ b/js/src/devtools/gctrace/Makefile
@@ -0,0 +1,6 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+gcstats: gcstats.cpp ../../gc/GCTraceFormat.h Makefile
+	$(CXX) -std=c++11 -g -O2 -I../.. -o $@ $<
new file mode 100644
--- /dev/null
+++ b/js/src/devtools/gctrace/gcstats.cpp
@@ -0,0 +1,765 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=78:
+ *
+ * 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/. */
+
+/*
+ * Read and process GC trace logs.
+ */
+
+#include "gc/GCTraceFormat.h"
+
+#define __STDC_FORMAT_MACROS
+
+#include <assert.h>
+#include <inttypes.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <algorithm>
+#include <functional>
+#include <unordered_map>
+#include <vector>
+
+// State of the program
+
+enum Heap
+{
+    Nursery,
+    TenuredHeap,
+
+    HeapKinds
+};
+
+enum State
+{
+    StateMutator,
+    StateMinorGC,
+    StateMajorGC
+};
+
+typedef uint64_t address;
+typedef uint8_t AllocKind;
+typedef uint8_t ClassId;
+
+struct AllocInfo
+{
+    const uint64_t serial;
+    const AllocKind kind;
+    const Heap initialHeap;
+    ClassId classId;
+
+    AllocInfo(uint64_t allocCount, uint8_t kind, Heap loc)
+      : serial(allocCount), kind(kind), initialHeap(loc), classId(0)
+    {
+        assert(kind < AllocKinds);
+        assert(initialHeap < HeapKinds);
+    }
+};
+
+struct ClassInfo
+{
+    const ClassId id;
+    const char *name;
+    const uint32_t flags;
+
+    ClassInfo(ClassId id, const char *name, uint32_t flags)
+      : id(id), name(name), flags(flags) {}
+};
+
+typedef std::unordered_map<address, AllocInfo> AllocMap;
+typedef std::unordered_map<address, ClassId> ClassMap;
+typedef std::vector<ClassInfo> ClassVector;
+
+uint64_t thingSizes[AllocKinds];
+AllocMap nurseryThings;
+AllocMap tenuredThings;
+ClassMap classMap;
+ClassVector classes;
+uint64_t allocCount = 0;
+
+// Collected data
+
+const unsigned MaxClasses = 128;
+const unsigned LifetimeBinLog = 10;
+const unsigned MaxLifetimeBins = 40;
+
+const unsigned AugHeapKinds = HeapKinds + 1;
+const unsigned HeapTotal = HeapKinds;
+const unsigned AugAllocKinds = AllocKinds + 1;
+const unsigned AllocKindTotal = AllocKinds;
+const unsigned AugLifetimeBins = MaxLifetimeBins + 1;
+const unsigned LifetimeBinTotal = MaxLifetimeBins;
+const unsigned AugClasses = MaxClasses + 1;
+const unsigned ClassTotal = MaxClasses;
+
+struct CountByHeap
+{
+    CountByHeap() { memset(this, 0, sizeof(*this)); }
+    void inc(Heap heap) {
+        assert(heap < AugHeapKinds);
+        ++count[heap];
+        ++count[HeapTotal];
+    }
+    uint64_t get(unsigned heap) {
+        assert(heap < AugHeapKinds);
+        return count[heap];
+    }
+  private:
+    uint64_t count[AugHeapKinds];
+};
+
+struct CountByHeapAndKind
+{
+    CountByHeapAndKind() {}
+    void inc(Heap heap, AllocKind kind) {
+        assert(kind < AugAllocKinds);
+        count[kind].inc(heap);
+        count[AllocKindTotal].inc(heap);
+    }
+    uint64_t get(unsigned heap, unsigned kind) {
+        assert(kind < AugAllocKinds);
+        return count[kind].get(heap);
+    }
+  private:
+    CountByHeap count[AugAllocKinds];
+};
+
+struct CountByHeapKindAndLifetime
+{
+    CountByHeapKindAndLifetime() {}
+    void inc(Heap heap, AllocKind kind, unsigned lifetimeBin) {
+        assert(lifetimeBin < MaxLifetimeBins);
+        count[lifetimeBin].inc(heap, kind);
+        count[LifetimeBinTotal].inc(heap, kind);
+    }
+    uint64_t get(unsigned heap, unsigned kind, unsigned lifetimeBin) {
+        assert(lifetimeBin < AugLifetimeBins);
+        return count[lifetimeBin].get(heap, kind);
+    }
+  private:
+    CountByHeapAndKind count[AugLifetimeBins];
+};
+
+struct CountByHeapAndClass
+{
+    CountByHeapAndClass() {}
+    void inc(Heap heap, unsigned classId) {
+        assert(classId < MaxClasses);
+        count[classId].inc(heap);
+        count[ClassTotal].inc(heap);
+    }
+    uint64_t get(unsigned heap, unsigned classId) {
+        assert(classId < AugClasses);
+        return count[classId].get(heap);
+    }
+  private:
+    CountByHeap count[AugClasses];
+};
+
+struct CountByHeapClassAndLifetime
+{
+    CountByHeapClassAndLifetime() {}
+    void inc(Heap heap, unsigned classId, unsigned lifetimeBin) {
+        assert(lifetimeBin < MaxLifetimeBins);
+        count[lifetimeBin].inc(heap, classId);
+        count[LifetimeBinTotal].inc(heap, classId);
+    }
+    uint64_t get(unsigned heap, unsigned classId, unsigned lifetimeBin) {
+        assert(lifetimeBin < AugLifetimeBins);
+        return count[lifetimeBin].get(heap, classId);
+    }
+  private:
+    CountByHeapAndClass count[AugLifetimeBins];
+};
+
+unsigned timesliceSize;
+unsigned lifetimeBins;
+CountByHeapKindAndLifetime allocTotals;
+std::vector<CountByHeapKindAndLifetime> allocTotalsBySlice;
+CountByHeapClassAndLifetime objectTotals;
+std::vector<uint64_t> gcBytesAllocatedInSlice;
+std::vector<uint64_t> gcBytesFreedInSlice;
+
+static void
+die(const char *format, ...)
+{
+    va_list va;
+    va_start(va, format);
+    vfprintf(stderr, format, va);
+    fprintf(stderr, "\n");
+    va_end(va);
+    exit(1);
+}
+
+const uint64_t FirstBinSize = 100;
+const unsigned BinLog = 2;
+
+static unsigned
+getBin(uint64_t lifetime)
+{
+    /*
+     * Calculate a bin number for a given lifetime.
+     *
+     * We use a logarithmic scale, starting with a bin size of 100 and doubling
+     * from there.
+     */
+    static double logDivisor = log(BinLog);
+    if (lifetime < FirstBinSize)
+        return 0;
+    return unsigned(log(lifetime / FirstBinSize) / logDivisor) + 1;
+}
+
+static unsigned
+binLimit(unsigned bin)
+{
+    return unsigned(pow(BinLog, bin) * FirstBinSize);
+}
+
+static void
+testBinning()
+{
+    assert(getBin(0) == 0);
+    assert(getBin(FirstBinSize - 1) == 0);
+    assert(getBin(FirstBinSize) == 1);
+    assert(getBin(2 * FirstBinSize - 1) == 1);
+    assert(getBin(2 * FirstBinSize) == 2);
+    assert(getBin(4 * FirstBinSize - 1) == 2);
+    assert(getBin(4 * FirstBinSize) == 3);
+    assert(binLimit(0) == FirstBinSize);
+    assert(binLimit(1) == 2 * FirstBinSize);
+    assert(binLimit(2) == 4 * FirstBinSize);
+    assert(binLimit(3) == 8 * FirstBinSize);
+}
+
+static const char *
+allocKindName(AllocKind kind)
+{
+    static const char *AllocKindNames[] = {
+        "Object0",
+        "Object0Bg",
+        "Object2",
+        "Object2Bg",
+        "Object4",
+        "Object4Bg",
+        "Object8",
+        "Object8Bg",
+        "Object12",
+        "Object12Bg",
+        "Object16",
+        "Object16Bg",
+        "Script",
+        "LazyScript",
+        "Shape",
+        "BaseShape",
+        "TypeObject",
+        "FatInlineString",
+        "String",
+        "ExternalString",
+        "Symbol",
+        "JitCode",
+        "Total"
+    };
+    assert(sizeof(AllocKindNames) / sizeof(const char *) == AugAllocKinds);
+    assert(kind < AugAllocKinds);
+    return AllocKindNames[kind];
+}
+
+static const char *
+heapName(unsigned heap)
+{
+    static const char *HeapNames[] = {
+        "nursery",
+        "tenured heap",
+        "all"
+    };
+    assert(heap < AugHeapKinds);
+    return HeapNames[heap];
+}
+
+
+static const char *
+heapLabel(unsigned heap)
+{
+    static const char *HeapLabels[] = {
+        "Nursery",
+        "Tenured heap",
+        "Total"
+    };
+    assert(heap < AugHeapKinds);
+    return HeapLabels[heap];
+}
+
+static void
+outputGcBytesAllocated(FILE *file)
+{
+    fprintf(file, "# Total GC bytes allocated by timeslice\n");
+    fprintf(file, "# Total allocations: %" PRIu64 "\n", allocCount);
+    fprintf(file, "Time, GCBytesAllocated\n");
+
+    uint64_t timesliceCount = allocCount / timesliceSize + 1;
+    uint64_t total = 0;
+    for (uint64_t i = 0; i < timesliceCount; ++i) {
+        total += gcBytesAllocatedInSlice[i];
+        fprintf(file, "%12" PRIu64 ", %12" PRIu64 "\n", i * timesliceSize, total);
+    }
+}
+
+static void
+outputGcBytesUsed(FILE *file)
+{
+    fprintf(file, "# Total GC bytes used by timeslice\n");
+    fprintf(file, "# Total allocations: %" PRIu64 "\n", allocCount);
+    fprintf(file, "Time, GCBytesUsed\n");
+
+    uint64_t timesliceCount = allocCount / timesliceSize + 1;
+    uint64_t total = 0;
+    for (uint64_t i = 0; i < timesliceCount; ++i) {
+        total += gcBytesAllocatedInSlice[i] - gcBytesFreedInSlice[i];
+        fprintf(file, "%12" PRIu64 ", %12" PRIu64 "\n", i * timesliceSize, total);
+    }
+}
+
+static void
+outputThingCounts(FILE *file)
+{
+    fprintf(file, "# GC thing allocation count in nursery and tenured heap by kind\n");
+    fprintf(file, "# This shows what kind of things we are allocating in the nursery\n");
+    fprintf(file, "# Total allocations: %" PRIu64 "\n", allocCount);
+    fprintf(file, "Kind, Nursery, Tenured heap\n");
+    for (unsigned i = 0; i < AllocKinds; ++i) {
+        fprintf(file, "%15s, %8" PRIu64 ", %8" PRIu64 "\n", allocKindName(i),
+                allocTotals.get(Nursery, i, LifetimeBinTotal),
+                allocTotals.get(TenuredHeap, i, LifetimeBinTotal));
+    }
+}
+
+static void
+outputObjectCounts(FILE *file)
+{
+    fprintf(file, "# Object allocation count in nursery and tenured heap by class\n");
+    fprintf(file, "# This shows what kind of objects we are allocating in the nursery\n");
+    fprintf(file, "# Total allocations: %" PRIu64 "\n", allocCount);
+    fprintf(file, "Class, Nursery, Tenured heap, Total\n");
+    for (unsigned i = 0; i < classes.size(); ++i) {
+        fprintf(file, "%30s, %8" PRIu64 ", %8" PRIu64 ", %8" PRIu64 "\n",
+                classes[i].name,
+                objectTotals.get(Nursery, i, LifetimeBinTotal),
+                objectTotals.get(TenuredHeap, i, LifetimeBinTotal),
+                objectTotals.get(HeapTotal, i, LifetimeBinTotal));
+    }
+}
+
+static void
+outputLifetimeByHeap(FILE *file)
+{
+    fprintf(file, "# Lifetime of all things (in log2 bins) by initial heap\n");
+    fprintf(file, "# NB invalid unless execution was traced with appropriate zeal\n");
+    fprintf(file, "# Total allocations: %" PRIu64 "\n", allocCount);
+    fprintf(file, "Lifetime");
+    for (unsigned i = 0; i < HeapKinds; ++i)
+        fprintf(file, ", %s", heapLabel(i));
+    fprintf(file, "\n");
+
+    for (unsigned i = 0; i < lifetimeBins; ++i) {
+        fprintf(file, "%8d", binLimit(i));
+        for (unsigned j = 0; j < HeapKinds; ++j)
+            fprintf(file, ", %8" PRIu64, allocTotals.get(j, AllocKindTotal, i));
+        fprintf(file, "\n");
+    }
+}
+
+static void
+outputLifetimeByKind(FILE *file, unsigned initialHeap)
+{
+    assert(initialHeap < AugHeapKinds);
+
+    fprintf(file, "# Lifetime of %s things (in log2 bins) by kind\n", heapName(initialHeap));
+    fprintf(file, "# NB invalid unless execution was traced with appropriate zeal\n");
+    fprintf(file, "# Total allocations: %" PRIu64 "\n", allocCount);
+    fprintf(file, "Lifetime");
+    for (unsigned i = 0; i < AllocKinds; ++i)
+        fprintf(file, ", %15s", allocKindName(i));
+    fprintf(file, "\n");
+
+    for (unsigned i = 0; i < lifetimeBins; ++i) {
+        fprintf(file, "%8d", binLimit(i));
+        for (unsigned j = 0; j < AllocKinds; ++j)
+            fprintf(file, ", %8" PRIu64, allocTotals.get(initialHeap, j, i));
+        fprintf(file, "\n");
+    }
+}
+
+static void
+outputLifetimeByClass(FILE *file, unsigned initialHeap)
+{
+    assert(initialHeap < AugHeapKinds);
+
+    fprintf(file, "# Lifetime of %s things (in log2 bins) by class\n", heapName(initialHeap));
+    fprintf(file, "# NB invalid unless execution was traced with appropriate zeal\n");
+    fprintf(file, "# Total allocations: %" PRIu64 "\n", allocCount);
+    fprintf(file, "Lifetime");
+    for (unsigned i = 0; i < classes.size(); ++i)
+        fprintf(file, ", %15s", classes[i].name);
+    fprintf(file, "\n");
+
+    for (unsigned i = 0; i < lifetimeBins; ++i) {
+        fprintf(file, "%8d", binLimit(i));
+        for (unsigned j = 0; j < classes.size(); ++j)
+            fprintf(file, ", %8" PRIu64, objectTotals.get(initialHeap, j, i));
+        fprintf(file, "\n");
+    }
+}
+
+static void
+outputLifetimeBySlice(FILE *file)
+{
+    fprintf(file, "# Lifetime (in log2 bins) by allocation timeslice for all things\n");
+    fprintf(file, "Timeslice");
+    for (unsigned i = 0; i < lifetimeBins; ++i)
+        fprintf(file, ", Lifetime<%d", binLimit(i));
+    fprintf(file, "\n");
+
+    uint64_t timesliceCount = allocCount / timesliceSize + 1;
+    for (uint64_t i = 0; i < timesliceCount; ++i) {
+        fprintf(file, "%8" PRIu64, i);
+        for (unsigned j = 0; j < lifetimeBins; ++j) {
+            fprintf(file, ", %8" PRIu64,
+                    allocTotalsBySlice[i].get(HeapTotal, AllocKindTotal, j));
+        }
+        fprintf(file, "\n");
+    }
+}
+
+static void
+processAlloc(const AllocInfo &info, uint64_t finalizeTime)
+{
+    uint64_t lifetime = finalizeTime - info.serial;
+    unsigned timeslice = info.serial / timesliceSize;
+
+    unsigned lifetimeBin = getBin(lifetime);
+    assert(lifetimeBin < lifetimeBins);
+
+    allocTotals.inc(info.initialHeap, info.kind, lifetimeBin);
+    allocTotalsBySlice[timeslice].inc(info.initialHeap, info.kind, lifetimeBin);
+    if (info.kind <= LastObjectAllocKind)
+        objectTotals.inc(info.initialHeap, info.classId, lifetimeBin);
+
+    uint64_t size = thingSizes[info.kind];
+    gcBytesAllocatedInSlice[timeslice] += size;
+    gcBytesFreedInSlice[finalizeTime / timesliceSize] += size;
+}
+
+static bool
+readTrace(FILE* file, uint64_t& trace)
+{
+    if (fread(&trace, sizeof(trace), 1, file) != 1) {
+        if (feof(file))
+            return false;
+        else
+            die("Error reading input");
+    }
+    return true;
+}
+
+static GCTraceEvent
+getTraceEvent(uint64_t trace)
+{
+    uint64_t event = trace >> TraceEventShift;
+    assert(event < GCTraceEventCount);
+    return (GCTraceEvent)event;
+}
+
+static uint64_t
+getTracePayload(uint64_t trace)
+{
+    return trace & ((1lu << TracePayloadBits) - 1);
+}
+
+static uint8_t
+getTraceExtra(uint64_t trace)
+{
+    uint64_t extra = (trace >> TraceExtraShift) & ((1 << TraceExtraBits) - 1);
+    assert(extra < 256);
+    return (uint8_t)extra;
+}
+
+static uint64_t
+expectTrace(FILE *file, GCTraceEvent event)
+{
+    uint64_t trace;
+    if (!readTrace(file, trace))
+        die("End of file while expecting trace %d", event);
+    if (getTraceEvent(trace) != event)
+        die("Expected trace %d but got trace %d", event, getTraceEvent(trace));
+    return getTracePayload(trace);
+}
+
+static uint64_t
+expectDataAddress(FILE *file)
+{
+    return expectTrace(file, TraceDataAddress);
+}
+
+static uint32_t
+expectDataInt(FILE *file)
+{
+    return (uint32_t)expectTrace(file, TraceDataInt);
+}
+
+static char *
+expectDataString(FILE *file)
+{
+    uint64_t length = expectTrace(file, TraceDataString);
+    assert(length < 256); // Sanity check
+    char *string = static_cast<char *>(malloc(length + 1));
+    if (!string)
+        die("Out of memory while reading string data");
+
+    const unsigned charsPerWord = sizeof(uint64_t);
+    unsigned wordCount = (length + charsPerWord - 1) / charsPerWord;
+    for (unsigned i = 0; i < wordCount; ++i) {
+        if (fread(&string[i * charsPerWord], sizeof(char), charsPerWord, file) != charsPerWord)
+            die("Error or EOF while reading string data");
+    }
+    string[length] = 0;
+
+    return string;
+}
+
+static void
+createClassInfo(const char *name, uint32_t flags, address clasp = 0)
+{
+    ClassId id = classes.size();
+    classes.push_back(ClassInfo(id, name, flags));
+    if (clasp)
+        classMap.emplace(clasp, id);
+}
+
+static void
+readClassInfo(FILE *file, address clasp)
+{
+    assert(clasp);
+    uint32_t flags = expectDataInt(file);
+    char *name = expectDataString(file);
+    createClassInfo(name, flags, clasp);
+}
+
+static void
+allocHeapThing(address thing, AllocKind kind)
+{
+    uint64_t allocTime = allocCount++;
+    tenuredThings.emplace(thing, AllocInfo(allocTime, kind, TenuredHeap));
+}
+
+static void
+allocNurseryThing(address thing, AllocKind kind)
+{
+    uint64_t allocTime = allocCount++;
+    nurseryThings.emplace(thing, AllocInfo(allocTime, kind, Nursery));
+}
+
+static void
+setObjectClass(address obj, address clasp)
+{
+    auto i = classMap.find(clasp);
+    assert(i != classMap.end());
+    ClassId id = i->second;
+    assert(id < classes.size());
+
+    auto j = nurseryThings.find(obj);
+    if (j == nurseryThings.end()) {
+        j = tenuredThings.find(obj);
+        if (j == tenuredThings.end())
+            die("Can't find allocation for object %p", obj);
+    }
+    j->second.classId = id;
+}
+
+static void
+promoteToTenured(address src, address dst)
+{
+    auto i = nurseryThings.find(src);
+    assert(i != nurseryThings.end());
+    AllocInfo alloc = i->second;
+    tenuredThings.emplace(dst, alloc);
+    nurseryThings.erase(i);
+}
+
+static void
+finalizeThing(const AllocInfo &info)
+{
+    processAlloc(info, allocCount);
+}
+
+static void
+sweepNursery()
+{
+    for (auto i = nurseryThings.begin(); i != nurseryThings.end(); ++i) {
+        finalizeThing(i->second);
+    }
+    nurseryThings.clear();
+}
+
+static void
+finalizeTenuredThing(address thing)
+{
+    auto i = tenuredThings.find(thing);
+    assert(i != tenuredThings.end());
+    finalizeThing(i->second);
+    tenuredThings.erase(i);
+}
+
+static void
+updateTimeslices(std::vector<uint64_t> &data, uint64_t lastTime, uint64_t currentTime, uint64_t value)
+{
+    unsigned firstSlice = (lastTime / timesliceSize) + 1;
+    unsigned lastSlice = currentTime / timesliceSize;
+    for (unsigned i = firstSlice; i <= lastSlice; ++i)
+        data[i] = value;
+}
+
+static void
+processTraceFile(const char *filename)
+{
+    FILE *file;
+    file = fopen(filename, "r");
+    if (!file)
+        die("Can't read file: %s", filename);
+
+    // Get a conservative estimate of the total number of allocations so we can
+    // allocate buffers in advance.
+    fseek(file, 0, SEEK_END);
+    size_t length = ftell(file);
+    fseek(file, 0, SEEK_SET);
+    size_t maxTraces = length / sizeof(uint64_t);
+
+    uint64_t trace;
+    if (!readTrace(file, trace))
+        die("Empty input file");
+    if (getTraceEvent(trace) != TraceEventInit)
+        die("Can't parse input file");
+    if (getTraceExtra(trace) != TraceFormatVersion)
+        die("Unexpected format version %d", getTraceExtra(trace));
+    for (unsigned kind = 0; kind < AllocKinds; ++kind)
+        thingSizes[kind] = expectTrace(file, TraceEventThingSize);
+
+    timesliceSize = 1000;
+    while ((maxTraces / timesliceSize ) > 1000)
+        timesliceSize *= 2;
+
+    size_t maxTimeslices = maxTraces / timesliceSize;
+    allocTotalsBySlice.reserve(maxTimeslices);
+    gcBytesAllocatedInSlice.reserve(maxTimeslices);
+    gcBytesFreedInSlice.reserve(maxTimeslices);
+    lifetimeBins = getBin(maxTraces) + 1;
+    assert(lifetimeBins <= MaxLifetimeBins);
+
+    createClassInfo("unknown", 0);
+
+    State state = StateMutator;
+    while (readTrace(file, trace)) {
+        GCTraceEvent event = getTraceEvent(trace);
+        switch (event) {
+      case TraceEventNurseryAlloc:
+          assert(state == StateMutator);
+          allocNurseryThing(getTracePayload(trace), getTraceExtra(trace));
+          break;
+      case TraceEventTenuredAlloc:
+          assert(state == StateMutator);
+          allocHeapThing(getTracePayload(trace), getTraceExtra(trace));
+          break;
+      case TraceEventClassInfo:
+          assert(state == StateMutator);
+          readClassInfo(file, getTracePayload(trace));
+          break;
+      case TraceEventCreateObject:
+          assert(state == StateMutator);
+          setObjectClass(getTracePayload(trace), expectDataAddress(file));
+          break;
+      case TraceEventMinorGCStart:
+          assert(state == StateMutator);
+          state = StateMinorGC;
+          break;
+      case TraceEventPromoteToTenured:
+          assert(state == StateMinorGC);
+          promoteToTenured(getTracePayload(trace), expectDataAddress(file));
+          break;
+      case TraceEventMinorGCEnd:
+          assert(state == StateMinorGC);
+          sweepNursery();
+          state = StateMutator;
+          break;
+      case TraceEventMajorGCStart:
+          assert(state == StateMutator);
+          state = StateMajorGC;
+          break;
+      case TraceEventTenuredFinalize:
+          assert(state == StateMajorGC);
+          finalizeTenuredThing(getTracePayload(trace));
+          break;
+      case TraceEventMajorGCEnd:
+          assert(state == StateMajorGC);
+          state = StateMutator;
+          break;
+      default:
+          assert(false);
+          die("Unexpected trace event %d", event);
+          break;
+        }
+    }
+
+    // Correct number of lifetime bins now we know the real allocation count.
+    assert(allocCount < maxTraces);
+    lifetimeBins = getBin(allocCount) + 1;
+    assert(lifetimeBins <= MaxLifetimeBins);
+
+    fclose(file);
+}
+
+template <class func>
+void withOutputFile(const char *base, const char *name, func f)
+{
+    const size_t bufSize = 256;
+    char filename[bufSize];
+    int r = snprintf(filename, bufSize, "%s%s", base, name);
+    assert(r > 0 && r < bufSize);
+
+    FILE *file = fopen(filename, "w");
+    if (!file)
+        die("Can't write to %s", filename);
+    f(file);
+    fclose(file);
+}
+
+int
+main(int argc, const char *argv[])
+{
+    testBinning();
+
+    if (argc != 3)
+        die("usage: gctrace INPUT_FILE OUTPUT_BASE");
+    const char* inputFile = argv[1];
+    const char* outputBase = argv[2];
+
+    processTraceFile(inputFile);
+
+    using namespace std::placeholders;
+    withOutputFile(outputBase, "-bytesAllocatedBySlice.csv", outputGcBytesAllocated);
+    withOutputFile(outputBase, "-bytesUsedBySlice.csv", outputGcBytesUsed);
+    withOutputFile(outputBase, "-thingCounts.csv", outputThingCounts);
+    withOutputFile(outputBase, "-objectCounts.csv", outputObjectCounts);
+    withOutputFile(outputBase, "-lifetimeByClassForNursery.csv",
+                   std::bind(outputLifetimeByClass, _1, Nursery));
+    withOutputFile(outputBase, "-lifetimeByKindForHeap.csv",
+                   std::bind(outputLifetimeByKind, _1, TenuredHeap));
+    withOutputFile(outputBase, "-lifetimeByHeap.csv", outputLifetimeByHeap);
+    withOutputFile(outputBase, "-lifetimeBySlice.csv", outputLifetimeBySlice);
+    return 0;
+}
--- a/js/src/devtools/rootAnalysis/analyzeRoots.js
+++ b/js/src/devtools/rootAnalysis/analyzeRoots.js
@@ -80,49 +80,80 @@ function expressionUsesVariable(exp, var
         return false;
     for (var childExp of exp.Exp) {
         if (expressionUsesVariable(childExp, variable))
             return true;
     }
     return false;
 }
 
-function edgeUsesVariable(edge, variable)
+// Detect simple |return nullptr;| statements.
+function isReturningImmobileValue(edge, variable)
+{
+    if (variable.Kind == "Return") {
+        if (edge.Exp[0].Kind == "Var" && sameVariable(edge.Exp[0].Variable, variable)) {
+            if (edge.Exp[1].Kind == "Int" && edge.Exp[1].String == "0") {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+// If the edge uses the given variable, return the earliest point at which the
+// use is definite. Usually, that means the source of the edge (anything that
+// reaches that source point will end up using the variable, but there may be
+// other ways to reach the destination of the edge.)
+//
+// Return values are implicitly used at the very last point in the function.
+// This makes a difference: if an RAII class GCs in its destructor, we need to
+// start looking at the final point in the function, not one point back from
+// that, since that would skip over the GCing call.
+//
+function edgeUsesVariable(edge, variable, body)
 {
     if (ignoreEdgeUse(edge, variable))
-        return false;
+        return 0;
+
+    if (variable.Kind == "Return" && body.Index[1] == edge.Index[1] && body.BlockId.Kind == "Function")
+        return edge.Index[1]; // Last point in function body uses the return value.
+
+    var src = edge.Index[0];
+
     switch (edge.Kind) {
 
     case "Assign":
+        if (isReturningImmobileValue(edge, variable))
+            return 0;
         if (expressionUsesVariable(edge.Exp[0], variable))
-            return true;
-        return expressionUsesVariable(edge.Exp[1], variable);
+            return src;
+        return expressionUsesVariable(edge.Exp[1], variable) ? src : 0;
 
     case "Assume":
-        return expressionUsesVariable(edge.Exp[0], variable);
+        return expressionUsesVariable(edge.Exp[0], variable) ? src : 0;
 
     case "Call":
         if (expressionUsesVariable(edge.Exp[0], variable))
-            return true;
+            return src;
         if (1 in edge.Exp && expressionUsesVariable(edge.Exp[1], variable))
-            return true;
+            return src;
         if ("PEdgeCallInstance" in edge) {
             if (expressionUsesVariable(edge.PEdgeCallInstance.Exp, variable))
-                return true;
+                return src;
         }
         if ("PEdgeCallArguments" in edge) {
             for (var exp of edge.PEdgeCallArguments.Exp) {
                 if (expressionUsesVariable(exp, variable))
-                    return true;
+                    return src;
             }
         }
-        return false;
+        return 0;
 
     case "Loop":
-        return false;
+        return 0;
 
     default:
         assert(false);
     }
 }
 
 function expressionIsVariableAddress(exp, variable)
 {
@@ -154,17 +185,17 @@ function edgeTakesVariableAddress(edge, 
 }
 
 function edgeKillsVariable(edge, variable)
 {
     // Direct assignments kill their lhs.
     if (edge.Kind == "Assign") {
         var lhs = edge.Exp[0];
         if (lhs.Kind == "Var" && sameVariable(lhs.Variable, variable))
-            return true;
+            return !isReturningImmobileValue(edge, variable);
     }
 
     if (edge.Kind != "Call")
         return false;
 
     // Assignments of call results kill their lhs.
     if (1 in edge.Exp) {
         var lhs = edge.Exp[1];
@@ -219,17 +250,17 @@ function edgeCanGC(edge)
 {
     if (edge.Kind != "Call")
         return false;
     var callee = edge.Exp[0];
     if (callee.Kind == "Var") {
         var variable = callee.Variable;
         assert(variable.Kind == "Func");
         var callee = mangled(variable.Name[0]);
-        if (callee in gcFunctions)
+        if ((callee in gcFunctions) || ((callee + internalMarker) in gcFunctions))
             return "'" + variable.Name[0] + "'";
         return null;
     }
     assert(callee.Kind == "Drf");
     if (callee.Exp[0].Kind == "Fld") {
         var field = callee.Exp[0].Field;
         var csuName = field.FieldCSU.Type.Name;
         var fullFieldName = csuName + "." + field.Name[0];
@@ -237,21 +268,21 @@ function edgeCanGC(edge)
             return null;
         return (fullFieldName in suppressedFunctions) ? null : fullFieldName;
     }
     assert(callee.Exp[0].Kind == "Var");
     var varName = callee.Exp[0].Variable.Name[0];
     return indirectCallCannotGC(functionName, varName) ? null : "*" + varName;
 }
 
-function variableUseFollowsGC(suppressed, variable, worklist)
+function variableUsePrecedesGC(suppressed, variable, worklist)
 {
-    // Scan through all edges following an unrooted variable use, using an
-    // explicit worklist. A worklist contains a following edge together with a
-    // description of where one of its predecessors GC'd (if any).
+    // Scan through all edges preceding an unrooted variable use, using an
+    // explicit worklist. A worklist contains an incoming edge together with a
+    // description of where it or one of its successors GC'd (if any).
 
     while (worklist.length) {
         var entry = worklist.pop();
         var body = entry.body, ppoint = entry.ppoint;
 
         if (body.seen) {
             if (ppoint in body.seen) {
                 var seenEntry = body.seen[ppoint];
@@ -276,43 +307,45 @@ function variableUseFollowsGC(suppressed
                                 worklist.push({body:xbody, ppoint:parent.Index,
                                                gcInfo:entry.gcInfo, why:entry});
                             }
                         }
                         assert(found);
                     }
                 }
             } else if (variable.Kind == "Arg" && entry.gcInfo) {
+                // The scope of arguments starts at the beginning of the
+                // function
                 return {gcInfo:entry.gcInfo, why:entry};
             }
         }
 
         var predecessors = getPredecessors(body);
         if (!(ppoint in predecessors))
             continue;
 
         for (var edge of predecessors[ppoint]) {
             var source = edge.Index[0];
 
             if (edgeKillsVariable(edge, variable)) {
                 if (entry.gcInfo)
-                    return {gcInfo:entry.gcInfo, why:entry};
+                    return {gcInfo: entry.gcInfo, why: {body:body, ppoint:source, gcInfo:entry.gcInfo, why:entry } }
                 if (!body.minimumUse || source < body.minimumUse)
                     body.minimumUse = source;
                 continue;
             }
 
             var gcInfo = entry.gcInfo;
             if (!gcInfo && !(source in body.suppressed) && !suppressed) {
                 var gcName = edgeCanGC(edge, body);
                 if (gcName)
                     gcInfo = {name:gcName, body:body, ppoint:source};
             }
 
-            if (edgeUsesVariable(edge, variable)) {
+            if (edgeUsesVariable(edge, variable, body)) {
                 if (gcInfo)
                     return {gcInfo:gcInfo, why:entry};
                 if (!body.minimumUse || source < body.minimumUse)
                     body.minimumUse = source;
             }
 
             if (edge.Kind == "Loop") {
                 // propagate to exit points of the loop body, in addition to the
@@ -345,21 +378,25 @@ function variableLiveAcrossGC(suppressed
         body.seen = null;
         body.minimumUse = 0;
     }
 
     for (var body of functionBodies) {
         if (!("PEdge" in body))
             continue;
         for (var edge of body.PEdge) {
-            if (edgeUsesVariable(edge, variable) && !edgeKillsVariable(edge, variable)) {
-                var worklist = [{body:body, ppoint:edge.Index[0], gcInfo:null, why:null}];
-                var call = variableUseFollowsGC(suppressed, variable, worklist);
-                if (call)
+            var usePoint = edgeUsesVariable(edge, variable, body);
+            if (usePoint && !edgeKillsVariable(edge, variable)) {
+                // Found a use, possibly after a GC.
+                var worklist = [{body:body, ppoint:usePoint, gcInfo:null, why:null}];
+                var call = variableUsePrecedesGC(suppressed, variable, worklist);
+                if (call) {
+                    call.afterGCUse = usePoint;
                     return call;
+                }
             }
         }
     }
     return null;
 }
 
 // An unrooted variable has its address stored in another variable via
 // assignment, or passed into a function that can GC. If the address is
@@ -387,18 +424,17 @@ function computePrintedLines(functionNam
     assert(!system("xdbfind src_body.xdb '" + functionName + "' > " + tmpfile));
     var lines = snarf(tmpfile).split('\n');
 
     for (var body of functionBodies)
         body.lines = [];
 
     // Distribute lines of output to the block they originate from.
     var currentBody = null;
-    for (var i = 0; i < lines.length; i++) {
-        var line = lines[i];
+    for (var line of lines) {
         if (/^block:/.test(line)) {
             if (match = /:(loop#[\d#]+)/.exec(line)) {
                 var loop = match[1];
                 var found = false;
                 for (var body of functionBodies) {
                     if (body.BlockId.Kind == "Loop" && body.BlockId.Loop == loop) {
                         assert(!found);
                         found = true;
@@ -431,47 +467,58 @@ function locationLine(text)
 {
     if (match = /:(\d+)$/.exec(text))
         return match[1];
     return 0;
 }
 
 function printEntryTrace(functionName, entry)
 {
+    var gcPoint = ('gcInfo' in entry) ? entry.gcInfo.ppoint : 0;
+
     if (!functionBodies[0].lines)
         computePrintedLines(functionName);
 
     while (entry) {
         var ppoint = entry.ppoint;
         var lineText = findLocation(entry.body, ppoint);
 
-        var edgeText = null;
+        var edgeText = "";
         if (entry.why && entry.why.body == entry.body) {
             // If the next point in the trace is in the same block, look for an edge between them.
             var next = entry.why.ppoint;
-            for (var line of entry.body.lines) {
-                if (match = /\((\d+),(\d+),/.exec(line)) {
-                    if (match[1] == ppoint && match[2] == next)
-                        edgeText = line; // May be multiple
+
+            if (!entry.body.edgeTable) {
+                var table = {};
+                entry.body.edgeTable = table;
+                for (var line of entry.body.lines) {
+                    if (match = /\((\d+),(\d+),/.exec(line))
+                        table[match[1] + "," + match[2]] = line; // May be multiple?
                 }
             }
+
+            edgeText = entry.body.edgeTable[ppoint + "," + next];
             assert(edgeText);
+            if (ppoint == gcPoint)
+                edgeText += " [[GC call]]";
         } else {
             // Look for any outgoing edge from the chosen point.
             for (var line of entry.body.lines) {
                 if (match = /\((\d+),/.exec(line)) {
                     if (match[1] == ppoint) {
                         edgeText = line;
                         break;
                     }
                 }
             }
+            if (ppoint == entry.body.Index[1] && entry.body.BlockId.Kind == "Function")
+                edgeText += " [[end of function]]";
         }
 
-        print("    " + lineText + (edgeText ? ": " + edgeText : ""));
+        print("    " + lineText + (edgeText.length ? ": " + edgeText : ""));
         entry = entry.why;
     }
 }
 
 function isRootedType(type)
 {
     return type.Kind == "CSU" && isRootedTypeName(type.Name);
 }
@@ -494,23 +541,24 @@ function typeDesc(type)
 }
 
 function processBodies(functionName)
 {
     if (!("DefineVariable" in functionBodies[0]))
         return;
     var suppressed = (mangled(functionName) in suppressedFunctions);
     for (var variable of functionBodies[0].DefineVariable) {
-        if (variable.Variable.Kind == "Return")
-            continue;
         var name;
         if (variable.Variable.Kind == "This")
             name = "this";
+        else if (variable.Variable.Kind == "Return")
+            name = "<returnvalue>";
         else
             name = variable.Variable.Name[0];
+
         if (isRootedType(variable.Type)) {
             if (!variableLiveAcrossGC(suppressed, variable.Variable)) {
                 // The earliest use of the variable should be its constructor.
                 var lineText;
                 for (var body of functionBodies) {
                     if (body.minimumUse) {
                         var text = findLocation(body, body.minimumUse);
                         if (!lineText || locationLine(lineText) > locationLine(text))
@@ -552,25 +600,41 @@ xdb.open("src_body.xdb");
 var minStream = xdb.min_data_stream()|0;
 var maxStream = xdb.max_data_stream()|0;
 
 var N = (maxStream - minStream) + 1;
 var each = Math.floor(N/numBatches);
 var start = minStream + each * (batch - 1);
 var end = Math.min(minStream + each * batch - 1, maxStream);
 
+// For debugging: Set this variable to the function name you're interested in
+// debugging and run once. That will print out the nameIndex of that function.
+// Insert that into the following statement to go directly to just that
+// function. Add your debugging printouts or debugger; statements or whatever.
+var theFunctionNameToFind;
+// var start = end = 12345;
+
 for (var nameIndex = start; nameIndex <= end; nameIndex++) {
     var name = xdb.read_key(nameIndex);
     var functionName = name.readString();
     var data = xdb.read_entry(name);
     xdb.free_string(name);
     var json = data.readString();
     xdb.free_string(data);
     functionBodies = JSON.parse(json);
 
+    if (theFunctionNameToFind) {
+        if (functionName == theFunctionNameToFind) {
+            printErr("nameIndex = " + nameIndex);
+            quit(1);
+        } else {
+            continue;
+        }
+    }
+
     for (var body of functionBodies)
         body.suppressed = [];
     for (var body of functionBodies) {
         for (var [pbody, id] of allRAIIGuardedCallPoints(body, isSuppressConstructor))
             pbody.suppressed[id] = true;
     }
     processBodies(functionName);
 }
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -243,11 +243,17 @@ function isOverridableField(initialCSU, 
     if (field == 'GetNativeContext')
         return false;
     if (field == "GetGlobalJSObject")
         return false;
     if (field == "GetIsMainThread")
         return false;
     if (initialCSU == 'nsIXPConnectJSObjectHolder' && field == 'GetJSObject')
         return false;
+    if (initialCSU == 'nsIXPConnect' && field == 'GetSafeJSContext')
+        return false;
+    if (initialCSU == 'nsIScriptContext') {
+        if (field == 'GetWindowProxy' || field == 'GetWindowProxyPreserveColor')
+            return false;
+    }
 
     return true;
 }
--- a/js/src/devtools/rootAnalysis/computeCallgraph.js
+++ b/js/src/devtools/rootAnalysis/computeCallgraph.js
@@ -271,11 +271,27 @@ for (var nameIndex = minStream; nameInde
 
     seenCallees = {};
     seenSuppressedCallees = {};
 
     var functionName = name.readString();
     for (var body of functionBodies)
         processBody(functionName, body);
 
+    // GCC generates multiple constructors and destructors ("in-charge" and
+    // "not-in-charge") to handle virtual base classes. They are normally
+    // identical, and it appears that GCC does some magic to alias them to the
+    // same thing. But this aliasing is not visible to the analysis. So we'll
+    // add a dummy call edge from "foo" -> "foo *INTERNAL* ", since only "foo"
+    // will show up as called but only "foo *INTERNAL* " will be emitted in the
+    // case where the constructors are identical.
+    //
+    // This is slightly conservative in the case where they are *not*
+    // identical, but that should be rare enough that we don't care.
+    var markerPos = functionName.indexOf(internalMarker);
+    if (markerPos > 0) {
+        var inChargeXTor = functionName.substr(0, markerPos) + functionName.substr(markerPos + internalMarker.length);
+        print("D " + memo(inChargeXTor) + " " + memo(functionName));
+    }
+
     xdb.free_string(name);
     xdb.free_string(data);
 }
--- a/js/src/devtools/rootAnalysis/utility.js
+++ b/js/src/devtools/rootAnalysis/utility.js
@@ -1,12 +1,16 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
 
 "use strict";
 
+// gcc appends this to mangled function names for "not in charge"
+// constructors/destructors.
+var internalMarker = " *INTERNAL* ";
+
 function assert(x, msg)
 {
     if (x)
         return;
     debugger;
     if (msg)
         throw "assertion failed: " + msg + "\n" + (Error().stack);
     else
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -314,18 +314,19 @@ class GCRuntime
     void requestInterrupt(JS::gcreason::Reason reason);
     bool gcCycle(bool incremental, int64_t budget, JSGCInvocationKind gckind,
                  JS::gcreason::Reason reason);
     void budgetIncrementalGC(int64_t *budget);
     void resetIncrementalGC(const char *reason);
     void incrementalCollectSlice(int64_t budget, JS::gcreason::Reason reason,
                                  JSGCInvocationKind gckind);
     void pushZealSelectedObjects();
-    bool beginMarkPhase();
-    bool shouldPreserveJITCode(JSCompartment *comp, int64_t currentTime);
+    bool beginMarkPhase(JS::gcreason::Reason reason);
+    bool shouldPreserveJITCode(JSCompartment *comp, int64_t currentTime,
+                               JS::gcreason::Reason reason);
     void bufferGrayRoots();
     bool drainMarkStack(SliceBudget &sliceBudget, gcstats::Phase phase);
     template <class CompartmentIterT> void markWeakReferences(gcstats::Phase phase);
     void markWeakReferencesInCurrentGroup(gcstats::Phase phase);
     template <class ZoneIterT, class CompartmentIterT> void markGrayReferences();
     void markGrayReferencesInCurrentGroup();
     void beginSweepPhase(bool lastGC);
     void findZoneGroups();
new file mode 100644
--- /dev/null
+++ b/js/src/gc/GCTrace.cpp
@@ -0,0 +1,195 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifdef JS_GC_TRACE
+
+#include "gc/GCTrace.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "gc/GCTraceFormat.h"
+
+#include "js/HashTable.h"
+
+using namespace js;
+using namespace js::gc;
+
+JS_STATIC_ASSERT(AllocKinds == FINALIZE_LIMIT);
+JS_STATIC_ASSERT(LastObjectAllocKind == FINALIZE_OBJECT_LAST);
+
+static FILE *gcTraceFile = nullptr;
+
+static HashSet<const Class *, DefaultHasher<const Class *>, SystemAllocPolicy> tracedClasses;
+
+static inline void
+WriteWord(uint64_t data)
+{
+    if (gcTraceFile)
+        fwrite(&data, sizeof(data), 1, gcTraceFile);
+}
+
+static inline void
+TraceEvent(GCTraceEvent event, uint64_t payload = 0, uint8_t extra = 0)
+{
+    JS_ASSERT(event < GCTraceEventCount);
+    JS_ASSERT((payload >> TracePayloadBits) == 0);
+    WriteWord((uint64_t(event) << TraceEventShift) |
+               (uint64_t(extra) << TraceExtraShift) | payload);
+}
+
+static inline void
+TraceAddress(const void *p)
+{
+    TraceEvent(TraceDataAddress, uint64_t(p));
+}
+
+static inline void
+TraceInt(uint32_t data)
+{
+    TraceEvent(TraceDataInt, data);
+}
+
+static void
+TraceString(const char* string)
+{
+    JS_STATIC_ASSERT(sizeof(char) == 1);
+
+    size_t length = strlen(string);
+    const unsigned charsPerWord = sizeof(uint64_t);
+    unsigned wordCount = (length + charsPerWord - 1) / charsPerWord;
+
+    TraceEvent(TraceDataString, length);
+    for (unsigned i = 0; i < wordCount; ++i) {
+        union
+        {
+            uint64_t word;
+            char chars[charsPerWord];
+        } data;
+        strncpy(data.chars, string + (i * charsPerWord), charsPerWord);
+        WriteWord(data.word);
+    }
+}
+
+bool
+js::gc::InitTrace(GCRuntime &gc)
+{
+    char *filename = getenv("JS_GC_TRACE");
+    if (!filename)
+        return true;
+
+    if (!tracedClasses.init())
+        return false;
+
+    /* This currently does not support multiple runtimes. */
+    JS_ASSERT(!gcTraceFile);
+
+    gcTraceFile = fopen(filename, "w");
+    if (!gcTraceFile)
+        return false;
+
+    TraceEvent(TraceEventInit, 0, TraceFormatVersion);
+
+    /* Trace information about thing sizes. */
+    for (unsigned kind = 0; kind < FINALIZE_LIMIT; ++kind)
+        TraceEvent(TraceEventThingSize, Arena::thingSize((AllocKind)kind));
+
+    return true;
+}
+
+void
+js::gc::FinishTrace()
+{
+    if (gcTraceFile)
+        fclose(gcTraceFile);
+}
+
+bool
+js::gc::TraceEnabled()
+{
+    return gcTraceFile != nullptr;
+}
+
+void
+js::gc::TraceNurseryAlloc(Cell *thing, size_t size)
+{
+    if (thing) {
+        /* We don't have AllocKind here, but we can work it out from size. */
+        unsigned slots = (size - sizeof(JSObject)) / sizeof(JS::Value);
+        AllocKind kind = GetBackgroundAllocKind(GetGCObjectKind(slots));
+        TraceEvent(TraceEventNurseryAlloc, uint64_t(thing), kind);
+    }
+}
+
+void
+js::gc::TraceTenuredAlloc(Cell *thing, AllocKind kind)
+{
+    if (thing)
+        TraceEvent(TraceEventTenuredAlloc, uint64_t(thing), kind);
+}
+
+static void
+MaybeTraceClass(const Class *clasp)
+{
+    if (tracedClasses.has(clasp))
+        return;
+
+    TraceEvent(TraceEventClassInfo, uint64_t(clasp));
+    TraceInt(clasp->flags);
+    TraceString(clasp->name);
+    tracedClasses.put(clasp);
+}
+
+void
+js::gc::TraceCreateObject(JSObject* object)
+{
+    if (!gcTraceFile)
+        return;
+
+    const Class *clasp = object->type()->clasp();
+    MaybeTraceClass(clasp);
+    TraceEvent(TraceEventCreateObject, uint64_t(object));
+    TraceAddress(clasp);
+}
+
+void
+js::gc::TraceMinorGCStart()
+{
+    TraceEvent(TraceEventMinorGCStart);
+}
+
+void
+js::gc::TracePromoteToTenured(Cell *src, Cell *dst)
+{
+    TraceEvent(TraceEventPromoteToTenured, uint64_t(src));
+    TraceAddress(dst);
+}
+
+void
+js::gc::TraceMinorGCEnd()
+{
+    TraceEvent(TraceEventMinorGCEnd);
+}
+
+void
+js::gc::TraceMajorGCStart()
+{
+    TraceEvent(TraceEventMajorGCStart);
+}
+
+void
+js::gc::TraceTenuredFinalize(Cell *thing)
+{
+    TraceEvent(TraceEventTenuredFinalize, uint64_t(thing));
+}
+
+void
+js::gc::TraceMajorGCEnd()
+{
+    TraceEvent(TraceEventMajorGCEnd);
+}
+
+#endif
new file mode 100644
--- /dev/null
+++ b/js/src/gc/GCTrace.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef gc_GCTrace_h
+#define gc_GCTrace_h
+
+#include "gc/Heap.h"
+
+namespace js {
+
+namespace types { struct TypeObject; }
+
+namespace gc {
+
+#ifdef JS_GC_TRACE
+
+extern bool InitTrace(GCRuntime &gc);
+extern void FinishTrace();
+extern bool TraceEnabled();
+extern void TraceNurseryAlloc(Cell *thing, size_t size);
+extern void TraceTenuredAlloc(Cell *thing, AllocKind kind);
+extern void TraceCreateObject(JSObject* object);
+extern void TraceMinorGCStart();
+extern void TracePromoteToTenured(Cell *src, Cell *dst);
+extern void TraceMinorGCEnd();
+extern void TraceMajorGCStart();
+extern void TraceTenuredFinalize(Cell *thing);
+extern void TraceMajorGCEnd();
+
+#else
+
+inline bool InitTrace(GCRuntime &gc) { return true; }
+inline void FinishTrace() {}
+inline bool TraceEnabled() { return false; }
+inline void TraceNurseryAlloc(Cell *thing, size_t size) {}
+inline void TraceTenuredAlloc(Cell *thing, AllocKind kind) {}
+inline void TraceCreateObject(JSObject* object) {}
+inline void TraceMinorGCStart() {}
+inline void TracePromoteToTenured(Cell *src, Cell *dst) {}
+inline void TraceMinorGCEnd() {}
+inline void TraceMajorGCStart() {}
+inline void TraceTenuredFinalize(Cell *thing) {}
+inline void TraceMajorGCEnd() {}
+
+#endif
+
+} /* namespace gc */
+} /* namespace js */
+
+#endif
new file mode 100644
--- /dev/null
+++ b/js/src/gc/GCTraceFormat.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef gc_GCTraceFormat_h
+#define gc_GCTraceFormat_h
+
+/*
+ * Each trace is stored as a 64-bit word with the following format:
+ *
+ *                 56              48                                         0
+ * +----------------+----------------+-----------------------------------------+
+ * | Event type     | Optional extra | Optional payload                        |
+ * +----------------+----------------+-----------------------------------------+
+ */
+
+enum GCTraceEvent {
+    // Events
+    TraceEventInit,
+    TraceEventThingSize,
+    TraceEventNurseryAlloc,
+    TraceEventTenuredAlloc,
+    TraceEventClassInfo,
+    TraceEventCreateObject,
+    TraceEventMinorGCStart,
+    TraceEventPromoteToTenured,
+    TraceEventMinorGCEnd,
+    TraceEventMajorGCStart,
+    TraceEventTenuredFinalize,
+    TraceEventMajorGCEnd,
+
+    TraceDataAddress,  // following TraceEventPromote
+    TraceDataInt,      // following TraceEventClassInfo
+    TraceDataString,   // following TraceEventClassInfo
+
+    GCTraceEventCount
+};
+
+const unsigned TraceFormatVersion = 1;
+
+const unsigned TracePayloadBits = 48;
+
+const unsigned TraceExtraShift = 48;
+const unsigned TraceExtraBits = 8;
+
+const unsigned TraceEventShift = 56;
+const unsigned TraceEventBits = 8;
+
+const unsigned AllocKinds = 22;
+const unsigned LastObjectAllocKind = 11;
+
+#endif
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -160,16 +160,17 @@ js::Nursery::allocateObject(JSContext *c
     JS_ASSERT(size >= sizeof(RelocationOverlay));
 
     /* Attempt to allocate slots contiguously after object, if possible. */
     if (numDynamic && numDynamic <= MaxNurserySlots) {
         size_t totalSize = size + sizeof(HeapSlot) * numDynamic;
         JSObject *obj = static_cast<JSObject *>(allocate(totalSize));
         if (obj) {
             obj->setInitialSlots(reinterpret_cast<HeapSlot *>(size_t(obj) + size));
+            TraceNurseryAlloc(obj, size);
             return obj;
         }
         /* If we failed to allocate as a block, retry with out-of-line slots. */
     }
 
     HeapSlot *slots = nullptr;
     if (numDynamic) {
         slots = allocateHugeSlots(cx, numDynamic);
@@ -179,16 +180,17 @@ js::Nursery::allocateObject(JSContext *c
 
     JSObject *obj = static_cast<JSObject *>(allocate(size));
 
     if (obj)
         obj->setInitialSlots(slots);
     else
         freeSlots(cx, slots);
 
+    TraceNurseryAlloc(obj, size);
     return obj;
 }
 
 void *
 js::Nursery::allocate(size_t size)
 {
     JS_ASSERT(isEnabled());
     JS_ASSERT(!runtime()->isHeapBusy());
@@ -568,16 +570,17 @@ js::Nursery::moveToTenured(MinorCollecti
         CrashAtUnhandlableOOM("Failed to allocate object while tenuring.");
 
     trc->tenuredSize += moveObjectToTenured(dst, src, dstKind);
 
     RelocationOverlay *overlay = reinterpret_cast<RelocationOverlay *>(src);
     overlay->forwardTo(dst);
     trc->insertIntoFixupList(overlay);
 
+    TracePromoteToTenured(src, dst);
     return static_cast<void *>(dst);
 }
 
 size_t
 js::Nursery::moveObjectToTenured(JSObject *dst, JSObject *src, AllocKind dstKind)
 {
     size_t srcSize = Arena::thingSize(dstKind);
     size_t tenuredSize = srcSize;
@@ -759,16 +762,18 @@ js::Nursery::collect(JSRuntime *rt, JS::
          * may be freed after this point.
          */
         sb.clear();
         return;
     }
 
     rt->gc.stats.count(gcstats::STAT_MINOR_GC);
 
+    TraceMinorGCStart();
+
     TIME_START(total);
 
     AutoStopVerifyingBarriers av(rt, false);
 
     // Move objects pointed to by roots from the nursery to the major heap.
     MinorCollectionTracer trc(rt, this);
 
     // Mark the store buffer. This must happen first.
@@ -879,16 +884,18 @@ js::Nursery::collect(JSRuntime *rt, JS::
     // We ignore gcMaxBytes when allocating for minor collection. However, if we
     // overflowed, we disable the nursery. The next time we allocate, we'll fail
     // because gcBytes >= gcMaxBytes.
     if (rt->gc.bytes >= rt->gc.maxBytes)
         disable();
 
     TIME_END(total);
 
+    TraceMinorGCEnd();
+
 #ifdef PROFILE_NURSERY
     int64_t totalTime = TIME_TOTAL(total);
 
     if (totalTime >= GCReportThreshold) {
         static bool printedHeader = false;
         if (!printedHeader) {
             fprintf(stderr,
                     "MinorGC: Reason               PRate  Size Time   mkVals mkClls mkSlts mkWCll mkRVal mkRCll mkGnrc ckTbls mkRntm mkDbgr clrNOC collct swpABO updtIn resize pretnr frSlts clrSB  sweep\n");
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -96,51 +96,51 @@ MarkExactStackRootList(JSTracer *trc, So
     while (rooter) {
         T *addr = rooter->address();
         if (!IgnoreExactRoot(addr))
             MarkFunc(trc, addr, name);
         rooter = rooter->previous();
     }
 }
 
-template <class T, void (MarkFunc)(JSTracer *trc, T *ref, const char *name)>
-static inline void
-MarkExactStackRootsForType(JSRuntime* rt, JSTracer *trc, const char *name = nullptr)
+template<class T>
+static void
+MarkExactStackRootsAcrossTypes(T context, JSTracer *trc)
 {
-    for (ContextIter cx(rt); !cx.done(); cx.next())
-        MarkExactStackRootList<T, MarkFunc>(trc, cx.get(), name);
-    MarkExactStackRootList<T, MarkFunc>(trc, &rt->mainThread, name);
-}
-
-template <class T, void (MarkFunc)(JSTracer *trc, T *ref, const char *name)>
-static inline void
-MarkExactStackRootsForType(ThreadSafeContext* cx, JSTracer *trc, const char *name = nullptr)
-{
-    MarkExactStackRootList<T, MarkFunc>(trc, cx->perThreadData, name);
+    MarkExactStackRootList<JSObject *, MarkObjectRoot>(trc, context, "exact-object");
+    MarkExactStackRootList<Shape *, MarkShapeRoot>(trc, context, "exact-shape");
+    MarkExactStackRootList<BaseShape *, MarkBaseShapeRoot>(trc, context, "exact-baseshape");
+    MarkExactStackRootList<types::TypeObject *, MarkTypeObjectRoot>(
+        trc, context, "exact-typeobject");
+    MarkExactStackRootList<JSString *, MarkStringRoot>(trc, context, "exact-string");
+    MarkExactStackRootList<JS::Symbol *, MarkSymbolRoot>(trc, context, "exact-symbol");
+    MarkExactStackRootList<jit::JitCode *, MarkJitCodeRoot>(trc, context, "exact-jitcode");
+    MarkExactStackRootList<JSScript *, MarkScriptRoot>(trc, context, "exact-script");
+    MarkExactStackRootList<LazyScript *, MarkLazyScriptRoot>(trc, context, "exact-lazy-script");
+    MarkExactStackRootList<jsid, MarkIdRoot>(trc, context, "exact-id");
+    MarkExactStackRootList<Value, MarkValueRoot>(trc, context, "exact-value");
+    MarkExactStackRootList<types::Type, MarkTypeRoot>(trc, context, "types::Type");
+    MarkExactStackRootList<Bindings, MarkBindingsRoot>(trc, context, "Bindings");
+    MarkExactStackRootList<JSPropertyDescriptor, MarkPropertyDescriptorRoot>(
+        trc, context, "JSPropertyDescriptor");
+    MarkExactStackRootList<PropDesc, MarkPropDescRoot>(trc, context, "PropDesc");
 }
 
-template <class T>
 static void
-MarkExactStackRoots(T context, JSTracer *trc)
+MarkExactStackRoots(ThreadSafeContext* cx, JSTracer *trc)
 {
-    MarkExactStackRootsForType<JSObject *, MarkObjectRoot>(context, trc, "exact-object");
-    MarkExactStackRootsForType<Shape *, MarkShapeRoot>(context, trc, "exact-shape");
-    MarkExactStackRootsForType<BaseShape *, MarkBaseShapeRoot>(context, trc, "exact-baseshape");
-    MarkExactStackRootsForType<types::TypeObject *, MarkTypeObjectRoot>(context, trc, "exact-typeobject");
-    MarkExactStackRootsForType<JSString *, MarkStringRoot>(context, trc, "exact-string");
-    MarkExactStackRootsForType<JS::Symbol *, MarkSymbolRoot>(context, trc, "exact-symbol");
-    MarkExactStackRootsForType<jit::JitCode *, MarkJitCodeRoot>(context, trc, "exact-jitcode");
-    MarkExactStackRootsForType<JSScript *, MarkScriptRoot>(context, trc, "exact-script");
-    MarkExactStackRootsForType<LazyScript *, MarkLazyScriptRoot>(context, trc, "exact-lazy-script");
-    MarkExactStackRootsForType<jsid, MarkIdRoot>(context, trc, "exact-id");
-    MarkExactStackRootsForType<Value, MarkValueRoot>(context, trc, "exact-value");
-    MarkExactStackRootsForType<types::Type, MarkTypeRoot>(context, trc, "exact-type");
-    MarkExactStackRootsForType<Bindings, MarkBindingsRoot>(context, trc);
-    MarkExactStackRootsForType<JSPropertyDescriptor, MarkPropertyDescriptorRoot>(context, trc);
-    MarkExactStackRootsForType<PropDesc, MarkPropDescRoot>(context, trc);
+    MarkExactStackRootsAcrossTypes<ThreadSafeContext*>(cx, trc);
+}
+
+static void
+MarkExactStackRoots(JSRuntime* rt, JSTracer *trc)
+{
+    for (ContextIter cx(rt); !cx.done(); cx.next())
+        MarkExactStackRootsAcrossTypes<ThreadSafeContext*>(cx.get(), trc);
+    MarkExactStackRootsAcrossTypes<PerThreadData*>(&rt->mainThread, trc);
 }
 #endif /* JSGC_USE_EXACT_ROOTING */
 
 enum ConservativeGCTest
 {
     CGCT_VALID,
     CGCT_LOWBITSET, /* excluded because one of the low bits was set */
     CGCT_NOTARENA,  /* not within arena range in a chunk */
@@ -696,17 +696,17 @@ js::gc::MarkPersistentRootedChains(JSTra
 #ifdef JSGC_FJGENERATIONAL
 void
 js::gc::MarkForkJoinStack(ForkJoinNurseryCollectionTracer *trc)
 {
     ForkJoinContext *cx = ForkJoinContext::current();
     PerThreadData *ptd = cx->perThreadData;
 
     AutoGCRooter::traceAllInContext(cx, trc);
-    MarkExactStackRoots<ThreadSafeContext*>(cx, trc);
+    MarkExactStackRoots(cx, trc);
     jit::MarkJitActivations(ptd, trc);
 
 #ifdef DEBUG
     // There should be only JIT activations on the stack
     for (ActivationIterator iter(ptd); !iter.done(); ++iter) {
         Activation *act = iter.activation();
         JS_ASSERT(act->isJit());
     }
@@ -728,17 +728,17 @@ js::gc::GCRuntime::markRuntime(JSTracer 
         }
         Debugger::markCrossCompartmentDebuggerObjectReferents(trc);
     }
 
     AutoGCRooter::traceAll(trc);
 
     if (!rt->isBeingDestroyed()) {
 #ifdef JSGC_USE_EXACT_ROOTING
-        MarkExactStackRoots<JSRuntime*>(rt, trc);
+        MarkExactStackRoots(rt, trc);
 #else
         markConservativeStackRoots(trc, useSavedRoots);
 #endif
         rt->markSelfHostingGlobal(trc);
     }
 
     for (RootRange r = rootsHash.all(); !r.empty(); r.popFront()) {
         const RootEntry &entry = r.front();
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/IonMacroAssembler.h"
 
 #include "jsinfer.h"
 #include "jsprf.h"
 
 #include "builtin/TypedObject.h"
+#include "gc/GCTrace.h"
 #include "jit/Bailouts.h"
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineIC.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Lowering.h"
 #include "jit/MIR.h"
 #include "jit/ParallelFunctions.h"
 #include "vm/ForkJoin.h"
@@ -432,22 +433,26 @@ template void MacroAssembler::loadFromTy
 template void MacroAssembler::loadFromTypedArray(int arrayType, const BaseIndex &src, const ValueOperand &dest,
                                                  bool allowDouble, Register temp, Label *fail);
 
 // Inlined version of gc::CheckAllocatorState that checks the bare essentials
 // and bails for anything that cannot be handled with our jit allocators.
 void
 MacroAssembler::checkAllocatorState(Label *fail)
 {
-#ifdef JS_GC_ZEAL
-    // Don't execute the inline path if gcZeal is active.
+    // Don't execute the inline path if we are tracing allocations.
+    if (js::gc::TraceEnabled())
+        jump(fail);
+
+# ifdef JS_GC_ZEAL
+    // Don't execute the inline path if gc zeal or tracing are active.
     branch32(Assembler::NotEqual,
              AbsoluteAddress(GetIonContext()->runtime->addressOfGCZeal()), Imm32(0),
              fail);
-#endif
+# endif
 
     // Don't execute the inline path if the compartment has an object metadata callback,
     // as the metadata to use for the object may vary between executions of the op.
     if (GetIonContext()->compartment->hasObjectMetadataCallback())
         jump(fail);
 }
 
 // Inline version of ShouldNurseryAllocate.
@@ -875,16 +880,31 @@ MacroAssembler::initGCThing(Register obj
         initGCSlots(obj, slots, templateObj, initFixedSlots);
 
         if (templateObj->hasPrivate()) {
             uint32_t nfixed = templateObj->numFixedSlots();
             storePtr(ImmPtr(templateObj->getPrivate()),
                      Address(obj, JSObject::getPrivateDataOffset(nfixed)));
         }
     }
+
+#ifdef JS_GC_TRACE
+    RegisterSet regs = RegisterSet::Volatile();
+    PushRegsInMask(regs);
+    regs.takeUnchecked(obj);
+    Register temp = regs.takeGeneral();
+
+    setupUnalignedABICall(2, temp);
+    passABIArg(obj);
+    movePtr(ImmGCPtr(templateObj->type()), temp);
+    passABIArg(temp);
+    callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::gc::TraceCreateObject));
+
+    PopRegsInMask(RegisterSet::Volatile());
+#endif
 }
 
 void
 MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register result,
                                Label *fail)
 {
     JS_ASSERT(IsEqualityOp(op));
 
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -2088,26 +2088,25 @@ Assembler::as_vxfer(Register vt1, Regist
         // If we are transferring a single half of the double
         // then it must be moving a VFP reg to a core reg.
         if (vt2 == InvalidReg)
             JS_ASSERT(f2c == FloatToCore);
         idx = idx << 21;
     } else {
         JS_ASSERT(idx == 0);
     }
-    VFPXferSize xfersz = WordTransfer;
-    uint32_t (*encodeVFP)(VFPRegister) = VN;
-    if (vt2 != InvalidReg) {
+
+    if (vt2 == InvalidReg) {
+        return writeVFPInst(sz, WordTransfer | f2c | c |
+                            RT(vt1) | maybeRN(vt2) | VN(vm) | idx);
+    } else {
         // We are doing a 64 bit transfer.
-        xfersz = DoubleTransfer;
-        encodeVFP = VM;
+        return writeVFPInst(sz, DoubleTransfer | f2c | c |
+                            RT(vt1) | maybeRN(vt2) | VM(vm) | idx);
     }
-
-    return writeVFPInst(sz, xfersz | f2c | c |
-                        RT(vt1) | maybeRN(vt2) | encodeVFP(vm) | idx);
 }
 enum vcvt_destFloatness {
     toInteger = 1 << 18,
     toFloat  = 0 << 18
 };
 enum vcvt_toZero {
     toZero = 1 << 7, // use the default rounding mode, which rounds truncates
     toFPSCR = 0 << 7 // use whatever rounding mode the fpscr specifies
--- a/js/src/jsapi-tests/testChromeBuffer.cpp
+++ b/js/src/jsapi-tests/testChromeBuffer.cpp
@@ -66,18 +66,18 @@ BEGIN_TEST(testChromeBuffer)
     {
         {
             JSAutoCompartment ac(cx, trusted_glob);
             const char *paramName = "x";
             const char *bytes = "return x ? 1 + trusted(x-1) : 0";
             JS::HandleObject global = JS::HandleObject::fromMarkedLocation(trusted_glob.unsafeGet());
             JS::CompileOptions options(cx);
             options.setFileAndLine("", 0);
-            CHECK(fun = JS_CompileFunction(cx, global, "trusted", 1, &paramName,
-                                           bytes, strlen(bytes), options));
+            CHECK(JS_CompileFunction(cx, global, "trusted", 1, &paramName,
+                                     bytes, strlen(bytes), options, &fun));
             trusted_fun = JS_GetFunctionObject(fun);
             if (!JS::AddNamedObjectRoot(cx, &trusted_fun, "trusted-function"))
                 return false;
         }
 
         JS::RootedValue v(cx, JS::ObjectValue(*trusted_fun));
         CHECK(JS_WrapValue(cx, &v));
 
@@ -88,18 +88,18 @@ BEGIN_TEST(testChromeBuffer)
                             "    try {                                  "
                             "        return trusted(100);               "
                             "    } catch(e) {                           "
                             "        return -1;                         "
                             "    }                                      "
                             "}                                          ";
         JS::CompileOptions options(cx);
         options.setFileAndLine("", 0);
-        CHECK(fun = JS_CompileFunction(cx, global, "untrusted", 1, &paramName,
-                                       bytes, strlen(bytes), options));
+        CHECK(JS_CompileFunction(cx, global, "untrusted", 1, &paramName,
+                                 bytes, strlen(bytes), options, &fun));
 
         JS::RootedValue rval(cx);
         CHECK(JS_CallFunction(cx, JS::NullPtr(), fun, JS::HandleValueArray(v), &rval));
         CHECK(rval.toInt32() == 100);
     }
 
     /*
      * Check that content called from chrome in the reserved-buffer space
@@ -112,34 +112,34 @@ BEGIN_TEST(testChromeBuffer)
             const char *bytes = "try {                                  "
                                 "  untrusted();                         "
                                 "} catch (e) {                          "
                                 "  return 'From trusted: ' + e;         "
                                 "}                                      ";
             JS::HandleObject global = JS::HandleObject::fromMarkedLocation(trusted_glob.unsafeGet());
             JS::CompileOptions options(cx);
             options.setFileAndLine("", 0);
-            CHECK(fun = JS_CompileFunction(cx, global, "trusted", 1, &paramName,
-                                           bytes, strlen(bytes), options));
+            CHECK(JS_CompileFunction(cx, global, "trusted", 1, &paramName,
+                                     bytes, strlen(bytes), options, &fun));
             trusted_fun = JS_GetFunctionObject(fun);
         }
 
         JS::RootedValue v(cx, JS::ObjectValue(*trusted_fun));
         CHECK(JS_WrapValue(cx, &v));
 
         const char *paramName = "trusted";
         const char *bytes = "try {                                      "
                             "  return untrusted(trusted);               "
                             "} catch (e) {                              "
                             "  return trusted(untrusted);               "
                             "}                                          ";
         JS::CompileOptions options(cx);
         options.setFileAndLine("", 0);
-        CHECK(fun = JS_CompileFunction(cx, global, "untrusted", 1, &paramName,
-                                       bytes, strlen(bytes), options));
+        CHECK(JS_CompileFunction(cx, global, "untrusted", 1, &paramName,
+                                 bytes, strlen(bytes), options, &fun));
 
         JS::RootedValue rval(cx);
         CHECK(JS_CallFunction(cx, JS::NullPtr(), fun, JS::HandleValueArray(v), &rval));
         bool match;
         CHECK(JS_StringEqualsAscii(cx, rval.toString(), "From trusted: InternalError: too much recursion", &match));
         CHECK(match);
     }
 
@@ -149,34 +149,34 @@ BEGIN_TEST(testChromeBuffer)
      */
     {
         {
             JSAutoCompartment ac(cx, trusted_glob);
             const char *bytes = "return 42";
             JS::HandleObject global = JS::HandleObject::fromMarkedLocation(trusted_glob.unsafeGet());
             JS::CompileOptions options(cx);
             options.setFileAndLine("", 0);
-            CHECK(fun = JS_CompileFunction(cx, global, "trusted", 0, nullptr,
-                                           bytes, strlen(bytes), options));
+            CHECK(JS_CompileFunction(cx, global, "trusted", 0, nullptr,
+                                     bytes, strlen(bytes), options, &fun));
             trusted_fun = JS_GetFunctionObject(fun);
         }
 
         JS::RootedFunction fun(cx, JS_NewFunction(cx, CallTrusted, 0, 0, global, "callTrusted"));
         JS::RootedObject callTrusted(cx, JS_GetFunctionObject(fun));
 
         const char *paramName = "f";
         const char *bytes = "try {                                      "
                             "  return untrusted(trusted);               "
                             "} catch (e) {                              "
                             "  return f();                              "
                             "}                                          ";
         JS::CompileOptions options(cx);
         options.setFileAndLine("", 0);
-        CHECK(fun = JS_CompileFunction(cx, global, "untrusted", 1, &paramName,
-                                       bytes, strlen(bytes), options));
+        CHECK(JS_CompileFunction(cx, global, "untrusted", 1, &paramName,
+                                 bytes, strlen(bytes), options, &fun));
 
         JS::RootedValue arg(cx, JS::ObjectValue(*callTrusted));
         JS::RootedValue rval(cx);
         CHECK(JS_CallFunction(cx, JS::NullPtr(), fun, JS::HandleValueArray(arg), &rval));
         CHECK(rval.toInt32() == 42);
     }
 
     return true;
--- a/js/src/jsapi-tests/testCloneScript.cpp
+++ b/js/src/jsapi-tests/testCloneScript.cpp
@@ -28,21 +28,21 @@ BEGIN_TEST(test_cloneScript)
         "}\n"
         "(sum);\n";
 
     JS::RootedObject obj(cx);
 
     // compile for A
     {
         JSAutoCompartment a(cx, A);
-        JSFunction *fun;
+        JS::RootedFunction fun(cx);
         JS::CompileOptions options(cx);
         options.setFileAndLine(__FILE__, 1);
-        CHECK(fun = JS_CompileFunction(cx, A, "f", 0, nullptr, source,
-                                       strlen(source), options));
+        CHECK(JS_CompileFunction(cx, A, "f", 0, nullptr, source,
+                                 strlen(source), options, &fun));
         CHECK(obj = JS_GetFunctionObject(fun));
     }
 
     // clone into B
     {
         JSAutoCompartment b(cx, B);
         CHECK(JS_CloneFunctionObject(cx, obj, B));
     }
@@ -104,19 +104,20 @@ BEGIN_TEST(test_cloneScriptWithPrincipal
 
     JS::RootedObject obj(cx);
 
     // Compile in A
     {
         JSAutoCompartment a(cx, A);
         JS::CompileOptions options(cx);
         options.setFileAndLine(__FILE__, 1);
-        JS::RootedFunction fun(cx, JS_CompileFunction(cx, A, "f",
-                mozilla::ArrayLength(argnames), argnames, source,
-                strlen(source), options));
+        JS::RootedFunction fun(cx);
+        JS_CompileFunction(cx, A, "f",
+                           mozilla::ArrayLength(argnames), argnames, source,
+                           strlen(source), options, &fun);
         CHECK(fun);
 
         JSScript *script;
         CHECK(script = JS_GetFunctionScript(cx, fun));
 
         CHECK(JS_GetScriptPrincipals(script) == principalsA);
         CHECK(obj = JS_GetFunctionObject(fun));
     }
--- a/js/src/jsapi-tests/testEnclosingFunction.cpp
+++ b/js/src/jsapi-tests/testEnclosingFunction.cpp
@@ -35,31 +35,31 @@ BEGIN_TEST(test_enclosingFunction)
     CHECK(foundFun == nullptr);
 
     RootedFunction fun(cx);
 
     JS::CompileOptions options(cx);
     options.setFileAndLine(__FILE__, __LINE__);
 
     const char s1chars[] = "checkEnclosing()";
-    fun = JS_CompileFunction(cx, global, "s1", 0, nullptr, s1chars,
-                             strlen(s1chars), options);
+    JS_CompileFunction(cx, global, "s1", 0, nullptr, s1chars,
+                       strlen(s1chars), options, &fun);
     CHECK(fun);
     EXEC("s1()");
     CHECK(foundFun == fun);
 
     const char s2chars[] = "return function() { checkEnclosing() }";
-    fun = JS_CompileFunction(cx, global, "s2", 0, nullptr, s2chars,
-                             strlen(s2chars), options);
+    JS_CompileFunction(cx, global, "s2", 0, nullptr, s2chars,
+                       strlen(s2chars), options, &fun);
     CHECK(fun);
     EXEC("s2()()");
     CHECK(foundFun == fun);
 
     const char s3chars[] = "return function() { let (x) { function g() { checkEnclosing() } return g() } }";
-    fun = JS_CompileFunction(cx, global, "s3", 0, nullptr, s3chars,
-                             strlen(s3chars), options);
+    JS_CompileFunction(cx, global, "s3", 0, nullptr, s3chars,
+                       strlen(s3chars), options, &fun);
     CHECK(fun);
     EXEC("s3()()");
     CHECK(foundFun == fun);
 
     return true;
 }
 END_TEST(test_enclosingFunction)
--- a/js/src/jsapi-tests/testScriptInfo.cpp
+++ b/js/src/jsapi-tests/testScriptInfo.cpp
@@ -24,19 +24,18 @@ catch (e)          \n\
 
 // Bug 670958 - fix JS_GetScriptLineExtent, among others
 BEGIN_TEST(testScriptInfo)
 {
     unsigned startLine = 1000;
 
     JS::CompileOptions options(cx);
     options.setFileAndLine(__FILE__, startLine);
-    JS::RootedScript script(cx, JS_CompileScript(cx, global, code, strlen(code),
-                                                 options));
-
+    JS::RootedScript script(cx);
+    CHECK(JS_CompileScript(cx, global, code, strlen(code), options, &script));
     CHECK(script);
 
     jsbytecode *start = JS_LineNumberToPC(cx, script, startLine);
     CHECK_EQUAL(JS_GetScriptBaseLineNumber(cx, script), startLine);
     CHECK_EQUAL(JS_PCToLineNumber(cx, script, start), startLine);
     CHECK_EQUAL(JS_GetScriptLineExtent(cx, script), 11u);
     CHECK(strcmp(JS_GetScriptFilename(script), __FILE__) == 0);
     const jschar *sourceMap = JS_GetScriptSourceMap(cx, script);
--- a/js/src/jsapi-tests/testScriptObject.cpp
+++ b/js/src/jsapi-tests/testScriptObject.cpp
@@ -13,19 +13,18 @@ struct ScriptObjectFixture : public JSAP
     static jschar uc_code[];
 
     ScriptObjectFixture()
     {
         for (int i = 0; i < code_size; i++)
             uc_code[i] = code[i];
     }
 
-    bool tryScript(JS::HandleObject global, JSScript *scriptArg)
+    bool tryScript(JS::HandleObject global, JS::HandleScript script)
     {
-        JS::RootedScript script(cx, scriptArg);
         CHECK(script);
 
         JS_GC(rt);
 
         /* After a garbage collection, the script should still work. */
         JS::RootedValue result(cx);
         CHECK(JS_ExecuteScript(cx, global, script, &result));
 
@@ -37,121 +36,136 @@ const char ScriptObjectFixture::code[] =
     "(function(a, b){return a+' '+b;}('hello', 'world'))";
 const int ScriptObjectFixture::code_size = sizeof(ScriptObjectFixture::code) - 1;
 jschar ScriptObjectFixture::uc_code[ScriptObjectFixture::code_size];
 
 BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_CompileScript)
 {
     JS::CompileOptions options(cx);
     options.setFileAndLine(__FILE__, __LINE__);
-    return tryScript(global, JS_CompileScript(cx, global, code, code_size,
-                                              options));
+    JS::RootedScript script(cx);
+    CHECK(JS_CompileScript(cx, global, code, code_size, options, &script));
+    return tryScript(global, script);
 }
 END_FIXTURE_TEST(ScriptObjectFixture, bug438633_CompileScript)
 
 BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_CompileScript_empty)
 {
     JS::CompileOptions options(cx);
     options.setFileAndLine(__FILE__, __LINE__);
-    return tryScript(global, JS_CompileScript(cx, global, "", 0, options));
+    JS::RootedScript script(cx);
+    CHECK(JS_CompileScript(cx, global, "", 0, options, &script));
+    return tryScript(global, script);
 }
 END_FIXTURE_TEST(ScriptObjectFixture, bug438633_CompileScript_empty)
 
 BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_CompileScriptForPrincipals)
 {
     JS::CompileOptions options(cx);
     options.setFileAndLine(__FILE__, __LINE__);
-    return tryScript(global, JS_CompileScript(cx, global, code, code_size,
-                                              options));
+    JS::RootedScript script(cx);
+    CHECK(JS_CompileScript(cx, global, code, code_size, options, &script));
+    return tryScript(global, script);
 }
 END_FIXTURE_TEST(ScriptObjectFixture, bug438633_CompileScriptForPrincipals)
 
 BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileUCScript)
 {
     JS::CompileOptions options(cx);
     options.setFileAndLine(__FILE__, __LINE__);
-    return tryScript(global, JS_CompileUCScript(cx, global, uc_code, code_size,
-                                                options));
+    JS::RootedScript script(cx);
+    CHECK(JS_CompileUCScript(cx, global, uc_code, code_size, options, &script));
+    return tryScript(global, script);
 }
 END_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileUCScript)
 
 BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileUCScript_empty)
 {
     JS::CompileOptions options(cx);
     options.setFileAndLine(__FILE__, __LINE__);
-    return tryScript(global, JS_CompileUCScript(cx, global, uc_code, 0,
-                                                options));
+    JS::RootedScript script(cx);
+    CHECK(JS_CompileUCScript(cx, global, uc_code, 0, options, &script));
+    return tryScript(global, script);
 }
 END_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileUCScript_empty)
 
 BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileUCScriptForPrincipals)
 {
     JS::CompileOptions options(cx);
     options.setFileAndLine(__FILE__, __LINE__);
-    return tryScript(global, JS_CompileUCScript(cx, global, uc_code, code_size,
-                                                options));
+    JS::RootedScript script(cx);
+    CHECK(JS_CompileUCScript(cx, global, uc_code, code_size, options, &script));
+    return tryScript(global, script);
 }
 END_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileUCScriptForPrincipals)
 
 BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFile)
 {
     TempFile tempScript;
     static const char script_filename[] = "temp-bug438633_JS_CompileFile";
     FILE *script_stream = tempScript.open(script_filename);
     CHECK(fputs(code, script_stream) != EOF);
     tempScript.close();
     JS::CompileOptions options(cx);
     options.setFileAndLine(script_filename, 1);
-    JSScript *script = JS::Compile(cx, global, options, script_filename);
+    JS::RootedScript script(cx);
+    CHECK(JS::Compile(cx, global, options, script_filename, &script));
     tempScript.remove();
     return tryScript(global, script);
 }
 END_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFile)
 
 BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFile_empty)
 {
     TempFile tempScript;
     static const char script_filename[] = "temp-bug438633_JS_CompileFile_empty";
     tempScript.open(script_filename);
     tempScript.close();
     JS::CompileOptions options(cx);
     options.setFileAndLine(script_filename, 1);
-    JSScript *script = JS::Compile(cx, global, options, script_filename);
+    JS::RootedScript script(cx);
+    CHECK(JS::Compile(cx, global, options, script_filename, &script));
     tempScript.remove();
     return tryScript(global, script);
 }
 END_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFile_empty)
 
 BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFileHandle)
 {
     const char *script_filename = "temporary file";
     TempFile tempScript;
     FILE *script_stream = tempScript.open("temp-bug438633_JS_CompileFileHandle");
     CHECK(fputs(code, script_stream) != EOF);
     CHECK(fseek(script_stream, 0, SEEK_SET) != EOF);
     JS::CompileOptions options(cx);
     options.setFileAndLine(script_filename, 1);
-    return tryScript(global, JS::Compile(cx, global, options, script_stream));
+    JS::RootedScript script(cx);
+    CHECK(JS::Compile(cx, global, options, script_stream, &script));
+    return tryScript(global, script);
 }
 END_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFileHandle)
 
 BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFileHandle_empty)
 {
     const char *script_filename = "empty temporary file";
     TempFile tempScript;
     FILE *script_stream = tempScript.open("temp-bug438633_JS_CompileFileHandle_empty");
     JS::CompileOptions options(cx);
     options.setFileAndLine(script_filename, 1);
-    return tryScript(global, JS::Compile(cx, global, options, script_stream));
+    JS::RootedScript script(cx);
+    CHECK(JS::Compile(cx, global, options, script_stream, &script));
+    return tryScript(global, script);
 }
 END_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFileHandle_empty)
 
 BEGIN_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFileHandleForPrincipals)
 {
     TempFile tempScript;
     FILE *script_stream = tempScript.open("temp-bug438633_JS_CompileFileHandleForPrincipals");
     CHECK(fputs(code, script_stream) != EOF);
     CHECK(fseek(script_stream, 0, SEEK_SET) != EOF);
     JS::CompileOptions options(cx);
     options.setFileAndLine("temporary file", 1);
-    return tryScript(global, JS::Compile(cx, global, options, script_stream));
+    JS::RootedScript script(cx);
+    CHECK(JS::Compile(cx, global, options, script_stream, &script));
+    return tryScript(global, script);
 }
 END_FIXTURE_TEST(ScriptObjectFixture, bug438633_JS_CompileFileHandleForPrincipals)
--- a/js/src/jsapi-tests/testSourcePolicy.cpp
+++ b/js/src/jsapi-tests/testSourcePolicy.cpp
@@ -12,17 +12,19 @@ BEGIN_TEST(testBug795104)
     JS::CompartmentOptionsRef(cx->compartment()).setDiscardSource(true);
     const size_t strLen = 60002;
     char *s = static_cast<char *>(JS_malloc(cx, strLen));
     CHECK(s);
     s[0] = '"';
     memset(s + 1, 'x', strLen - 2);
     s[strLen - 1] = '"';
     CHECK(JS::Evaluate(cx, global, opts, s, strLen));
-    CHECK(JS::CompileFunction(cx, global, opts, "f", 0, nullptr, s, strLen));
+    JS::RootedFunction fun(cx);
+    CHECK(JS::CompileFunction(cx, global, opts, "f", 0, nullptr, s, strLen, &fun));
+    CHECK(fun);
     JS_free(cx, s);
 
     return true;
 }
 END_TEST(testBug795104)
 
 static const char *const simpleSource = "var x = 4;";
 
--- a/js/src/jsapi-tests/testTrap.cpp
+++ b/js/src/jsapi-tests/testTrap.cpp
@@ -31,18 +31,18 @@ BEGIN_TEST(testTrap_gc)
 "    ++i;\n"
 "}\n"
 "({ result: sum });\n"
         ;
 
     // compile
     JS::CompileOptions options(cx);
     options.setFileAndLine(__FILE__, 1);
-    JS::RootedScript script(cx, JS_CompileScript(cx, global, source,
-                                                 strlen(source), options));
+    JS::RootedScript script(cx);
+    CHECK(JS_CompileScript(cx, global, source, strlen(source), options, &script));
     CHECK(script);
 
     // execute
     JS::RootedValue v2(cx);
     CHECK(JS_ExecuteScript(cx, global, script, &v2));
     CHECK(v2.isObject());
     CHECK_EQUAL(emptyTrapCallCount, 0);
 
--- a/js/src/jsapi-tests/testXDR.cpp
+++ b/js/src/jsapi-tests/testXDR.cpp
@@ -25,17 +25,18 @@ CompileScriptForPrincipalsVersionOrigin(
     jschar *chars = static_cast<jschar *>(JS_malloc(cx, nchars * sizeof(jschar)));
     if (!chars)
         return nullptr;
     JS_ALWAYS_TRUE(JS_DecodeBytes(cx, bytes, nbytes, chars, &nchars));
     JS::CompileOptions options(cx);
     options.setOriginPrincipals(originPrincipals)
            .setFileAndLine(filename, lineno)
            .setVersion(version);
-    JSScript *script = JS::Compile(cx, obj, options, chars, nchars);
+    JS::RootedScript script(cx);
+    JS::Compile(cx, obj, options, chars, nchars, &script);
     free(chars);
     return script;
 }
 
 static JSScript *
 FreezeThaw(JSContext *cx, JS::HandleScript script)
 {
     // freeze
@@ -157,18 +158,18 @@ BEGIN_TEST(testXDR_bug506491)
         "    Math.sin(value);\n"
         "    return let (n = name, v = value) function () { return String(v); };\n"
         "}\n"
         "var f = makeClosure('0;', 'status', 'ok');\n";
 
     // compile
     JS::CompileOptions options(cx);
     options.setFileAndLine(__FILE__, __LINE__);
-    JS::RootedScript script(cx, JS_CompileScript(cx, global, s, strlen(s),
-                                                 options));
+    JS::RootedScript script(cx);
+    CHECK(JS_CompileScript(cx, global, s, strlen(s), options, &script));
     CHECK(script);
 
     script = FreezeThaw(cx, script);
     CHECK(script);
 
     // execute
     JS::RootedValue v2(cx);
     CHECK(JS_ExecuteScript(cx, global, script, &v2));
@@ -184,17 +185,18 @@ BEGIN_TEST(testXDR_bug506491)
 }
 END_TEST(testXDR_bug506491)
 
 BEGIN_TEST(testXDR_bug516827)
 {
     // compile an empty script
     JS::CompileOptions options(cx);
     options.setFileAndLine(__FILE__, __LINE__);
-    JS::RootedScript script(cx, JS_CompileScript(cx, global, "", 0, options));
+    JS::RootedScript script(cx);
+    CHECK(JS_CompileScript(cx, global, "", 0, options, &script));
     CHECK(script);
 
     script = FreezeThaw(cx, script);
     CHECK(script);
 
     // execute with null result meaning no result wanted
     CHECK(JS_ExecuteScript(cx, global, script));
     return true;
@@ -207,18 +209,18 @@ BEGIN_TEST(testXDR_source)
         // This can't possibly fail to compress well, can it?
         "function f(x) { return x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x + x }",
         "short",
         nullptr
     };
     for (const char **s = samples; *s; s++) {
         JS::CompileOptions options(cx);
         options.setFileAndLine(__FILE__, __LINE__);
-        JS::RootedScript script(cx, JS_CompileScript(cx, global, *s, strlen(*s),
-                                                     options));
+        JS::RootedScript script(cx);
+        CHECK(JS_CompileScript(cx, global, *s, strlen(*s), options, &script));
         CHECK(script);
         script = FreezeThaw(cx, script);
         CHECK(script);
         JSString *out = JS_DecompileScript(cx, script, "testing", 0);
         CHECK(out);
         bool equal;
         CHECK(JS_StringEqualsAscii(cx, out, *s, &equal));
         CHECK(equal);
@@ -233,17 +235,17 @@ BEGIN_TEST(testXDR_sourceMap)
         "http://example.com/source-map.json",
         "file:///var/source-map.json",
         nullptr
     };
     JS::RootedScript script(cx);
     for (const char **sm = sourceMaps; *sm; sm++) {
         JS::CompileOptions options(cx);
         options.setFileAndLine(__FILE__, __LINE__);
-        script = JS_CompileScript(cx, global, "", 0, options);
+        CHECK(JS_CompileScript(cx, global, "", 0, options, &script));
         CHECK(script);
 
         size_t len = strlen(*sm);
         jschar *expected = js::InflateString(cx, *sm, &len);
         CHECK(expected);
 
         // The script source takes responsibility of free'ing |expected|.
         CHECK(script->scriptSource()->setSourceMapURL(cx, expected));
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1104,64 +1104,68 @@ JS_WrapValue(JSContext *cx, MutableHandl
 JS_PUBLIC_API(JSObject *)
 JS_TransplantObject(JSContext *cx, HandleObject origobj, HandleObject target)
 {
     AssertHeapIsIdle(cx);
     JS_ASSERT(origobj != target);
     JS_ASSERT(!origobj->is<CrossCompartmentWrapperObject>());
     JS_ASSERT(!target->is<CrossCompartmentWrapperObject>());
 
-    AutoMaybeTouchDeadZones agc(cx);
-    AutoDisableProxyCheck adpc(cx->runtime());
-
-    JSCompartment *destination = target->compartment();
     RootedValue origv(cx, ObjectValue(*origobj));
     RootedObject newIdentity(cx);
 
-    if (origobj->compartment() == destination) {
-        // If the original object is in the same compartment as the
-        // destination, then we know that we won't find a wrapper in the
-        // destination's cross compartment map and that the same
-        // object will continue to work.
-        if (!JSObject::swap(cx, origobj, target))
-            MOZ_CRASH();
-        newIdentity = origobj;
-    } else if (WrapperMap::Ptr p = destination->lookupWrapper(origv)) {
-        // There might already be a wrapper for the original object in
-        // the new compartment. If there is, we use its identity and swap
-        // in the contents of |target|.
-        newIdentity = &p->value().get().toObject();
-
-        // When we remove origv from the wrapper map, its wrapper, newIdentity,
-        // must immediately cease to be a cross-compartment wrapper. Neuter it.
-        destination->removeWrapper(p);
-        NukeCrossCompartmentWrapper(cx, newIdentity);
-
-        if (!JSObject::swap(cx, newIdentity, target))
+    {
+        // Scope to make ~AutoMaybeTouchDeadZones do its GC before the return value is on the stack.
+        AutoMaybeTouchDeadZones agc(cx);
+        AutoDisableProxyCheck adpc(cx->runtime());
+
+        JSCompartment *destination = target->compartment();
+
+        if (origobj->compartment() == destination) {
+            // If the original object is in the same compartment as the
+            // destination, then we know that we won't find a wrapper in the
+            // destination's cross compartment map and that the same
+            // object will continue to work.
+            if (!JSObject::swap(cx, origobj, target))
+                MOZ_CRASH();
+            newIdentity = origobj;
+        } else if (WrapperMap::Ptr p = destination->lookupWrapper(origv)) {
+            // There might already be a wrapper for the original object in
+            // the new compartment. If there is, we use its identity and swap
+            // in the contents of |target|.
+            newIdentity = &p->value().get().toObject();
+
+            // When we remove origv from the wrapper map, its wrapper, newIdentity,
+            // must immediately cease to be a cross-compartment wrapper. Neuter it.
+            destination->removeWrapper(p);
+            NukeCrossCompartmentWrapper(cx, newIdentity);
+
+            if (!JSObject::swap(cx, newIdentity, target))
+                MOZ_CRASH();
+        } else {
+            // Otherwise, we use |target| for the new identity object.
+            newIdentity = target;
+        }
+
+        // Now, iterate through other scopes looking for references to the
+        // old object, and update the relevant cross-compartment wrappers.
+        if (!RemapAllWrappersForObject(cx, origobj, newIdentity))
             MOZ_CRASH();
-    } else {
-        // Otherwise, we use |target| for the new identity object.
-        newIdentity = target;
-    }
-
-    // Now, iterate through other scopes looking for references to the
-    // old object, and update the relevant cross-compartment wrappers.
-    if (!RemapAllWrappersForObject(cx, origobj, newIdentity))
-        MOZ_CRASH();
-
-    // Lastly, update the original object to point to the new one.
-    if (origobj->compartment() != destination) {
-        RootedObject newIdentityWrapper(cx, newIdentity);
-        AutoCompartment ac(cx, origobj);
-        if (!JS_WrapObject(cx, &newIdentityWrapper))
-            MOZ_CRASH();
-        JS_ASSERT(Wrapper::wrappedObject(newIdentityWrapper) == newIdentity);
-        if (!JSObject::swap(cx, origobj, newIdentityWrapper))
-            MOZ_CRASH();
-        origobj->compartment()->putWrapper(cx, CrossCompartmentKey(newIdentity), origv);
+
+        // Lastly, update the original object to point to the new one.
+        if (origobj->compartment() != destination) {
+            RootedObject newIdentityWrapper(cx, newIdentity);
+            AutoCompartment ac(cx, origobj);
+            if (!JS_WrapObject(cx, &newIdentityWrapper))
+                MOZ_CRASH();
+            JS_ASSERT(Wrapper::wrappedObject(newIdentityWrapper) == newIdentity);
+            if (!JSObject::swap(cx, origobj, newIdentityWrapper))
+                MOZ_CRASH();
+            origobj->compartment()->putWrapper(cx, CrossCompartmentKey(newIdentity), origv);
+        }
     }
 
     // The new identity object might be one of several things. Return it to avoid
     // ambiguity.
     return newIdentity;
 }
 
 /*
@@ -4533,75 +4537,74 @@ JS::CompileOptions::wrap(JSContext *cx, 
     if (introductionScriptRoot) {
         if (introductionScriptRoot->compartment() != compartment)
             introductionScriptRoot = nullptr;
     }
 
     return true;
 }
 
-JSScript *
+bool
 JS::Compile(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &options,
-            SourceBufferHolder &srcBuf)
+            SourceBufferHolder &srcBuf, MutableHandleScript script)
 {
     JS_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     AutoLastFrameCheck lfc(cx);
 
-    return frontend::CompileScript(cx, &cx->tempLifoAlloc(), obj, NullPtr(), options, srcBuf);
-}
-
-JSScript *
+    script.set(frontend::CompileScript(cx, &cx->tempLifoAlloc(), obj, NullPtr(), options, srcBuf));
+    return !!script;
+}
+
+bool
 JS::Compile(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &options,
-            const jschar *chars, size_t length)
+            const jschar *chars, size_t length, MutableHandleScript script)
 {
     SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
-    return Compile(cx, obj, options, srcBuf);
-}
-
-JSScript *
+    return Compile(cx, obj, options, srcBuf, script);
+}
+
+bool
 JS::Compile(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &options,
-            const char *bytes, size_t length)
-{
-    jschar *chars;
+            const char *bytes, size_t length, MutableHandleScript script)
+{
+    mozilla::ScopedFreePtr<jschar> chars;
     if (options.utf8)
         chars = UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length), &length).get();
     else
         chars = InflateString(cx, bytes, &length);
     if (!chars)
         return nullptr;
 
-    JSScript *script = Compile(cx, obj, options, chars, length);
-    js_free(chars);
-    return script;
-}
-
-JSScript *
-JS::Compile(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &options, FILE *fp)
+    return Compile(cx, obj, options, chars, length, script);
+}
+
+bool
+JS::Compile(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &options, FILE *fp,
+            MutableHandleScript script)
 {
     FileContents buffer(cx);
     if (!ReadCompleteFile(cx, fp, buffer))
         return nullptr;
 
-    JSScript *script = Compile(cx, obj, options, buffer.begin(), buffer.length());
-    return script;
-}
-
-JSScript *
-JS::Compile(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &optionsArg, const char *filename)
+    return Compile(cx, obj, options, buffer.begin(), buffer.length(), script);
+}
+
+bool
+JS::Compile(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &optionsArg, const char *filename,
+            MutableHandleScript script)
 {
     AutoFile file;
     if (!file.open(cx, filename))
         return nullptr;
     CompileOptions options(cx, optionsArg);
     options.setFileAndLine(filename, 1);
-    JSScript *script = Compile(cx, obj, options, file.fp());
-    return script;
+    return Compile(cx, obj, options, file.fp(), script);
 }
 
 JS_PUBLIC_API(bool)
 JS::CanCompileOffThread(JSContext *cx, const ReadOnlyCompileOptions &options, size_t length)
 {
     static const size_t TINY_LENGTH = 1000;
     static const size_t HUGE_LENGTH = 100 * 1000;
 
@@ -4635,38 +4638,43 @@ JS::CompileOffThread(JSContext *cx, cons
 }
 
 JS_PUBLIC_API(JSScript *)
 JS::FinishOffThreadScript(JSContext *maybecx, JSRuntime *rt, void *token)
 {
 #ifdef JS_THREADSAFE
     JS_ASSERT(CurrentThreadCanAccessRuntime(rt));
 
-    Maybe<AutoLastFrameCheck> lfc;
-    if (maybecx)
-        lfc.construct(maybecx);
-
-    return HelperThreadState().finishParseTask(maybecx, rt, token);
+    if (maybecx) {
+        RootedScript script(maybecx);
+        {
+            AutoLastFrameCheck lfc(maybecx);
+            script = HelperThreadState().finishParseTask(maybecx, rt, token);
+        }
+        return script;
+    } else {
+        return HelperThreadState().finishParseTask(maybecx, rt, token);
+    }
 #else
     MOZ_ASSUME_UNREACHABLE("Off thread compilation is not available.");
 #endif
 }
 
-JS_PUBLIC_API(JSScript *)
+JS_PUBLIC_API(bool)
 JS_CompileScript(JSContext *cx, JS::HandleObject obj, const char *ascii,
-                 size_t length, const JS::CompileOptions &options)
-{
-    return Compile(cx, obj, options, ascii, length);
-}
-
-JS_PUBLIC_API(JSScript *)
+                 size_t length, const JS::CompileOptions &options, MutableHandleScript script)
+{
+    return Compile(cx, obj, options, ascii, length, script);
+}
+
+JS_PUBLIC_API(bool)
 JS_CompileUCScript(JSContext *cx, JS::HandleObject obj, const jschar *chars,
-                   size_t length, const JS::CompileOptions &options)
-{
-    return Compile(cx, obj, options, chars, length);
+                   size_t length, const JS::CompileOptions &options, MutableHandleScript script)
+{
+    return Compile(cx, obj, options, chars, length, script);
 }
 
 JS_PUBLIC_API(bool)
 JS_BufferIsCompilableUnit(JSContext *cx, HandleObject obj, const char *utf8, size_t length)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
@@ -4704,102 +4712,100 @@ JS_BufferIsCompilableUnit(JSContext *cx,
 
 JS_PUBLIC_API(JSObject *)
 JS_GetGlobalFromScript(JSScript *script)
 {
     JS_ASSERT(!script->isCachedEval());
     return &script->global();
 }
 
-JS_PUBLIC_API(JSFunction *)
+JS_PUBLIC_API(bool)
 JS::CompileFunction(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &options,
                     const char *name, unsigned nargs, const char *const *argnames,
-                    SourceBufferHolder &srcBuf)
+                    SourceBufferHolder &srcBuf, MutableHandleFunction fun)
 {
     JS_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
+    RootedAtom funAtom(cx);
     AutoLastFrameCheck lfc(cx);
 
-    RootedAtom funAtom(cx);
     if (name) {
         funAtom = Atomize(cx, name, strlen(name));
         if (!funAtom)
-            return nullptr;
+            return false;
     }
 
     AutoNameVector formals(cx);
     for (unsigned i = 0; i < nargs; i++) {
         RootedAtom argAtom(cx, Atomize(cx, argnames[i], strlen(argnames[i])));
         if (!argAtom || !formals.append(argAtom->asPropertyName()))
-            return nullptr;
+            return false;
     }
 
-    RootedFunction fun(cx, NewFunction(cx, NullPtr(), nullptr, 0, JSFunction::INTERPRETED, obj,
-                                       funAtom, JSFunction::FinalizeKind, TenuredObject));
+    fun.set(NewFunction(cx, NullPtr(), nullptr, 0, JSFunction::INTERPRETED, obj,
+                        funAtom, JSFunction::FinalizeKind, TenuredObject));
     if (!fun)
-        return nullptr;
-
-    if (!frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf))
-        return nullptr;
+        return false;
+
+    if (!frontend::CompileFunctionBody(cx, fun, options, formals, srcBuf))
+        return false;
 
     if (obj && funAtom && options.defineOnScope) {
         Rooted<jsid> id(cx, AtomToId(funAtom));
         RootedValue value(cx, ObjectValue(*fun));
         if (!JSObject::defineGeneric(cx, obj, id, value, nullptr, nullptr, JSPROP_ENUMERATE))
-            return nullptr;
+            return false;
     }
 
-    return fun;
-}
-
-JS_PUBLIC_API(JSFunction *)
+    return true;
+}
+
+JS_PUBLIC_API(bool)
 JS::CompileFunction(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &options,
                     const char *name, unsigned nargs, const char *const *argnames,
-                    const jschar *chars, size_t length)
-{
-  SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
-  return JS::CompileFunction(cx, obj, options, name, nargs, argnames, srcBuf);
-}
-
-JS_PUBLIC_API(JSFunction *)
+                    const jschar *chars, size_t length, MutableHandleFunction fun)
+{
+    SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
+    return JS::CompileFunction(cx, obj, options, name, nargs, argnames, srcBuf, fun);
+}
+
+JS_PUBLIC_API(bool)
 JS::CompileFunction(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &options,
                     const char *name, unsigned nargs, const char *const *argnames,
-                    const char *bytes, size_t length)
-{
-    jschar *chars;
+                    const char *bytes, size_t length, MutableHandleFunction fun)
+{
+    mozilla::ScopedFreePtr<jschar> chars;
     if (options.utf8)
         chars = UTF8CharsToNewTwoByteCharsZ(cx, UTF8Chars(bytes, length), &length).get();
     else
         chars = InflateString(cx, bytes, &length);
     if (!chars)
         return nullptr;
 
-    JSFunction *fun = CompileFunction(cx, obj, options, name, nargs, argnames, chars, length);
-    js_free(chars);
-    return fun;
-}
-
-JS_PUBLIC_API(JSFunction *)
+    return CompileFunction(cx, obj, options, name, nargs, argnames, chars, length, fun);
+}
+
+JS_PUBLIC_API(bool)
 JS_CompileUCFunction(JSContext *cx, JS::HandleObject obj, const char *name,
                      unsigned nargs, const char *const *argnames,
                      const jschar *chars, size_t length,
-                     const CompileOptions &options)
-{
-    return CompileFunction(cx, obj, options, name, nargs, argnames, chars, length);
-}
-
-JS_PUBLIC_API(JSFunction *)
+                     const CompileOptions &options, MutableHandleFunction fun)
+{
+    return CompileFunction(cx, obj, options, name, nargs, argnames, chars, length, fun);
+}
+
+JS_PUBLIC_API(bool)
 JS_CompileFunction(JSContext *cx, JS::HandleObject obj, const char *name,
                    unsigned nargs, const char *const *argnames,
                    const char *ascii, size_t length,
-                   const JS::CompileOptions &options)
-{
-    return CompileFunction(cx, obj, options, name, nargs, argnames, ascii, length);
+                   const JS::CompileOptions &options, MutableHandleFunction fun)
+{
+    return CompileFunction(cx, obj, options, name, nargs, argnames, ascii, length, fun);
 }
 
 JS_PUBLIC_API(JSString *)
 JS_DecompileScript(JSContext *cx, HandleScript script, const char *name, unsigned indent)
 {
     JS_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
 
     AssertHeapIsIdle(cx);
@@ -5133,23 +5139,22 @@ JS::Construct(JSContext *cx, HandleValue
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, fval, args);
     AutoLastFrameCheck lfc(cx);
 
     return InvokeConstructor(cx, fval, args.length(), args.begin(), rval.address());
 }
 
-JS_PUBLIC_API(JSObject *)
-JS_New(JSContext *cx, HandleObject ctor, const JS::HandleValueArray& inputArgs)
+static JSObject *
+JS_NewHelper(JSContext *cx, HandleObject ctor, const JS::HandleValueArray& inputArgs)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, ctor, inputArgs);
-    AutoLastFrameCheck lfc(cx);
 
     // This is not a simple variation of JS_CallFunctionValue because JSOP_NEW
     // is not a simple variation of JSOP_CALL. We have to determine what class
     // of object to create, create it, and clamp the return value to an object,
     // among other details. InvokeConstructor does the hard work.
     InvokeArgs args(cx);
     if (!args.init(inputArgs.length()))
         return nullptr;
@@ -5172,16 +5177,27 @@ JS_New(JSContext *cx, HandleObject ctor,
                                  bytes.ptr());
         }
         return nullptr;
     }
 
     return &args.rval().toObject();
 }
 
+JS_PUBLIC_API(JSObject *)
+JS_New(JSContext *cx, HandleObject ctor, const JS::HandleValueArray& inputArgs)
+{
+    RootedObject obj(cx);
+    {
+        AutoLastFrameCheck lfc(cx);
+        obj = JS_NewHelper(cx, ctor, inputArgs);
+    }
+    return obj;
+}
+
 JS_PUBLIC_API(JSInterruptCallback)
 JS_SetInterruptCallback(JSRuntime *rt, JSInterruptCallback callback)
 {
     JSInterruptCallback old = rt->interruptCallback;
     rt->interruptCallback = callback;
     return old;
 }
 
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3410,40 +3410,56 @@ JS_CloneFunctionObject(JSContext *cx, JS
  * true.  The intent is to support interactive compilation - accumulate
  * lines in a buffer until JS_BufferIsCompilableUnit is true, then pass it to
  * the compiler.
  */
 extern JS_PUBLIC_API(bool)
 JS_BufferIsCompilableUnit(JSContext *cx, JS::Handle<JSObject*> obj, const char *utf8,
                           size_t length);
 
-extern JS_PUBLIC_API(JSScript *)
+/*
+ * |script| will always be set. On failure, it will be set to nullptr.
+ */
+extern JS_PUBLIC_API(bool)
 JS_CompileScript(JSContext *cx, JS::HandleObject obj,
                  const char *ascii, size_t length,
-                 const JS::CompileOptions &options);
-
-extern JS_PUBLIC_API(JSScript *)
+                 const JS::CompileOptions &options,
+                 JS::MutableHandleScript script);
+
+/*
+ * |script| will always be set. On failure, it will be set to nullptr.
+ */
+extern JS_PUBLIC_API(bool)
 JS_CompileUCScript(JSContext *cx, JS::HandleObject obj,
                    const jschar *chars, size_t length,
-                   const JS::CompileOptions &options);
+                   const JS::CompileOptions &options,
+                   JS::MutableHandleScript script);
 
 extern JS_PUBLIC_API(JSObject *)
 JS_GetGlobalFromScript(JSScript *script);
 
-extern JS_PUBLIC_API(JSFunction *)
+/*
+ * |fun| will always be set. On failure, it will be set to nullptr.
+ */
+extern JS_PUBLIC_API(bool)
 JS_CompileFunction(JSContext *cx, JS::HandleObject obj, const char *name,
                    unsigned nargs, const char *const *argnames,
                    const char *bytes, size_t length,
-                   const JS::CompileOptions &options);
-
-extern JS_PUBLIC_API(JSFunction *)
+                   const JS::CompileOptions &options,
+                   JS::MutableHandleFunction fun);
+
+/*
+ * |fun| will always be set. On failure, it will be set to nullptr.
+ */
+extern JS_PUBLIC_API(bool)
 JS_CompileUCFunction(JSContext *cx, JS::HandleObject obj, const char *name,
                      unsigned nargs, const char *const *argnames,
                      const jschar *chars, size_t length,
-                     const JS::CompileOptions &options);
+                     const JS::CompileOptions &options,
+                     JS::MutableHandleFunction fun);
 
 namespace JS {
 
 /* Options for JavaScript compilation. */
 
 /*
  * In the most common use case, a CompileOptions instance is allocated on the
  * stack, and holds non-owning references to non-POD option values: strings;
@@ -3755,33 +3771,38 @@ class MOZ_STACK_CLASS JS_FRIEND_API(Comp
     }
 
     virtual bool wrap(JSContext *cx, JSCompartment *compartment) MOZ_OVERRIDE;
 
   private:
     void operator=(const CompileOptions &rhs) MOZ_DELETE;
 };
 
-extern JS_PUBLIC_API(JSScript *)
+/*
+ * |script| will always be set. On failure, it will be set to nullptr.
+ */
+extern JS_PUBLIC_API(bool)
 Compile(JSContext *cx, JS::HandleObject obj, const ReadOnlyCompileOptions &options,
-        const char *bytes, size_t length);
-
-extern JS_PUBLIC_API(JSScript *)
+        SourceBufferHolder &srcBuf, JS::MutableHandleScript script);
+
+extern JS_PUBLIC_API(bool)
 Compile(JSContext *cx, JS::HandleObject obj, const ReadOnlyCompileOptions &options,
-        const jschar *chars, size_t length);
-
-extern JS_PUBLIC_API(JSScript *)
+        const char *bytes, size_t length, JS::MutableHandleScript script);
+
+extern JS_PUBLIC_API(bool)
 Compile(JSContext *cx, JS::HandleObject obj, const ReadOnlyCompileOptions &options,
-        SourceBufferHolder &srcBuf);
-
-extern JS_PUBLIC_API(JSScript *)
-Compile(JSContext *cx, JS::HandleObject obj, const ReadOnlyCompileOptions &options, FILE *file);
-
-extern JS_PUBLIC_API(JSScript *)
-Compile(JSContext *cx, JS::HandleObject obj, const ReadOnlyCompileOptions &options, const char *filename);
+        const jschar *chars, size_t length, JS::MutableHandleScript script);
+
+extern JS_PUBLIC_API(bool)
+Compile(JSContext *cx, JS::HandleObject obj, const ReadOnlyCompileOptions &options, FILE *file,
+        JS::MutableHandleScript script);
+
+extern JS_PUBLIC_API(bool)
+Compile(JSContext *cx, JS::HandleObject obj, const ReadOnlyCompileOptions &options, const char *filename,
+        JS::MutableHandleScript script);
 
 extern JS_PUBLIC_API(bool)
 CanCompileOffThread(JSContext *cx, const ReadOnlyCompileOptions &options, size_t length);
 
 /*
  * Off thread compilation control flow.
  *
  * After successfully triggering an off thread compile of a script, the
@@ -3800,30 +3821,30 @@ CanCompileOffThread(JSContext *cx, const
 extern JS_PUBLIC_API(bool)
 CompileOffThread(JSContext *cx, const ReadOnlyCompileOptions &options,
                  const jschar *chars, size_t length,
                  OffThreadCompileCallback callback, void *callbackData);
 
 extern JS_PUBLIC_API(JSScript *)
 FinishOffThreadScript(JSContext *maybecx, JSRuntime *rt, void *token);
 
-extern JS_PUBLIC_API(JSFunction *)
+extern JS_PUBLIC_API(bool)
 CompileFunction(JSContext *cx, JS::HandleObject obj, const ReadOnlyCompileOptions &options,
                 const char *name, unsigned nargs, const char *const *argnames,
-                const char *bytes, size_t length);
-
-extern JS_PUBLIC_API(JSFunction *)
+                SourceBufferHolder &srcBuf, JS::MutableHandleFunction fun);
+
+extern JS_PUBLIC_API(bool)
 CompileFunction(JSContext *cx, JS::HandleObject obj, const ReadOnlyCompileOptions &options,
                 const char *name, unsigned nargs, const char *const *argnames,
-                const jschar *chars, size_t length);
-
-extern JS_PUBLIC_API(JSFunction *)
+                const char *bytes, size_t length, JS::MutableHandleFunction fun);
+
+extern JS_PUBLIC_API(bool)
 CompileFunction(JSContext *cx, JS::HandleObject obj, const ReadOnlyCompileOptions &options,
                 const char *name, unsigned nargs, const char *const *argnames,
-                SourceBufferHolder &srcBuf);
+                const jschar *chars, size_t length, JS::MutableHandleFunction fun);
 
 } /* namespace JS */
 
 extern JS_PUBLIC_API(JSString *)
 JS_DecompileScript(JSContext *cx, JS::Handle<JSScript*> script, const char *name, unsigned indent);
 
 /*
  * API extension: OR this into indent to avoid pretty-printing the decompiled
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -919,17 +919,17 @@ JSCompartment::removeDebuggeeUnderGC(Fre
     if (debuggees.empty()) {
         debugModeBits &= ~DebugFromJS;
         if (wasEnabled && !debugMode())
             DebugScopes::onCompartmentLeaveDebugMode(this);
     }
 }
 
 void
-JSCompartment::clearBreakpointsIn(FreeOp *fop, js::Debugger *dbg, JSObject *handler)
+JSCompartment::clearBreakpointsIn(FreeOp *fop, js::Debugger *dbg, HandleObject handler)
 {
     for (gc::ZoneCellIter i(zone(), gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
         if (script->compartment() == this && script->hasAnyBreakpointsOrStepMode())
             script->clearBreakpointsIn(fop, dbg, handler);
     }
 }
 
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -420,17 +420,17 @@ struct JSCompartment
     void removeDebuggeeUnderGC(js::FreeOp *fop, js::GlobalObject *global,
                                js::GlobalObjectSet::Enum *debuggeesEnum = nullptr);
     void removeDebuggeeUnderGC(js::FreeOp *fop, js::GlobalObject *global,
                                js::AutoDebugModeInvalidation &invalidate,
                                js::GlobalObjectSet::Enum *debuggeesEnum = nullptr);
     bool setDebugModeFromC(JSContext *cx, bool b,
                            js::AutoDebugModeInvalidation &invalidate);
 
-    void clearBreakpointsIn(js::FreeOp *fop, js::Debugger *dbg, JSObject *handler);
+    void clearBreakpointsIn(js::FreeOp *fop, js::Debugger *dbg, JS::HandleObject handler);
     void clearTraps(js::FreeOp *fop);
 
   private:
     void sweepBreakpoints(js::FreeOp *fop);
 
   public:
     js::WatchpointMap *watchpointMap;
 
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -49,17 +49,16 @@
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 using namespace js::frontend;
 
 using mozilla::ArrayLength;
 using mozilla::PodCopy;
-using mozilla::Range;
 using mozilla::RangedPtr;
 
 static bool
 fun_getProperty(JSContext *cx, HandleObject obj_, HandleId id, MutableHandleValue vp)
 {
     RootedObject obj(cx, obj_);
     while (!obj->is<JSFunction>()) {
         if (!JSObject::getProto(cx, obj, &obj))
@@ -760,17 +759,17 @@ js::FindBody(JSContext *cx, HandleFuncti
         options.setVersion(fun->nonLazyScript()->getVersion());
 
     AutoKeepAtoms keepAtoms(cx->perThreadData);
 
     AutoStableStringChars stableChars(cx);
     if (!stableChars.initTwoByte(cx, src))
         return false;
 
-    const Range<const jschar> srcChars = stableChars.twoByteRange();
+    const mozilla::Range<const jschar> srcChars = stableChars.twoByteRange();
     TokenStream ts(cx, options, srcChars.start().get(), srcChars.length(), nullptr);
     int nest = 0;
     bool onward = true;
     // Skip arguments list.
     do {
         switch (ts.getToken()) {
           case TOK_NAME:
           case TOK_YIELD:
@@ -1537,17 +1536,17 @@ FunctionConstructor(JSContext *cx, unsig
     AutoKeepAtoms keepAtoms(cx->perThreadData);
     AutoNameVector formals(cx);
 
     bool hasRest = false;
 
     bool isStarGenerator = generatorKind == StarGenerator;
     JS_ASSERT(generatorKind != LegacyGenerator);
 
-    JSScript *maybeScript = nullptr;
+    RootedScript maybeScript(cx);
     const char *filename;
     unsigned lineno;
     JSPrincipals *originPrincipals;
     uint32_t pcOffset;
     DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset,
                                          &originPrincipals);
 
     const char *introductionType = "Function";
@@ -1736,17 +1735,17 @@ FunctionConstructor(JSContext *cx, unsig
 
     if (hasRest)
         fun->setHasRest();
 
     AutoStableStringChars stableChars(cx);
     if (!stableChars.initTwoByte(cx, str))
         return false;
 
-    Range<const jschar> chars = stableChars.twoByteRange();
+    mozilla::Range<const jschar> chars = stableChars.twoByteRange();
     SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller()
                                               ? SourceBufferHolder::GiveOwnership
                                               : SourceBufferHolder::NoOwnership;
     bool ok;
     SourceBufferHolder srcBuf(chars.start().get(), chars.length(), ownership);
     if (isStarGenerator)
         ok = frontend::CompileStarGeneratorBody(cx, &fun, options, formals, srcBuf);
     else
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -196,16 +196,17 @@
 #include "jsweakmap.h"
 #ifdef XP_WIN
 # include "jswin.h"
 #endif
 #include "prmjtime.h"
 
 #include "gc/FindSCCs.h"
 #include "gc/GCInternals.h"
+#include "gc/GCTrace.h"
 #include "gc/Marking.h"
 #include "gc/Memory.h"
 #ifdef JS_ION
 # include "jit/BaselineJIT.h"
 #endif
 #include "jit/IonCode.h"
 #include "js/SliceBudget.h"
 #include "vm/Debugger.h"
@@ -485,16 +486,17 @@ Arena::finalize(FreeOp *fop, AllocKind t
                                                  thing - thingSize);
                 newListTail = newListTail->nextSpanUnchecked();
             }
             firstThingOrSuccessorOfLastMarkedThing = thing + thingSize;
             nmarked++;
         } else {
             t->finalize(fop);
             JS_POISON(t, JS_SWEPT_TENURED_PATTERN, thingSize);
+            TraceTenuredFinalize(t);
         }
     }
 
     if (nmarked == 0) {
         // Do nothing. The caller will update the arena header appropriately.
         JS_ASSERT(newListTail == &newListHead);
         JS_EXTRA_POISON(data, JS_SWEPT_TENURED_PATTERN, sizeof(data));
         return true;
@@ -1275,16 +1277,19 @@ GCRuntime::init(uint32_t maxbytes)
         return false;
 #endif
 
 #ifdef JS_GC_ZEAL
     if (!initZeal())
         return false;
 #endif
 
+    if (!InitTrace(*this))
+        return false;
+
     if (!marker.init(mode))
         return false;
 
     return true;
 }
 
 void
 GCRuntime::recordNativeStackTop()
@@ -1338,16 +1343,18 @@ GCRuntime::finish()
     FinishPersistentRootedChains(rt);
 
 #ifdef JS_THREADSAFE
     if (lock) {
         PR_DestroyLock(lock);
         lock = nullptr;
     }
 #endif
+
+    FinishTrace();
 }
 
 void
 js::gc::FinishPersistentRootedChains(JSRuntime *rt)
 {
     /* The lists of persistent roots are stored on the shadow runtime. */
     rt->functionPersistentRooteds.clear();
     rt->idPersistentRooteds.clear();
@@ -2995,25 +3002,28 @@ PurgeRuntime(JSRuntime *rt)
     rt->uncompressedSourceCache.purge();
     rt->evalCache.clear();
 
     if (!rt->hasActiveCompilations())
         rt->parseMapPool().purgeAll();
 }
 
 bool
-GCRuntime::shouldPreserveJITCode(JSCompartment *comp, int64_t currentTime)
+GCRuntime::shouldPreserveJITCode(JSCompartment *comp, int64_t currentTime,
+                                 JS::gcreason::Reason reason)
 {
     if (cleanUpEverything)
         return false;
 
     if (alwaysPreserveCode)
         return true;
     if (comp->lastAnimationTime + PRMJ_USEC_PER_SEC >= currentTime)
         return true;
+    if (reason == JS::gcreason::DEBUG_GC)
+        return true;
 
 #ifdef JS_ION
     if (comp->jitCompartment() && comp->jitCompartment()->hasRecentParallelActivity())
         return true;
 #endif
 
     return false;
 }
@@ -3114,17 +3124,17 @@ GCRuntime::checkForCompartmentMismatches
                 JS_TraceChildren(&trc, trc.src, trc.srcKind);
             }
         }
     }
 }
 #endif
 
 bool
-GCRuntime::beginMarkPhase()
+GCRuntime::beginMarkPhase(JS::gcreason::Reason reason)
 {
     int64_t currentTime = PRMJ_Now();
 
 #ifdef DEBUG
     if (fullCompartmentChecks)
         checkForCompartmentMismatches();
 #endif
 
@@ -3151,17 +3161,17 @@ GCRuntime::beginMarkPhase()
         zone->scheduledForDestruction = false;
         zone->maybeAlive = false;
         zone->setPreservingCode(false);
     }
 
     for (CompartmentsIter c(rt, WithAtoms); !c.done(); c.next()) {
         JS_ASSERT(c->gcLiveArrayBuffers.empty());
         c->marked = false;
-        if (shouldPreserveJITCode(c, currentTime))
+        if (shouldPreserveJITCode(c, currentTime, reason))
             c->zone()->setPreservingCode(true);
     }
 
     if (!rt->gc.cleanUpEverything) {
 #ifdef JS_ION
         if (JSCompartment *comp = jit::TopmostIonActivationCompartment(rt))
             comp->zone()->setPreservingCode(true);
 #endif
@@ -4251,17 +4261,17 @@ GCRuntime::beginSweepPhase(bool lastGC)
 
     JS_ASSERT(!abortSweepAfterCurrentGroup);
 
     computeNonIncrementalMarkingForValidation();
 
     gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP);
 
 #ifdef JS_THREADSAFE
-    sweepOnBackgroundThread = !lastGC;
+    sweepOnBackgroundThread = !lastGC && !TraceEnabled();
 #endif
 
 #ifdef DEBUG
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
         JS_ASSERT(!c->gcIncomingGrayPointers);
         for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
             if (e.front().key().kind != CrossCompartmentKey::StringWrapper)
                 AssertNotOnGrayList(&e.front().value().get().toObject());
@@ -4767,17 +4777,17 @@ GCRuntime::incrementalCollectSlice(int64
     }
 
     if (incrementalState == MARK)
         AutoGCRooter::traceAllWrappers(&marker);
 
     switch (incrementalState) {
 
       case MARK_ROOTS:
-        if (!beginMarkPhase()) {
+        if (!beginMarkPhase(reason)) {
             incrementalState = NO_INCREMENTAL;
             return;
         }
 
         if (!lastGC)
             pushZealSelectedObjects();
 
         incrementalState = MARK;
@@ -4960,16 +4970,18 @@ GCRuntime::gcCycle(bool incremental, int
     } else {
         budgetIncrementalGC(&budget);
     }
 
     /* The GC was reset, so we need a do-over. */
     if (prevState != NO_INCREMENTAL && incrementalState == NO_INCREMENTAL)
         return true;
 
+    TraceMajorGCStart();
+
     incrementalCollectSlice(budget, reason, gckind);
 
 #ifndef JS_MORE_DETERMINISTIC
     nextFullGCTime = PRMJ_Now() + GC_IDLE_FULL_SPAN;
 #endif
 
     chunkAllocationSinceLastGC = false;
 
@@ -4981,16 +4993,18 @@ GCRuntime::gcCycle(bool incremental, int
     /* Clear gcMallocBytes for all compartments */
     for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
         zone->resetGCMallocBytes();
         zone->unscheduleGC();
     }
 
     resetMallocBytes();
 
+    TraceMajorGCEnd();
+
     return false;
 }
 
 #ifdef JS_GC_ZEAL
 static bool
 IsDeterministicGCReason(JS::gcreason::Reason reason)
 {
     if (reason > JS::gcreason::DEBUG_GC &&
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jsgcinlines_h
 #define jsgcinlines_h
 
 #include "jsgc.h"
 
+#include "gc/GCTrace.h"
 #include "gc/Zone.h"
 #include "vm/ForkJoin.h"
 
 namespace js {
 
 class Shape;
 
 /*
@@ -644,16 +645,17 @@ AllocateObject(ThreadSafeContext *cx, Al
         obj = static_cast<JSObject *>(js::gc::ArenaLists::refillFreeList<allowGC>(cx, kind));
 
     if (obj)
         obj->setInitialSlots(slots);
     else
         js_free(slots);
 
     CheckIncrementalZoneState(cx, obj);
+    js::gc::TraceTenuredAlloc(obj, kind);
     return obj;
 }
 
 template <typename T, AllowGC allowGC>
 inline T *
 AllocateNonObject(ThreadSafeContext *cx)
 {
     static_assert(sizeof(T) >= CellSize,
@@ -666,16 +668,17 @@ AllocateNonObject(ThreadSafeContext *cx)
     if (!CheckAllocatorState<allowGC>(cx, kind))
         return nullptr;
 
     T *t = static_cast<T *>(cx->allocator()->arenas.allocateFromFreeList(kind, thingSize));
     if (!t)
         t = static_cast<T *>(js::gc::ArenaLists::refillFreeList<allowGC>(cx, kind));
 
     CheckIncrementalZoneState(cx, t);
+    js::gc::TraceTenuredAlloc(t, kind);
     return t;
 }
 
 /*
  * When allocating for initialization from a cached object copy, we will
  * potentially destroy the cache entry we want to copy if we allow GC. On the
  * other hand, since these allocations are extremely common, we don't want to
  * delay GC from these allocation sites. Instead we allow the GC, but still
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -40,17 +40,16 @@
 using namespace js;
 using namespace js::types;
 
 using mozilla::Abs;
 using mozilla::MinNumberValue;
 using mozilla::NegativeInfinity;
 using mozilla::PodCopy;
 using mozilla::PositiveInfinity;
-using mozilla::Range;
 using mozilla::RangedPtr;
 
 using JS::AutoCheckCannotGC;
 using JS::GenericNaN;
 
 /*
  * If we're accumulating a decimal number and the number is >= 2^53, then the
  * fast result from the loop in Get{Prefix,Decimal}Integer may be inaccurate.
@@ -173,17 +172,17 @@ ComputeAccurateBinaryBaseInteger(const C
         value *= factor;
     }
 
     return value;
 }
 
 template <typename CharT>
 double
-js::ParseDecimalNumber(const Range<const CharT> chars)
+js::ParseDecimalNumber(const mozilla::Range<const CharT> chars)
 {
     MOZ_ASSERT(chars.length() > 0);
     uint64_t dec = 0;
     RangedPtr<const CharT> s = chars.start(), end = chars.end();
     do {
         CharT c = *s;
         MOZ_ASSERT('0' <= c && c <= '9');
         uint8_t digit = c - '0';
@@ -191,20 +190,20 @@ js::ParseDecimalNumber(const Range<const
         MOZ_ASSERT(next < DOUBLE_INTEGRAL_PRECISION_LIMIT,
                    "next value won't be an integrally-precise double");
         dec = next;
     } while (++s < end);
     return static_cast<double>(dec);
 }
 
 template double
-js::ParseDecimalNumber(const Range<const Latin1Char> chars);
+js::ParseDecimalNumber(const mozilla::Range<const Latin1Char> chars);
 
 template double
-js::ParseDecimalNumber(const Range<const jschar> chars);
+js::ParseDecimalNumber(const mozilla::Range<const jschar> chars);
 
 template <typename CharT>
 bool
 js::GetPrefixInteger(ThreadSafeContext *cx, const CharT *start, const CharT *end, int base,
                      const CharT **endp, double *dp)
 {
     JS_ASSERT(start <= end);
     JS_ASSERT(2 <= base && base <= 36);
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -530,16 +530,18 @@ JSObject::create(js::ExclusiveContext *c
 
     if (clasp->hasPrivate())
         obj->privateRef(shape->numFixedSlots()) = nullptr;
 
     size_t span = shape->slotSpan();
     if (span)
         obj->initializeSlotRange(0, span);
 
+    js::gc::TraceCreateObject(obj);
+
     return obj;
 }
 
 /* static */ inline js::ArrayObject *
 JSObject::createArray(js::ExclusiveContext *cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
                       js::HandleShape shape, js::HandleTypeObject type,
                       uint32_t length)
 {
@@ -565,16 +567,18 @@ JSObject::createArray(js::ExclusiveConte
     obj->type_.init(type);
     obj->setFixedElements();
     new (obj->getElementsHeader()) js::ObjectElements(capacity, length);
 
     size_t span = shape->slotSpan();
     if (span)
         obj->initializeSlotRange(0, span);
 
+    js::gc::TraceCreateObject(obj);
+
     return &obj->as<js::ArrayObject>();
 }
 
 inline void
 JSObject::finish(js::FreeOp *fop)
 {
     if (hasDynamicSlots())
         fop->free_(slots);
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -27,17 +27,16 @@
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 using mozilla::IsFinite;
 using mozilla::Maybe;
-using mozilla::Range;
 using mozilla::RangedPtr;
 
 const Class js::JSONClass = {
     js_JSON_str,
     JSCLASS_HAS_CACHED_PROTO(JSProto_JSON),
     JS_PropertyStub,        /* addProperty */
     JS_DeletePropertyStub,  /* delProperty */
     JS_PropertyStub,        /* getProperty */
@@ -792,36 +791,36 @@ Revive(JSContext *cx, HandleValue revive
         return false;
 
     Rooted<jsid> id(cx, NameToId(cx->names().empty));
     return Walk(cx, obj, id, reviver, vp);
 }
 
 template <typename CharT>
 bool
-js::ParseJSONWithReviver(JSContext *cx, const Range<const CharT> chars, HandleValue reviver,
+js::ParseJSONWithReviver(JSContext *cx, const mozilla::Range<const CharT> chars, HandleValue reviver,
                          MutableHandleValue vp)
 {
     /* 15.12.2 steps 2-3. */
     JSONParser<CharT> parser(cx, chars);
     if (!parser.parse(vp))
         return false;
 
     /* 15.12.2 steps 4-5. */
     if (IsCallable(reviver))
         return Revive(cx, reviver, vp);
     return true;
 }
 
 template bool
-js::ParseJSONWithReviver(JSContext *cx, const Range<const Latin1Char> chars, HandleValue reviver,
-                         MutableHandleValue vp);
+js::ParseJSONWithReviver(JSContext *cx, const mozilla::Range<const Latin1Char> chars,
+                         HandleValue reviver, MutableHandleValue vp);
 
 template bool
-js::ParseJSONWithReviver(JSContext *cx, const Range<const jschar> chars, HandleValue reviver,
+js::ParseJSONWithReviver(JSContext *cx, const mozilla::Range<const jschar> chars, HandleValue reviver,
                          MutableHandleValue vp);
 
 #if JS_HAS_TOSOURCE
 static bool
 json_toSource(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setString(cx->names().JSON);
--- a/js/src/jsonparser.cpp
+++ b/js/src/jsonparser.cpp
@@ -17,17 +17,16 @@
 #include "jsprf.h"
 
 #include "vm/StringBuffer.h"
 
 #include "jsobjinlines.h"
 
 using namespace js;
 
-using mozilla::Range;
 using mozilla::RangedPtr;
 
 JSONParserBase::~JSONParserBase()
 {
     for (size_t i = 0; i < stack.length(); i++) {
         if (stack[i].state == FinishArrayElement)
             js_delete(&stack[i].elements());
         else
@@ -272,17 +271,17 @@ JSONParser<CharT>::readNumber()
         for (; current < end; current++) {
             if (!JS7_ISDEC(*current))
                 break;
         }
     }
 
     /* Fast path: no fractional or exponent part. */
     if (current == end || (*current != '.' && *current != 'e' && *current != 'E')) {
-        Range<const CharT> chars(digitStart.get(), current - digitStart);
+        mozilla::Range<const CharT> chars(digitStart.get(), current - digitStart);
         if (chars.length() < strlen("9007199254740992")) {
             // If the decimal number is shorter than the length of 2**53, (the
             // largest number a double can represent with integral precision),
             // parse it using a decimal-only parser.  This comparison is
             // conservative but faster than a fully-precise check.
             double d = ParseDecimalNumber(chars);
             return numberToken(negative ? -d : d);
         }
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -2879,57 +2879,57 @@ js_GetScriptLineExtent(JSScript *script)
         if (maxLineNo < lineno)
             maxLineNo = lineno;
     }
 
     return 1 + maxLineNo - script->lineno();
 }
 
 void
-js::DescribeScriptedCallerForCompilation(JSContext *cx, JSScript **maybeScript,
+js::DescribeScriptedCallerForCompilation(JSContext *cx, MutableHandleScript maybeScript,
                                          const char **file, unsigned *linenop,
                                          uint32_t *pcOffset, JSPrincipals **origin,
                                          LineOption opt)
 {
     if (opt == CALLED_FROM_JSOP_EVAL) {
         jsbytecode *pc = nullptr;
-        *maybeScript = cx->currentScript(&pc);
+        maybeScript.set(cx->currentScript(&pc));
         JS_ASSERT(JSOp(*pc) == JSOP_EVAL || JSOp(*pc) == JSOP_SPREADEVAL);
         JS_ASSERT(*(pc + (JSOp(*pc) == JSOP_EVAL ? JSOP_EVAL_LENGTH
                                                  : JSOP_SPREADEVAL_LENGTH)) == JSOP_LINENO);
-        *file = (*maybeScript)->filename();
+        *file = maybeScript->filename();
         *linenop = GET_UINT16(pc + (JSOp(*pc) == JSOP_EVAL ? JSOP_EVAL_LENGTH
                                                            : JSOP_SPREADEVAL_LENGTH));
-        *pcOffset = pc - (*maybeScript)->code();
-        *origin = (*maybeScript)->originPrincipals();
+        *pcOffset = pc - maybeScript->code();
+        *origin = maybeScript->originPrincipals();
         return;
     }
 
     NonBuiltinFrameIter iter(cx);
 
     if (iter.done()) {
-        *maybeScript = nullptr;
+        maybeScript.set(nullptr);
         *file = nullptr;
         *linenop = 0;
         *pcOffset = 0;
         *origin = cx->compartment()->principals;
         return;
     }
 
     *file = iter.scriptFilename();
     *linenop = iter.computeLine();
     *origin = iter.originPrincipals();
 
     // These values are only used for introducer fields which are debugging
     // information and can be safely left null for asm.js frames.
     if (iter.hasScript()) {
-        *maybeScript = iter.script();
-        *pcOffset = iter.pc() - (*maybeScript)->code();
+        maybeScript.set(iter.script());
+        *pcOffset = iter.pc() - maybeScript->code();
     } else {
-        *maybeScript = nullptr;
+        maybeScript.set(nullptr);
         *pcOffset = 0;
     }
 }
 
 template <class T>
 static inline T *
 Rebase(JSScript *dst, JSScript *src, T *srcp)
 {
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -2018,17 +2018,17 @@ PCToLineNumber(unsigned startLine, jssrc
  */
 
 enum LineOption {
     CALLED_FROM_JSOP_EVAL,
     NOT_CALLED_FROM_JSOP_EVAL
 };
 
 extern void
-DescribeScriptedCallerForCompilation(JSContext *cx, JSScript **maybeScript,
+DescribeScriptedCallerForCompilation(JSContext *cx, MutableHandleScript maybeScript,
                                      const char **file, unsigned *linenop,
                                      uint32_t *pcOffset, JSPrincipals **origin,
                                      LineOption opt = NOT_CALLED_FROM_JSOP_EVAL);
 
 bool
 CloneFunctionScript(JSContext *cx, HandleFunction original, HandleFunction clone,
                     NewObjectKind newKind = GenericObject);
 
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -70,17 +70,16 @@ using JS::Symbol;
 using JS::SymbolCode;
 
 using mozilla::CheckedInt;
 using mozilla::IsNaN;
 using mozilla::IsNegativeZero;
 using mozilla::IsSame;
 using mozilla::PodCopy;
 using mozilla::PodEqual;
-using mozilla::Range;
 using mozilla::RangedPtr;
 using mozilla::SafeCast;
 
 using JS::AutoCheckCannotGC;
 
 static JSLinearString *
 ArgToRootedString(JSContext *cx, CallArgs &args, unsigned argno)
 {
@@ -239,17 +238,17 @@ Unhex2(const RangedPtr<const CharT> char
         return false;
 
     *result = (JS7_UNHEX(a) << 4) + JS7_UNHEX(b);
     return true;
 }
 
 template <typename CharT>
 static bool
-Unescape(StringBuffer &sb, const Range<const CharT> chars)
+Unescape(StringBuffer &sb, const mozilla::Range<const CharT> chars)
 {
     /*
      * NB: use signed integers for length/index to allow simple length
      * comparisons without unsigned-underflow hazards.
      */
     static_assert(JSString::MAX_LENGTH <= INT_MAX, "String length must fit in a signed integer");
     int length = SafeCast<int>(chars.length());
 
@@ -4338,17 +4337,17 @@ CanStoreCharsAsLatin1(const jschar *s, s
 static bool
 CanStoreCharsAsLatin1(const Latin1Char *s, size_t length)
 {
     MOZ_CRASH("Shouldn't be called for Latin1 chars");
 }
 
 template <AllowGC allowGC>
 static MOZ_ALWAYS_INLINE JSInlineString *
-NewFatInlineStringDeflated(ThreadSafeContext *cx, Range<const jschar> chars)
+NewFatInlineStringDeflated(ThreadSafeContext *cx, mozilla::Range<const jschar> chars)
 {
     MOZ_ASSERT(EnableLatin1Strings);
 
     size_t len = chars.length();
     Latin1Char *storage;
     JSInlineString *str = AllocateFatInlineString<allowGC>(cx, len, &storage);
     if (!str)
         return nullptr;
@@ -4363,17 +4362,17 @@ NewFatInlineStringDeflated(ThreadSafeCon
 
 template <AllowGC allowGC>
 static JSFlatString *
 NewStringDeflated(ThreadSafeContext *cx, const jschar *s, size_t n)
 {
     MOZ_ASSERT(EnableLatin1Strings);
 
     if (JSFatInlineString::latin1LengthFits(n))
-        return NewFatInlineStringDeflated<allowGC>(cx, Range<const jschar>(s, n));
+        return NewFatInlineStringDeflated<allowGC>(cx, mozilla::Range<const jschar>(s, n));
 
     ScopedJSFreePtr<Latin1Char> news(cx->pod_malloc<Latin1Char>(n + 1));
     if (!news)
         return nullptr;
 
     for (size_t i = 0; i < n; i++) {
         MOZ_ASSERT(s[i] <= JSString::MAX_LATIN1_CHAR);
         news.get()[i] = Latin1Char(s[i]);
@@ -4464,17 +4463,17 @@ js::NewString<NoGC>(ThreadSafeContext *c
 namespace js {
 
 template <AllowGC allowGC, typename CharT>
 JSFlatString *
 NewStringCopyNDontDeflate(ThreadSafeContext *cx, const CharT *s, size_t n)
 {
     if (EnableLatin1Strings) {
         if (JSFatInlineString::lengthFits<CharT>(n))
-            return NewFatInlineString<allowGC>(cx, Range<const CharT>(s, n));
+            return NewFatInlineString<allowGC>(cx, mozilla::Range<const CharT>(s, n));
 
         ScopedJSFreePtr<CharT> news(cx->pod_malloc<CharT>(n + 1));
         if (!news)
             return nullptr;
 
         PodCopy(news.get(), s, n);
         news[n] = 0;
 
@@ -4482,17 +4481,17 @@ NewStringCopyNDontDeflate(ThreadSafeCont
         if (!str)
             return nullptr;
 
         news.forget();
         return str;
     }
 
     if (JSFatInlineString::twoByteLengthFits(n))
-        return NewFatInlineString<allowGC>(cx, Range<const CharT>(s, n));
+        return NewFatInlineString<allowGC>(cx, mozilla::Range<const CharT>(s, n));
 
     ScopedJSFreePtr<jschar> news(cx->pod_malloc<jschar>(n + 1));
     if (!news)
         return nullptr;
 
     CopyCharsMaybeInflate(news.get(), s, n);
     news[n] = 0;
 
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -111,16 +111,17 @@ UNIFIED_SOURCES += [
     'frontend/BytecodeEmitter.cpp',
     'frontend/FoldConstants.cpp',
     'frontend/NameFunctions.cpp',
     'frontend/ParseMaps.cpp',
     'frontend/ParseNode.cpp',
     'frontend/TokenStream.cpp',
     'gc/Barrier.cpp',
     'gc/ForkJoinNursery.cpp',
+    'gc/GCTrace.cpp',
     'gc/Iteration.cpp',
     'gc/Marking.cpp',
     'gc/Memory.cpp',
     'gc/Nursery.cpp',
     'gc/RootMarking.cpp',
     'gc/Statistics.cpp',
     'gc/StoreBuffer.cpp',
     'gc/Tracer.cpp',
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -81,17 +81,16 @@
 
 using namespace js;
 using namespace js::cli;
 
 using mozilla::ArrayLength;
 using mozilla::NumberEqualsInt32;
 using mozilla::Maybe;
 using mozilla::PodCopy;
-using mozilla::Range;
 
 enum JSShellExitCode {
     EXITCODE_RUNTIME_ERROR      = 3,
     EXITCODE_FILE_NOT_FOUND     = 4,
     EXITCODE_OUT_OF_MEMORY      = 5,
     EXITCODE_TIMEOUT            = 6
 };
 
@@ -432,17 +431,17 @@ RunFile(JSContext *cx, Handle<JSObject*>
 
         CompileOptions options(cx);
         options.setIntroductionType("js shell file")
                .setUTF8(true)
                .setFileAndLine(filename, 1)
                .setCompileAndGo(true);
 
         gGotError = false;
-        script = JS::Compile(cx, obj, options, file);
+        (void) JS::Compile(cx, obj, options, file, &script);
         JS_ASSERT_IF(!script, gGotError);
     }
 
     #ifdef DEBUG
         if (dumpEntrainedVariables)
             AnalyzeEntrainedVariables(cx, script);
     #endif
     if (script && !compileOnly) {
@@ -462,18 +461,17 @@ EvalAndPrint(JSContext *cx, Handle<JSObj
 {
     // Eval.
     JS::CompileOptions options(cx);
     options.setIntroductionType("js shell interactive")
            .setUTF8(true)
            .setCompileAndGo(true)
            .setFileAndLine("typein", lineno);
     RootedScript script(cx);
-    script = JS::Compile(cx, global, options, bytes, length);
-    if (!script)
+    if (!JS::Compile(cx, global, options, bytes, length, &script))
         return false;
     if (compileOnly)
         return true;
     RootedValue result(cx);
     if (!JS_ExecuteScript(cx, global, script, &result))
         return false;
 
     if (!result.isUndefined()) {
@@ -851,17 +849,18 @@ LoadScript(JSContext *cx, unsigned argc,
         if (!filename)
             return false;
         errno = 0;
         CompileOptions opts(cx);
         opts.setIntroductionType("js shell load")
             .setUTF8(true)
             .setCompileAndGo(true)
             .setNoScriptRval(true);
-        if ((compileOnly && !Compile(cx, thisobj, opts, filename.ptr())) ||
+        RootedScript script(cx);
+        if ((compileOnly && !Compile(cx, thisobj, opts, filename.ptr(), &script)) ||
             !Evaluate(cx, thisobj, opts, filename.ptr()))
         {
             return false;
         }
     }
 
     args.rval().setUndefined();
     return true;
@@ -1263,18 +1262,18 @@ Evaluate(JSContext *cx, unsigned argc, j
                     return false;
                 }
                 JS::CompartmentOptionsRef(cx).cloneSingletonsOverride().set(true);
             }
 
             if (loadBytecode) {
                 script = JS_DecodeScript(cx, loadBuffer, loadLength, options.originPrincipals(cx));
             } else {
-                Range<const jschar> chars = codeChars.twoByteRange();
-                script = JS::Compile(cx, global, options, chars.start().get(), chars.length());
+                mozilla::Range<const jschar> chars = codeChars.twoByteRange();
+                (void) JS::Compile(cx, global, options, chars.start().get(), chars.length(), &script);
             }
 
             if (!script)
                 return false;
         }
 
         if (displayURL && !script->scriptSource()->hasDisplayURL()) {
             JSFlatString *flat = displayURL->ensureFlat(cx);
@@ -1467,18 +1466,17 @@ Run(JSContext *cx, unsigned argc, jsval 
     {
         JS::AutoSaveContextOptions asco(cx);
         JS::ContextOptionsRef(cx).setNoScriptRval(true);
 
         JS::CompileOptions options(cx);
         options.setIntroductionType("js shell run")
                .setFileAndLine(filename.ptr(), 1)
                .setCompileAndGo(true);
-        script = JS_CompileUCScript(cx, thisobj, ucbuf, buflen, options);
-        if (!script)
+        if (!JS_CompileUCScript(cx, thisobj, ucbuf, buflen, options, &script))
             return false;
     }
 
     if (!JS_ExecuteScript(cx, thisobj, script))
         return false;
 
     int64_t endClock = PRMJ_Now();
 
@@ -1805,17 +1803,17 @@ TrapHandler(JSContext *cx, JSScript *, j
     /* Debug-mode currently disables Ion compilation. */
     JSAbstractFramePtr frame(iter.abstractFramePtr().raw(), iter.pc());
     RootedScript script(cx, iter.script());
 
     AutoStableStringChars stableChars(cx);
     if (!stableChars.initTwoByte(cx, str))
         return JSTRAP_ERROR;
 
-    Range<const jschar> chars = stableChars.twoByteRange();
+    mozilla::Range<const jschar> chars = stableChars.twoByteRange();
     if (!frame.evaluateUCInStackFrame(cx, chars.start().get(), chars.length(),
                                       script->filename(),
                                       script->lineno(),
                                       &rval))
     {
         *rvalArg = rval;
         return JSTRAP_ERROR;
     }
@@ -2314,18 +2312,17 @@ DisassFile(JSContext *cx, unsigned argc,
         JS::ContextOptionsRef(cx).setNoScriptRval(true);
 
         CompileOptions options(cx);
         options.setIntroductionType("js shell disFile")
                .setUTF8(true)
                .setFileAndLine(filename.ptr(), 1)
                .setCompileAndGo(true);
 
-        script = JS::Compile(cx, thisobj, options, filename.ptr());
-        if (!script)
+        if (!JS::Compile(cx, thisobj, options, filename.ptr(), &script))
             return false;
     }
 
     Sprinter sprinter(cx);
     if (!sprinter.init())
         return false;
     bool ok = DisassembleScript(cx, script, NullPtr(), p.lines, p.recursive, &sprinter);
     if (ok)
@@ -2527,17 +2524,17 @@ Intern(JSContext *cx, unsigned argc, jsv
     JSString *str = JS::ToString(cx, args.get(0));
     if (!str)
         return false;
 
     AutoStableStringChars strChars(cx);
     if (!strChars.initTwoByte(cx, str))
         return false;
 
-    Range<const jschar> chars = strChars.twoByteRange();
+    mozilla::Range<const jschar> chars = strChars.twoByteRange();
 
     if (!JS_InternUCStringN(cx, chars.start().get(), chars.length()))
         return false;
 
     args.rval().setUndefined();
     return true;
 }
 
@@ -2762,17 +2759,17 @@ EvalInContext(JSContext *cx, unsigned ar
     RootedObject sobj(cx);
     if (!JS_ConvertArguments(cx, args, "S / o", str.address(), sobj.address()))
         return false;
 
     AutoStableStringChars strChars(cx);
     if (!strChars.initTwoByte(cx, str))
         return false;
 
-    Range<const jschar> chars = strChars.twoByteRange();
+    mozilla::Range<const jschar> chars = strChars.twoByteRange();
     size_t srclen = chars.length();
     const jschar *src = chars.start().get();
 
     bool lazy = false;
     if (srclen == 4) {
         if (src[0] == 'l' && src[1] == 'a' && src[2] == 'z' && src[3] == 'y') {
             lazy = true;
             srclen = 0;
@@ -2860,17 +2857,17 @@ EvalInFrame(JSContext *cx, unsigned argc
             return false;
         ac.construct(cx, DefaultObjectForContextOrNull(cx));
     }
 
     AutoStableStringChars stableChars(cx);
     if (!stableChars.initTwoByte(cx, str))
         return JSTRAP_ERROR;
 
-    Range<const jschar> chars = stableChars.twoByteRange();
+    mozilla::Range<const jschar> chars = stableChars.twoByteRange();
     JSAbstractFramePtr frame(fi.abstractFramePtr().raw(), fi.pc());
     RootedScript fpscript(cx, frame.script());
     bool ok = !!frame.evaluateUCInStackFrame(cx, chars.start().get(), chars.length(),
                                              fpscript->filename(),
                                              JS_PCToLineNumber(cx, fpscript,
                                                                fi.pc()),
                                              MutableHandleValue::fromMarkedLocation(vp));
     return ok;
@@ -2920,19 +2917,18 @@ WorkerMain(void *arg)
             break;
 
         JSAutoCompartment ac(cx, global);
 
         JS::CompileOptions options(cx);
         options.setFileAndLine("<string>", 1)
                .setCompileAndGo(true);
 
-        RootedScript script(cx, JS::Compile(cx, global, options,
-                                            input->chars, input->length));
-        if (!script)
+        RootedScript script(cx);
+        if (!JS::Compile(cx, global, options, input->chars, input->length, &script))
             break;
         RootedValue result(cx);
         JS_ExecuteScript(cx, global, script, &result);
     } while (0);
 
     DestroyContext(cx, false);
     JS_DestroyRuntime(rt);
 
@@ -3555,20 +3551,20 @@ Compile(JSContext *cx, unsigned argc, js
         return false;
 
     JS::AutoSaveContextOptions asco(cx);
     JS::ContextOptionsRef(cx).setNoScriptRval(true);
     JS::CompileOptions options(cx);
     options.setIntroductionType("js shell compile")
            .setFileAndLine("<string>", 1)
            .setCompileAndGo(true);
-
+    RootedScript script(cx);
     const jschar *chars = stableChars.twoByteRange().start().get();
     bool ok = JS_CompileUCScript(cx, global, chars,
-                                 scriptContents->length(), options);
+                                 scriptContents->length(), options, &script);
     args.rval().setUndefined();
     return ok;
 }
 
 static bool
 Parse(JSContext *cx, unsigned argc, jsval *vp)
 {
     using namespace js::frontend;
--- a/js/src/vm/CharacterEncoding.cpp
+++ b/js/src/vm/CharacterEncoding.cpp
@@ -8,20 +8,19 @@
 
 #include "mozilla/Range.h"
 
 #include "jscntxt.h"
 #include "jsprf.h"
 
 using namespace JS;
 
-using mozilla::Range;
-
 Latin1CharsZ
-JS::LossyTwoByteCharsToNewLatin1CharsZ(js::ThreadSafeContext *cx, const Range<const jschar> tbchars)
+JS::LossyTwoByteCharsToNewLatin1CharsZ(js::ThreadSafeContext *cx,
+                                       const mozilla::Range<const jschar> tbchars)
 {
     JS_ASSERT(cx);
     size_t len = tbchars.length();
     unsigned char *latin1 = cx->pod_malloc<unsigned char>(len + 1);
     if (!latin1)
         return Latin1CharsZ();
     for (size_t i = 0; i < len; ++i)
         latin1[i] = static_cast<unsigned char>(tbchars[i]);
@@ -139,17 +138,17 @@ bufferTooSmall:
         JS_ReportErrorNumber(cx->asJSContext(), js_GetErrorMessage, nullptr,
                              JSMSG_BUFFER_TOO_SMALL);
     }
     return false;
 }
 
 template <typename CharT>
 UTF8CharsZ
-JS::CharsToNewUTF8CharsZ(js::ThreadSafeContext *cx, const Range<const CharT> chars)
+JS::CharsToNewUTF8CharsZ(js::ThreadSafeContext *cx, const mozilla::Range<const CharT> chars)
 {
     JS_ASSERT(cx);
 
     /* Get required buffer size. */
     const CharT *str = chars.start().get();
     size_t len = GetDeflatedUTF8StringLength(str, chars.length());
 
     /* Allocate buffer. */
@@ -160,20 +159,20 @@ JS::CharsToNewUTF8CharsZ(js::ThreadSafeC
     /* Encode to UTF8. */
     DeflateStringToUTF8Buffer(cx, str, chars.length(), utf8, &len);
     utf8[len] = '\0';
 
     return UTF8CharsZ(utf8, len);
 }
 
 template UTF8CharsZ
-JS::CharsToNewUTF8CharsZ(js::ThreadSafeContext *cx, const Range<const Latin1Char> chars);
+JS::CharsToNewUTF8CharsZ(js::ThreadSafeContext *cx, const mozilla::Range<const Latin1Char> chars);
 
 template UTF8CharsZ
-JS::CharsToNewUTF8CharsZ(js::ThreadSafeContext *cx, const Range<const jschar> chars);
+JS::CharsToNewUTF8CharsZ(js::ThreadSafeContext *cx, const mozilla::Range<const jschar> chars);
 
 static const uint32_t INVALID_UTF8 = UINT32_MAX;
 
 /*
  * Convert a utf8 character sequence into a UCS-4 character and return that
  * character.  It is assumed that the caller already checked that the sequence
  * is valid.
  */
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2192,17 +2192,17 @@ Debugger::getNewestFrame(JSContext *cx, 
 }
 
 bool
 Debugger::clearAllBreakpoints(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGGER(cx, argc, vp, "clearAllBreakpoints", args, dbg);
     for (GlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty(); r.popFront())
         r.front()->compartment()->clearBreakpointsIn(cx->runtime()->defaultFreeOp(),
-                                                     dbg, nullptr);
+                                                     dbg, NullPtr());
     return true;
 }
 
 bool
 Debugger::construct(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
--- a/js/src/vm/ForkJoin.cpp
+++ b/js/src/vm/ForkJoin.cpp
@@ -2196,17 +2196,17 @@ class ParallelSpewer
     }
 
     void beginOp(JSContext *cx, const char *name) {
         if (!active[SpewOps])
             return;
 
         if (cx) {
             jsbytecode *pc;
-            JSScript *script = cx->currentScript(&pc);
+            RootedScript script(cx, cx->currentScript(&pc));
             if (script && pc) {
                 NonBuiltinScriptFrameIter iter(cx);
                 if (iter.done()) {
                     spew(SpewOps, "%sBEGIN %s%s (%s:%u)", bold(), name, reset(),
                          script->filename(), PCToLineNumber(script, pc));
                 } else {
                     spew(SpewOps, "%sBEGIN %s%s (%s:%u -> %s:%u)", bold(), name, reset(),
                          iter.script()->filename(), PCToLineNumber(iter.script(), iter.pc()),
--- a/js/src/vm/Runtime-inl.h
+++ b/js/src/vm/Runtime-inl.h
@@ -68,16 +68,17 @@ NewObjectCache::newObjectFromHit(JSConte
         return nullptr;
     }
 
     JS_ASSERT(allowGC == NoGC);
     JSObject *obj = js::gc::AllocateObjectForCacheHit<NoGC>(cx, entry->kind, heap);
     if (obj) {
         copyCachedToObject(obj, templateObj, entry->kind);
         probes::CreateObject(cx, obj);
+        js::gc::TraceCreateObject(obj);
         return obj;
     }
 
     return nullptr;
 }
 
 }  /* namespace js */
 
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -1005,18 +1005,18 @@ JSRuntime::initSelfHosting(JSContext *cx
      * and we don't want errors in self-hosted code to be silently swallowed.
      */
     JSErrorReporter oldReporter = JS_SetErrorReporter(cx, selfHosting_ErrorReporter);
     RootedValue rv(cx);
     bool ok = false;
 
     char *filename = getenv("MOZ_SELFHOSTEDJS");
     if (filename) {
-        RootedScript script(cx, Compile(cx, shg, options, filename));
-        if (script)
+        RootedScript script(cx);
+        if (Compile(cx, shg, options, filename, &script))
             ok = Execute(cx, script, *shg.get(), rv.address());
     } else {
         uint32_t srcLen = GetRawScriptsSize();
 
 #ifdef USE_ZLIB
         const unsigned char *compressed = compressedSources;
         uint32_t compressedLen = GetCompressedSize();
         ScopedJSFreePtr<char> src(reinterpret_cast<char *>(cx->malloc_(srcLen)));
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -11,32 +11,33 @@
 #include "jscntxt.h"
 
 #include "gc/Marking.h"
 #ifdef JS_ION
 #include "jit/AsmJSModule.h"
 #include "jit/BaselineFrame.h"
 #include "jit/JitCompartment.h"
 #endif
+#include "js/GCAPI.h"
 #include "vm/Opcodes.h"
 
 #include "jit/JitFrameIterator-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/Probes-inl.h"
 #include "vm/ScopeObject-inl.h"
 
 using namespace js;
 
 using mozilla::PodCopy;
 
 /*****************************************************************************/
 
 void
 InterpreterFrame::initExecuteFrame(JSContext *cx, JSScript *script, AbstractFramePtr evalInFramePrev,
-                             const Value &thisv, JSObject &scopeChain, ExecuteType type)
+                                   const Value &thisv, JSObject &scopeChain, ExecuteType type)
 {
     /*
      * See encoding of ExecuteType. When GLOBAL isn't set, we are executing a
      * script in the context of another frame and the frame type is determined
      * by the context.
      */
     flags_ = type | HAS_SCOPECHAIN;
 
@@ -661,26 +662,30 @@ FrameIter::Data::Data(const FrameIter::D
 }
 
 FrameIter::FrameIter(ThreadSafeContext *cx, SavedOption savedOption)
   : data_(cx, savedOption, CURRENT_CONTEXT, nullptr)
 #ifdef JS_ION
   , ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr)
 #endif
 {
+    // settleOnActivation can only GC if principals are given.
+    JS::AutoSuppressGCAnalysis nogc;
     settleOnActivation();
 }
 
 FrameIter::FrameIter(ThreadSafeContext *cx, ContextOption contextOption,
                      SavedOption savedOption)
   : data_(cx, savedOption, contextOption, nullptr)
 #ifdef JS_ION
   , ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr)
 #endif
 {
+    // settleOnActivation can only GC if principals are given.
+    JS::AutoSuppressGCAnalysis nogc;
     settleOnActivation();
 }
 
 FrameIter::FrameIter(JSContext *cx, ContextOption contextOption,
                      SavedOption savedOption, JSPrincipals *principals)
   : data_(cx, savedOption, contextOption, principals)
 #ifdef JS_ION
   , ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr)
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -1085,16 +1085,21 @@ class MOZ_STACK_CLASS AutoStableStringCh
     bool init(JSContext *cx, JSString *s);
 
     /* Like init(), but Latin1 chars are inflated to TwoByte. */
     bool initTwoByte(JSContext *cx, JSString *s);
 
     bool isLatin1() const { return state_ == Latin1; }
     bool isTwoByte() const { return state_ == TwoByte; }
 
+    const jschar *twoByteChars() const {
+        MOZ_ASSERT(state_ == TwoByte);
+        return twoByteChars_;
+    }
+
     mozilla::Range<const Latin1Char> latin1Range() const {
         MOZ_ASSERT(state_ == Latin1);
         return mozilla::Range<const Latin1Char>(latin1Chars_, s_->length());
     }
 
     mozilla::Range<const jschar> twoByteRange() const {
         MOZ_ASSERT(state_ == TwoByte);
         return mozilla::Range<const jschar>(twoByteChars_, s_->length());
--- a/js/src/vm/StringBuffer.cpp
+++ b/js/src/vm/StringBuffer.cpp
@@ -9,18 +9,16 @@
 #include "mozilla/Range.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/String-inl.h"
 
 using namespace js;
 
-using mozilla::Range;
-
 template <typename CharT, class Buffer>
 static CharT *
 ExtractWellSized(ExclusiveContext *cx, Buffer &cb)
 {
     size_t capacity = cb.capacity();
     size_t length = cb.length();
 
     CharT *buf = cb.extractRawBuffer();
@@ -94,21 +92,25 @@ StringBuffer::finishString()
 
     if (!JSString::validateLength(cx, len))
         return nullptr;
 
     JS_STATIC_ASSERT(JSFatInlineString::MAX_LENGTH_TWO_BYTE < TwoByteCharBuffer::InlineLength);
     JS_STATIC_ASSERT(JSFatInlineString::MAX_LENGTH_LATIN1 < Latin1CharBuffer::InlineLength);
 
     if (isLatin1()) {
-        if (JSFatInlineString::latin1LengthFits(len))
-            return NewFatInlineString<CanGC>(cx, Range<const Latin1Char>(latin1Chars().begin(), len));
+        if (JSFatInlineString::latin1LengthFits(len)) {
+            mozilla::Range<const Latin1Char> range(latin1Chars().begin(), len);
+            return NewFatInlineString<CanGC>(cx, range);
+        }
     } else {
-        if (JSFatInlineString::twoByteLengthFits(len))
-            return NewFatInlineString<CanGC>(cx, Range<const jschar>(twoByteChars().begin(), len));
+        if (JSFatInlineString::twoByteLengthFits(len)) {
+            mozilla::Range<const jschar> range(twoByteChars().begin(), len);
+            return NewFatInlineString<CanGC>(cx, range);
+        }
     }
 
     return isLatin1()
         ? FinishStringFlat<Latin1Char>(cx, *this, latin1Chars())
         : FinishStringFlat<jschar>(cx, *this, twoByteChars());
 }
 
 JSAtom *
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -896,22 +896,21 @@ mozJSComponentLoader::ObjectForLocation(
 
             char *buf = static_cast<char*>(PR_MemMap(map, 0, fileSize32));
             if (!buf) {
                 NS_WARNING("Failed to map file");
                 return NS_ERROR_FAILURE;
             }
 
             if (!mReuseLoaderGlobal) {
-                script = Compile(cx, obj, options, buf,
-                                     fileSize32);
+                Compile(cx, obj, options, buf, fileSize32, &script);
             } else {
-                function = CompileFunction(cx, obj, options,
-                                               nullptr, 0, nullptr,
-                                               buf, fileSize32);
+                CompileFunction(cx, obj, options,
+                                nullptr, 0, nullptr,
+                                buf, fileSize32, &function);
             }
 
             PR_MemUnmap(buf, fileSize32);
 
 #else  /* HAVE_PR_MEMMAP */
 
             /**
              * No memmap implementation, so fall back to
@@ -983,21 +982,21 @@ mozJSComponentLoader::ObjectForLocation(
             /* read the file in one swoop */
             rv = scriptStream->Read(buf, len, &bytesRead);
             if (bytesRead != len)
                 return NS_BASE_STREAM_OSERROR;
 
             buf[len] = '\0';
 
             if (!mReuseLoaderGlobal) {
-                script = Compile(cx, obj, options, buf, bytesRead);
+                Compile(cx, obj, options, buf, bytesRead, &script);
             } else {
-                function = CompileFunction(cx, obj, options,
-                                               nullptr, 0, nullptr,
-                                               buf, bytesRead);
+                CompileFunction(cx, obj, options,
+                                nullptr, 0, nullptr,
+                                buf, bytesRead, &function);
             }
         }
         // Propagate the exception, if one exists. Also, don't leave the stale
         // exception on this context.
         if (!script && !function && aPropagateExceptions) {
             JS_GetPendingException(cx, aException);
             JS_ClearPendingException(cx);
         }
--- a/js/xpconnect/loader/mozJSSubScriptLoader.cpp
+++ b/js/xpconnect/loader/mozJSSubScriptLoader.cpp
@@ -90,23 +90,23 @@ ReportError(JSContext *cx, const char *m
     JS_SetPendingException(cx, exn);
     return NS_OK;
 }
 
 nsresult
 mozJSSubScriptLoader::ReadScript(nsIURI *uri, JSContext *cx, JSObject *targetObjArg,
                                  const nsAString &charset, const char *uriStr,
                                  nsIIOService *serv, nsIPrincipal *principal,
-                                 bool reuseGlobal, JSScript **scriptp,
-                                 JSFunction **functionp)
+                                 bool reuseGlobal, JS::MutableHandleScript script,
+                                 JS::MutableHandleFunction function)
 {
     RootedObject target_obj(cx, targetObjArg);
 
-    *scriptp = nullptr;
-    *functionp = nullptr;
+    script.set(nullptr);
+    function.set(nullptr);
 
     // Instead of calling NS_OpenURI, we create the channel ourselves and call
     // SetContentType, to avoid expensive MIME type lookups (bug 632490).
     nsCOMPtr<nsIChannel> chan;
     nsCOMPtr<nsIInputStream> instream;
     nsresult rv = NS_NewChannel(getter_AddRefs(chan), uri, serv,
                                 nullptr, nullptr, nsIRequest::LOAD_NORMAL);
     if (NS_SUCCEEDED(rv)) {
@@ -150,32 +150,33 @@ mozJSSubScriptLoader::ReadScript(nsIURI 
         JS::SourceBufferHolder srcBuf(scriptBuf, scriptLength,
                                       JS::SourceBufferHolder::GiveOwnership);
 
         if (NS_FAILED(rv)) {
             return ReportError(cx, LOAD_ERROR_BADCHARSET);
         }
 
         if (!reuseGlobal) {
-            *scriptp = JS::Compile(cx, target_obj, options, srcBuf);
+            JS::Compile(cx, target_obj, options, srcBuf, script);
         } else {
-            *functionp = JS::CompileFunction(cx, target_obj, options,
-                                             nullptr, 0, nullptr,
-                                             srcBuf);
+            JS::CompileFunction(cx, target_obj, options,
+                                nullptr, 0, nullptr,
+                                srcBuf,
+                                function);
         }
     } else {
         // We only use lazy source when no special encoding is specified because
         // the lazy source loader doesn't know the encoding.
         if (!reuseGlobal) {
             options.setSourceIsLazy(true);
-            *scriptp = JS::Compile(cx, target_obj, options, buf.get(), len);
+            JS::Compile(cx, target_obj, options, buf.get(), len, script);
         } else {
-            *functionp = JS::CompileFunction(cx, target_obj, options,
-                                             nullptr, 0, nullptr, buf.get(),
-                                             len);
+            JS::CompileFunction(cx, target_obj, options,
+                                nullptr, 0, nullptr, buf.get(),
+                                len, function);
         }
     }
 
     /* repent for our evil deeds */
     JS_SetErrorReporter(cx, er);
 
     return NS_OK;
 }
@@ -328,17 +329,17 @@ mozJSSubScriptLoader::DoLoadSubScriptWit
 
     RootedFunction function(cx);
     RootedScript script(cx);
     if (cache && !options.ignoreCache)
         rv = ReadCachedScript(cache, cachePath, cx, mSystemPrincipal, &script);
     if (!script) {
         rv = ReadScript(uri, cx, targetObj, options.charset,
                         static_cast<const char*>(uriStr.get()), serv,
-                        principal, reusingGlobal, script.address(), function.address());
+                        principal, reusingGlobal, &script, &function);
         writeScript = !!script;
     }
 
     if (NS_FAILED(rv) || (!script && !function))
         return rv;
 
     if (function) {
         script = JS_GetFunctionScript(cx, function);
--- a/js/xpconnect/loader/mozJSSubScriptLoader.h
+++ b/js/xpconnect/loader/mozJSSubScriptLoader.h
@@ -31,18 +31,18 @@ public:
     NS_DECL_MOZIJSSUBSCRIPTLOADER
 
 private:
     virtual ~mozJSSubScriptLoader();
 
     nsresult ReadScript(nsIURI *uri, JSContext *cx, JSObject *target_obj,
                         const nsAString &charset, const char *uriStr,
                         nsIIOService *serv, nsIPrincipal *principal,
-                        bool reuseGlobal, JSScript **scriptp,
-                        JSFunction **functionp);
+                        bool reuseGlobal, JS::MutableHandleScript script,
+                        JS::MutableHandleFunction function);
 
     nsresult DoLoadSubScriptWithOptions(const nsAString &url,
                                         LoadSubScriptOptions  &options,
                                         JSContext *cx,
                                         JS::MutableHandle<JS::Value> retval);
 
     nsCOMPtr<nsIPrincipal> mSystemPrincipal;
 };
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -3169,18 +3169,18 @@ nsXPCComponents_Utils::ForcePrivilegedCo
 NS_IMETHODIMP
 nsXPCComponents_Utils::GetComponentsForScope(HandleValue vscope, JSContext *cx,
                                              MutableHandleValue rval)
 {
     if (!vscope.isObject())
         return NS_ERROR_INVALID_ARG;
     JSObject *scopeObj = js::UncheckedUnwrap(&vscope.toObject());
     XPCWrappedNativeScope *scope = GetObjectScope(scopeObj);
-    RootedObject components(cx, scope->GetComponentsJSObject());
-    if (!components)
+    RootedObject components(cx);
+    if (!scope->GetComponentsJSObject(&components))
         return NS_ERROR_FAILURE;
     if (!JS_WrapObject(cx, &components))
         return NS_ERROR_FAILURE;
     rval.setObject(*components);
     return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -521,17 +521,17 @@ IsInContentXBLScope(JSObject *obj)
 {
     return IsContentXBLScope(js::GetObjectCompartment(obj));
 }
 
 bool
 IsInAddonScope(JSObject *obj)
 {
     // We always eagerly create compartment privates for addon scopes.
-    XPCWrappedNativeScope *scope = GetObjectScope(obj);
+    XPCWrappedNativeScope *scope = MaybeGetObjectScope(obj);
     return scope && scope->IsAddonScope();
 }
 
 bool
 IsUniversalXPConnectEnabled(JSCompartment *compartment)
 {
     CompartmentPrivate *priv = GetCompartmentPrivate(compartment);
     if (!priv)
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -335,17 +335,18 @@ Load(JSContext *cx, unsigned argc, jsval
         if (!file) {
             JS_ReportError(cx, "cannot open file '%s' for reading",
                            filename.ptr());
             return false;
         }
         JS::CompileOptions options(cx);
         options.setUTF8(true)
                .setFileAndLine(filename.ptr(), 1);
-        JS::Rooted<JSScript*> script(cx, JS::Compile(cx, obj, options, file));
+        JS::Rooted<JSScript*> script(cx);
+        JS::Compile(cx, obj, options, file, &script);
         fclose(file);
         if (!script)
             return false;
 
         if (!compileOnly && !JS_ExecuteScript(cx, obj, script))
             return false;
     }
     args.rval().setUndefined();
@@ -919,18 +920,17 @@ ProcessFile(JSContext *cx, JS::Handle<JS
             }
         }
         ungetc(ch, file);
         DoBeginRequest(cx);
 
         JS::CompileOptions options(cx);
         options.setUTF8(true)
                .setFileAndLine(filename, 1);
-        script = JS::Compile(cx, obj, options, file);
-        if (script && !compileOnly)
+        if (JS::Compile(cx, obj, options, file, &script) && !compileOnly)
             (void)JS_ExecuteScript(cx, obj, script, &result);
         DoEndRequest(cx);
 
         return;
     }
 
     /* It's an interactive filehandle; drop into read-eval-print loop. */
     lineno = 1;
@@ -955,18 +955,17 @@ ProcessFile(JSContext *cx, JS::Handle<JS
             lineno++;
         } while (!JS_BufferIsCompilableUnit(cx, obj, buffer, strlen(buffer)));
 
         DoBeginRequest(cx);
         /* Clear any pending exception from previous failed compiles.  */
         JS_ClearPendingException(cx);
         JS::CompileOptions options(cx);
         options.setFileAndLine("typein", startline);
-        script = JS_CompileScript(cx, obj, buffer, strlen(buffer), options);
-        if (script) {
+        if (JS_CompileScript(cx, obj, buffer, strlen(buffer), options, &script)) {
             JSErrorReporter older;
 
             if (!compileOnly) {
                 ok = JS_ExecuteScript(cx, obj, script, &result);
                 if (ok && result != JSVAL_VOID) {
                     /* Suppress error reports from JS::ToString(). */
                     older = JS_SetErrorReporter(cx, nullptr);
                     str = ToString(cx, result);
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -123,44 +123,44 @@ XPCWrappedNativeScope::IsDyingScope(XPCW
 {
     for (XPCWrappedNativeScope *cur = gDyingScopes; cur; cur = cur->mNext) {
         if (scope == cur)
             return true;
     }
     return false;
 }
 
-JSObject*
-XPCWrappedNativeScope::GetComponentsJSObject()
+bool
+XPCWrappedNativeScope::GetComponentsJSObject(JS::MutableHandleObject obj)
 {
     AutoJSContext cx;
     if (!mComponents) {
         nsIPrincipal *p = GetPrincipal();
         bool system = nsXPConnect::SecurityManager()->IsSystemPrincipal(p);
         mComponents = system ? new nsXPCComponents(this)
                              : new nsXPCComponentsBase(this);
     }
 
     RootedValue val(cx);
     xpcObjectHelper helper(mComponents);
     bool ok = XPCConvert::NativeInterface2JSObject(&val, nullptr, helper,
                                                    nullptr, nullptr, false,
                                                    nullptr);
     if (NS_WARN_IF(!ok))
-        return nullptr;
+        return false;
 
     if (NS_WARN_IF(!val.isObject()))
-        return nullptr;
+        return false;
 
     // The call to wrap() here is necessary even though the object is same-
     // compartment, because it applies our security wrapper.
-    JS::RootedObject obj(cx, &val.toObject());
-    if (NS_WARN_IF(!JS_WrapObject(cx, &obj)))
-        return nullptr;
-    return obj;
+    obj.set(&val.toObject());
+    if (NS_WARN_IF(!JS_WrapObject(cx, obj)))
+        return false;
+    return true;
 }
 
 void
 XPCWrappedNativeScope::ForcePrivilegedComponents()
 {
     // This may only be called on unprivileged scopes during automation where
     // we allow insecure things.
     MOZ_RELEASE_ASSERT(Preferences::GetBool("security.turn_off_all_security_so_"
@@ -169,18 +169,18 @@ XPCWrappedNativeScope::ForcePrivilegedCo
     nsCOMPtr<nsIXPCComponents> c = do_QueryInterface(mComponents);
     if (!c)
         mComponents = new nsXPCComponents(this);
 }
 
 bool
 XPCWrappedNativeScope::AttachComponentsObject(JSContext* aCx)
 {
-    RootedObject components(aCx, GetComponentsJSObject());
-    if (!components)
+    RootedObject components(aCx);
+    if (!GetComponentsJSObject(&components))
         return false;
 
     RootedObject global(aCx, GetGlobalJSObject());
     MOZ_ASSERT(js::IsObjectInContextCompartment(global, aCx));
 
     RootedId id(aCx, XPCJSRuntime::Get()->GetStringID(XPCJSRuntime::IDX_COMPONENTS));
     return JS_DefinePropertyById(aCx, global, id, components,
                                  JSPROP_PERMANENT | JSPROP_READONLY);
@@ -692,17 +692,17 @@ WNProtoRemover(PLDHashTable *table, PLDH
 void
 XPCWrappedNativeScope::RemoveWrappedNativeProtos()
 {
     mWrappedNativeProtoMap->Enumerate(WNProtoRemover,
                                       GetRuntime()->GetDetachedWrappedNativeProtoMap());
 }
 
 JSObject *
-XPCWrappedNativeScope::GetExpandoChain(JSObject *target)
+XPCWrappedNativeScope::GetExpandoChain(HandleObject target)
 {
     MOZ_ASSERT(GetObjectScope(target) == this);
     if (!mXrayExpandos.initialized())
         return nullptr;
     return mXrayExpandos.lookup(target);
 }
 
 bool
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -980,18 +980,18 @@ public:
     // Forces the creation of a privileged |Components| object, even in
     // content scopes. This will crash if used outside of automation.
     void
     ForcePrivilegedComponents();
 
     bool AttachComponentsObject(JSContext *aCx);
 
     // Returns the JS object reflection of the Components object.
-    JSObject*
-    GetComponentsJSObject();
+    bool
+    GetComponentsJSObject(JS::MutableHandleObject obj);
 
     JSObject*
     GetGlobalJSObject() const {
         JS::ExposeObjectToActiveJS(mGlobalJSObject);
         return mGlobalJSObject;
     }
 
     JSObject*
@@ -999,17 +999,17 @@ public:
 
     nsIPrincipal*
     GetPrincipal() const {
         JSCompartment *c = js::GetObjectCompartment(mGlobalJSObject);
         return nsJSPrincipals::get(JS_GetCompartmentPrincipals(c));
     }
 
     JSObject*
-    GetExpandoChain(JSObject *target);
+    GetExpandoChain(JS::HandleObject target);
 
     bool
     SetExpandoChain(JSContext *cx, JS::HandleObject target, JS::HandleObject chain);
 
     void RemoveWrappedNativeProtos();
 
     static void
     SystemIsBeingShutDown();
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -203,17 +203,17 @@ public:
                                HandleObject consumer);
     JSObject* ensureExpandoObject(JSContext *cx, HandleObject wrapper,
                                   HandleObject target);
 
     JSObject* getHolder(JSObject *wrapper);
     JSObject* ensureHolder(JSContext *cx, HandleObject wrapper);
     virtual JSObject* createHolder(JSContext *cx, JSObject *wrapper) = 0;
 
-    JSObject* getExpandoChain(JSObject *obj) {
+    JSObject* getExpandoChain(HandleObject obj) {
       return GetObjectScope(obj)->GetExpandoChain(obj);
     }
 
     bool setExpandoChain(JSContext *cx, HandleObject obj, HandleObject chain) {
       return GetObjectScope(obj)->SetExpandoChain(cx, obj, chain);
     }
     bool cloneExpandoChain(JSContext *cx, HandleObject dst, HandleObject src);
 
--- a/media/libcubeb/src/cubeb_opensl.c
+++ b/media/libcubeb/src/cubeb_opensl.c
@@ -6,20 +6,22 @@
  */
 #undef NDEBUG
 #include <assert.h>
 #include <dlfcn.h>
 #include <stdlib.h>
 #include <pthread.h>
 #include <SLES/OpenSLES.h>
 #if defined(__ANDROID__)
+#include <sys/system_properties.h>
 #include "android/sles_definitions.h"
 #include <SLES/OpenSLES_Android.h>
 #include <android/log.h>
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Cubeb_OpenSL" , ## args)
+#define ANDROID_VERSION_GINGERBREAD_MR1 10
 #endif
 #include "cubeb/cubeb.h"
 #include "cubeb-internal.h"
 #include "cubeb_resampler.h"
 
 static struct cubeb_ops const opensl_ops;
 
 struct cubeb {
@@ -162,21 +164,48 @@ convert_stream_type_to_sl_stream(cubeb_s
     default:
       return 0xFFFFFFFF;
   }
 }
 #endif
 
 static void opensl_destroy(cubeb * ctx);
 
+#if defined(__ANDROID__)
+
+static int
+get_android_version(void)
+{
+  char version_string[PROP_VALUE_MAX];
+
+  memset(version_string, 0, PROP_VALUE_MAX);
+
+  int len = __system_property_get("ro.build.version.sdk", version_string);
+  if (len <= 0) {
+    LOG("Failed to get Android version!\n");
+    return len;
+  }
+
+  return (int)strtol(version_string, NULL, 10);
+}
+#endif
+
 /*static*/ int
 opensl_init(cubeb ** context, char const * context_name)
 {
   cubeb * ctx;
 
+#if defined(__ANDROID__)
+  int android_version = get_android_version();
+  if (android_version > 0 && android_version <= ANDROID_VERSION_GINGERBREAD_MR1) {
+    // Don't even attempt to run on Gingerbread and lower
+    return CUBEB_ERROR;
+  }
+#endif
+
   *context = NULL;
 
   ctx = calloc(1, sizeof(*ctx));
   assert(ctx);
 
   ctx->ops = &opensl_ops;
 
   ctx->lib = dlopen("libOpenSLES.so", RTLD_LAZY);
--- a/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp
+++ b/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp
@@ -2798,16 +2798,27 @@ int vcmGetVideoCodecList(int request_typ
  */
 int vcmGetVideoMaxSupportedPacketizationMode()
 {
   // We support mode 1 packetization in webrtc
   return 1;
 }
 
 /**
+ * Get supported H.264 profile-level-id
+ * @return supported profile-level-id value
+ */
+uint32_t vcmGetVideoH264ProfileLevelID()
+{
+  // constrained baseline level 1.2
+  // XXX make variable based on openh264 and OMX support
+  return 0x42E00C;
+}
+
+/**
  *  MEDIA control received from far end on signaling path
  *
  *  @param call_handle - call_handle of the call
  *  @param to_encoder - the control request received
  *        Only FAST_PICTURE_UPDATE is supported
  *
  *  @return  void
  *
@@ -3039,72 +3050,16 @@ cc_boolean vcmCheckAttribs(cc_uint32_t m
         return TRUE;
 
     default:
         return FALSE;
     }
 }
 
 /**
- * Add Video attributes in the offer/answer SDP
- *
- * This method is called for video codecs only. This method should populate the
- * Video SDP attributes using the SDP helper API
- *
- * @param [in] sdp_p - opaque SDP pointer to be used via SDP helper APIs
- * @param [in] level - Parameter to be used with SDP helper APIs
- * @param [in] media_type - codec for which the SDP attributes are to be populated
- * @param [in] payload_number - RTP payload type used for the SDP
- * @param [in] isOffer - cc_boolean indicating we are encoding an offer or an aswer
- *
- * @return void
- */
-void vcmPopulateAttribs(void *sdp_p, int level, cc_uint32_t media_type,
-                          cc_uint16_t payload_number, cc_boolean isOffer)
-{
-    CSFLogDebug( logTag, "vcmPopulateAttribs(): media=%d PT=%d, isOffer=%d", media_type, payload_number, isOffer);
-    uint16_t a_inst;//, a_inst2, a_inst3, a_inst4;
-    int profile;
-    char profile_level_id[MAX_SPROP_LEN];
-
-    switch (media_type)
-    {
-    case RTP_H264_P0:
-    case RTP_H264_P1:
-
-        if ( ccsdpAddNewAttr(sdp_p, level, 0, SDP_ATTR_FMTP, &a_inst) != SDP_SUCCESS ) return;
-
-        (void) ccsdpAttrSetFmtpPayloadType(sdp_p, level, 0, a_inst, payload_number);
-
-        if (media_type == RTP_H264_P1) {
-          (void) ccsdpAttrSetFmtpPackMode(sdp_p, level, 0, a_inst, 1 /*packetization_mode*/);
-        }
-        //(void) sdp_attr_set_fmtp_parameter_sets(sdp_p, level, 0, a_inst, "J0KAFJWgUH5A,KM4H8n==");    // NAL units 27 42 80 14 95 a0 50 7e 40 28 ce 07 f2
-
-        //profile = 0x42E000 + H264ToSDPLevel( vt_GetClientProfileLevel() );
-        profile = 0x42E00C;
-        csf_sprintf(profile_level_id, MAX_SPROP_LEN, "%X", profile);
-        (void) ccsdpAttrSetFmtpProfileLevelId(sdp_p, level, 0, a_inst, profile_level_id);
-
-        //(void) sdp_attr_set_fmtp_max_mbps(sdp_p, level, 0, a_inst, max_mbps);
-        //(void) sdp_attr_set_fmtp_max_fs(sdp_p, level, 0, a_inst, max_fs);
-        //(void) sdp_attr_set_fmtp_max_cpb(sdp_p, level, 0, a_inst, max_cpb);
-        //(void) sdp_attr_set_fmtp_max_dpb(sdp_p, level, 0, a_inst, max_dpb);
-        //(void) sdp_attr_set_fmtp_max_br(sdp_p, level, 0, a_inst, max_br);
-        //(void) sdp_add_new_bw_line(sdp_p, level, &a_inst);
-        //(void) sdp_set_bw(sdp_p, level, a_inst, SDP_BW_MODIFIER_TIAS, tias_bw);
-
-        break;
-
-    default:
-        break;
-    }
-}
-
-/**
  * Send a DTMF digit
  *
  * This method is called for sending a DTMF tone for the specified duration
  *
  * @param [in] digit - the DTMF digit that needs to be played out.
  * @param [in] duration - duration of the tone
  * @param [in] direction - direction in which the tone needs to be played.
  *
--- a/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c
@@ -1132,16 +1132,17 @@ gsmsdp_set_2543_hold_sdp (fsmdef_dcb_t *
  *                  RTP_AVT.
  *
  */
 static void
 gsmsdp_set_video_media_attributes (uint32_t media_type, void *cc_sdp_p, uint16_t level,
                              uint16_t payload_number)
 {
     uint16_t a_inst;
+    int added_fmtp = 0;
     void *sdp_p = ((cc_sdp_t*)cc_sdp_p)->src_sdp;
     int max_fs = 0;
     int max_fr = 0;
 
     switch (media_type) {
         case RTP_H263:
         case RTP_H264_P0:
         case RTP_H264_P1:
@@ -1165,53 +1166,83 @@ gsmsdp_set_video_media_attributes (uint3
                                              RTPMAP_VIDEO_CLOCKRATE);
             break;
         case RTP_H264_P0:
         case RTP_H264_P1:
             (void) sdp_attr_set_rtpmap_encname(sdp_p, level, 0, a_inst,
                                                SIPSDP_ATTR_ENCNAME_H264);
             (void) sdp_attr_set_rtpmap_clockrate(sdp_p, level, 0, a_inst,
                                              RTPMAP_VIDEO_CLOCKRATE);
+            // we know we haven't added it yet
+            if (sdp_add_new_attr(sdp_p, level, 0, SDP_ATTR_FMTP, &a_inst)
+                != SDP_SUCCESS) {
+                GSM_ERR_MSG("Failed to add attribute");
+                return;
+            }
+            added_fmtp = 1;
+            {
+                char buffer[32];
+                uint32_t profile_level_id = vcmGetVideoH264ProfileLevelID();
+                snprintf(buffer, sizeof(buffer), "0x%x", profile_level_id);
+                (void) sdp_attr_set_fmtp_profile_level_id(sdp_p, level, 0, a_inst,
+                                                          buffer);
+            }
+            if (media_type == RTP_H264_P1) {
+                (void) sdp_attr_set_fmtp_pack_mode(sdp_p, level, 0, a_inst,
+                                                   1);
+            }
+            // TODO: other parameters we may want/need to set for H.264
+        //(void) sdp_attr_set_fmtp_max_mbps(sdp_p, level, 0, a_inst, max_mbps);
+        //(void) sdp_attr_set_fmtp_max_fs(sdp_p, level, 0, a_inst, max_fs);
+        //(void) sdp_attr_set_fmtp_max_cpb(sdp_p, level, 0, a_inst, max_cpb);
+        //(void) sdp_attr_set_fmtp_max_dpb(sdp_p, level, 0, a_inst, max_dpb);
+        //(void) sdp_attr_set_fmtp_max_br(sdp_p, level, 0, a_inst, max_br);
+        //(void) sdp_add_new_bw_line(sdp_p, level, &a_inst);
+        //(void) sdp_set_bw(sdp_p, level, a_inst, SDP_BW_MODIFIER_TIAS, tias_bw);
             break;
         case RTP_VP8:
             (void) sdp_attr_set_rtpmap_encname(sdp_p, level, 0, a_inst,
                                                SIPSDP_ATTR_ENCNAME_VP8);
             (void) sdp_attr_set_rtpmap_clockrate(sdp_p, level, 0, a_inst,
                                              RTPMAP_VIDEO_CLOCKRATE);
-
+            break;
+        }
+
+        switch (media_type) {
+        case RTP_H264_P0:
+        case RTP_H264_P1:
+        case RTP_VP8:
             max_fs = config_get_video_max_fs((rtp_ptype) media_type);
             max_fr = config_get_video_max_fr((rtp_ptype) media_type);
 
             if (max_fs || max_fr) {
-                if (sdp_add_new_attr(sdp_p, level, 0, SDP_ATTR_FMTP, &a_inst)
-                    != SDP_SUCCESS) {
-                    GSM_ERR_MSG("Failed to add attribute");
-                    return;
+                if (!added_fmtp) {
+                    if (sdp_add_new_attr(sdp_p, level, 0, SDP_ATTR_FMTP, &a_inst)
+                        != SDP_SUCCESS) {
+                        GSM_ERR_MSG("Failed to add attribute");
+                        return;
+                    }
+                    added_fmtp = 1;
                 }
 
                 (void) sdp_attr_set_fmtp_payload_type(sdp_p, level, 0, a_inst,
                                                       payload_number);
 
                 if (max_fs) {
                     (void) sdp_attr_set_fmtp_max_fs(sdp_p, level, 0, a_inst,
                                                     max_fs);
                 }
 
                 if (max_fr) {
                     (void) sdp_attr_set_fmtp_max_fr(sdp_p, level, 0, a_inst,
                                                     max_fr);
                 }
             }
-
             break;
         }
-    GSM_DEBUG("gsmsdp_set_video_media_attributes- populate attribs %d", payload_number );
-
-        vcmPopulateAttribs(cc_sdp_p, level, media_type, payload_number, FALSE);
-
         break;
 
         default:
             break;
     }
 }
 
 /*
@@ -3425,17 +3456,17 @@ gsmsdp_negotiate_codec (fsmdef_dcb_t *dc
 
                             sdp_attr_get_fmtp_cbr (sdp_p->dest_sdp, level, 0, 1,
                                 &payload_info->opus.cbr);
 
                             /* Copied from media/webrtc/trunk/src/modules/
                                audio_coding/main/source/acm_codec_database.cc */
                             payload_info->audio.frequency = 48000;
                             payload_info->audio.packet_size = 960;
-                            payload_info->audio.bitrate = 32000;
+                            payload_info->audio.bitrate = 16000; // Increase when we have higher capture rates
                             break;
 
                         case RTP_ISAC:
                             /* TODO: Update these from proper SDP constructs */
                             payload_info->audio.frequency = 16000;
                             payload_info->audio.packet_size = 480;
                             payload_info->audio.bitrate = 32000;
                             break;
--- a/media/webrtc/signaling/src/sipcc/include/vcm.h
+++ b/media/webrtc/signaling/src/sipcc/include/vcm.h
@@ -883,16 +883,22 @@ int vcmGetVideoCodecList(int request_typ
 /**
  * Get max supported H.264 video packetization mode.
  * @return maximum supported video packetization mode for H.264. Value returned
  * must be 0 or 1. Value 2 is not supported yet.
  */
 int vcmGetVideoMaxSupportedPacketizationMode();
 
 /**
+ * Get supported H.264 profile-level-id
+ * @return supported profile-level-id value
+ */
+uint32_t vcmGetVideoH264ProfileLevelID();
+
+/**
  * Get the rx/tx stream statistics associated with the call.
  * The rx/tx stats are defined as comma seperated string as follows.
  * Rx_stats:
  *   snprintf(rx_stats, CC_KFACTOR_STAT_LEN,
  *               "Dur=%d,Pkt=%d,Oct=%d,LatePkt=%d,LostPkt=%d,AvgJit=%d,VQMetrics=\"%s\"",
  *               duration, numberOfPackageReceived, numberOfByteReceived, numberOfLatePackage, numberOfPackageLost, averageJitter, qualityMatrics);
  * Tx_stats:
  *   snprintf(tx_stats, CC_KFACTOR_STAT_LEN, "Dur=%d,Pkt=%d,Oct=%d",
--- a/media/webrtc/signaling/src/sipcc/stub/vcm_stub.c
+++ b/media/webrtc/signaling/src/sipcc/stub/vcm_stub.c
@@ -486,38 +486,16 @@ void vcmSetRtcpDscp(cc_groupid_t group_i
  */
 
 boolean vcmCheckAttribs(uint32_t media_type, void *sdp_p, int level, void **rcapptr)
 {
     return TRUE;
 }
 
 /**
- * Add Video attributes in the offer/answer SDP
- *
- * This method is called for video codecs only. This method should populate the
- * Video SDP attributes using the SDP helper API
- *
- * @param [in] sdp_p - opaque SDP pointer to be used via SDP helper APIs
- * @param [in] level - Parameter to be used with SDP helper APIs
- * @param [in] media_type - codec for which the SDP attributes are to be populated
- * @param [in] payload_number - RTP payload type used for the SDP
- * @param [in] isOffer - boolean indicating we are encoding an offer or an aswer
- *
- * @return void
- */
-
-
-void vcmPopulateAttribs(void *sdp_p, int level, uint32_t media_type,
-                          uint16_t payload_number, boolean isOffer)
-{
-    return;
-}
-
-/**
  * Send a DTMF digit
  *
  * This method is called for sending a DTMF tone for the specified duration
  *
  * @param [in] digit - the DTMF digit that needs to be played out.
  * @param [in] duration - duration of the tone
  * @param [in] direction - direction in which the tone needs to be played.
  *
--- a/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus.gypi
+++ b/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus.gypi
@@ -2,16 +2,19 @@
 #
 # Use of this source code is governed by a BSD-style license
 # that can be found in the LICENSE file in the root of the source
 # tree. An additional intellectual property rights grant can be found
 # in the file PATENTS.  All contributing project authors may
 # be found in the AUTHORS file in the root of the source tree.
 
 {
+  'variables': {
+    'opus_complexity%': 0,
+  },
   'targets': [
     {
       'target_name': 'webrtc_opus',
       'type': 'static_library',
       'conditions': [
         ['build_with_mozilla==1', {
           # Mozilla provides its own build of the opus library.
           'include_dirs': [
@@ -21,15 +24,18 @@
           'dependencies': [
             '<(DEPTH)/third_party/opus/opus.gyp:opus'
           ],
         }],
       ],
       'include_dirs': [
         '<(webrtc_root)',
       ],
+      'defines': [
+        'OPUS_COMPLEXITY=<(opus_complexity)'
+      ],
       'sources': [
         'interface/opus_interface.h',
         'opus_interface.c',
       ],
     },
   ],
 }
--- a/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus_interface.c
+++ b/media/webrtc/trunk/webrtc/modules/audio_coding/codecs/opus/opus_interface.c
@@ -98,16 +98,19 @@ int16_t WebRtcOpus_Encode(OpusEncInst* i
   if (res > 0) {
     return res;
   }
   return -1;
 }
 
 int16_t WebRtcOpus_SetBitRate(OpusEncInst* inst, int32_t rate) {
   if (inst) {
+#if defined(OPUS_COMPLEXITY) && (OPUS_COMPLEXITY != 0)
+  opus_encoder_ctl(inst->encoder, OPUS_SET_COMPLEXITY(OPUS_COMPLEXITY));
+#endif
   return opus_encoder_ctl(inst->encoder, OPUS_SET_BITRATE(rate));
   } else {
     return -1;
   }
 }
 
 struct WebRtcOpusDecInst {
   int16_t state_48_32_left[8];
--- a/mobile/android/base/tests/robocop.ini
+++ b/mobile/android/base/tests/robocop.ini
@@ -35,16 +35,18 @@ skip-if = android_version == "10" || pro
 skip-if = android_version == "10"
 [testFilterOpenTab]
 # [testFindInPage] # see bug 975155, bug 1014708
 [testFlingCorrectness]
 # disabled on x86 only; bug 927476
 skip-if = processor == "x86"
 [testFormHistory]
 [testGetUserMedia]
+# disabled on 2.3; see bug 981881]
+skip-if = android_version == "10"
 # [testHistory] # see bug 915350
 [testHomeBanner]
 # disabled on x86 only; bug 957185
 skip-if = processor == "x86"
 [testImportFromAndroid]
 # disabled on x86 and 2.3; bug 900664, 979552
 skip-if = android_version == "10" || processor == "x86"
 [testInputUrlBar]
--- a/mobile/android/base/tests/robocop_getusermedia.html
+++ b/mobile/android/base/tests/robocop_getusermedia.html
@@ -1,24 +1,28 @@
 <!DOCTYPE html>
 <html><head>
   <title>gUM Test Page</title>
   <meta http-equiv="content-type" content="text/html; charset=UTF-8" charset="utf-8">
 </head>
 <body>
+  <div id="content"></div>
   <script type="application/javascript">
   var video_status = false;
   var video = document.createElement("video");
   video.setAttribute("width", 640);
   video.setAttribute("height", 480);
 
   var audio_status = false;
   var audio = document.createElement("audio");
   audio.setAttribute("controls", true);
 
+  var content = document.getElementById("content");
+  document.title = "gUM Test Page";
+
   startAudioVideo();
 
   function startAudioVideo() {
     video_status = true;
     audio_status = true;
     startMedia({video:true, audio:true});
   }
 
@@ -36,28 +40,38 @@
       content.removeChild(audio);
       audio_status = false;
     }
   }
 
   function startMedia(param) {
     try {
       window.navigator.mozGetUserMedia(param, function(stream) {
-        message.innerHTML = "<p>Success!</p>";
         if (video_status) {
           content.appendChild(video);
           video.mozSrcObject = stream;
           video.play();
         }
         if (audio_status) {
           content.appendChild(audio);
           audio.mozSrcObject = stream;
           audio.play();
         }
+        var audioTracks = stream.getAudioTracks();
+        var videoTracks = stream.getVideoTracks();
+        document.title = "";
+        if (audioTracks.length > 0) {
+          document.title += "audio";
+        }
+        if (videoTracks.length > 0) {
+          document.title += "video";
+        }
+        document.title += " gumtest";
       }, function(err) {
+        document.title = "failed gumtest";
         stopMedia();
       });
     } catch(e) {
       stopMedia();
     }
   }
 </script>
-</body></html>
\ No newline at end of file
+</body></html>
--- a/mobile/android/base/tests/testGetUserMedia.java
+++ b/mobile/android/base/tests/testGetUserMedia.java
@@ -3,28 +3,65 @@ package org.mozilla.gecko.tests;
 import android.hardware.Camera;
 import android.os.Build;
 
 public class testGetUserMedia extends BaseTest {
     public void testGetUserMedia() {
         String GUM_URL = getAbsoluteUrl("/robocop/robocop_getusermedia.html");
 
         String GUM_MESSAGE = "Would you like to share your camera and microphone with";
-        String GUM_ALLOW = "Share";
-        String GUM_DENY = "Don't share";
+        String GUM_ALLOW = "^Share$";
+        String GUM_DENY = "^Don't Share$";
+
+        String GUM_PAGE_FAILED = "failed gumtest";
+        String GUM_PAGE_AUDIO = "audio gumtest";
+        String GUM_PAGE_VIDEO = "video gumtest";
+        String GUM_PAGE_AUDIOVIDEO = "audiovideo gumtest";
 
         blockForGeckoReady();
 
         // Only try GUM test if the device has a camera. If there's a working Camera,
         // we'll assume there is a working audio device as well.
         // getNumberOfCameras is Gingerbread/9+
         // We could avoid that requirement by trying to open a Camera but we
         // already know our 2.2/Tegra test devices don't have them.
-        if (Build.VERSION.SDK_INT >= 9) {
-            if (Camera.getNumberOfCameras() > 0) {
-                // Test GUM notification
-                inputAndLoadUrl(GUM_URL);
-                waitForText(GUM_MESSAGE);
-                mAsserter.is(mSolo.searchText(GUM_MESSAGE), true, "GetUserMedia doorhanger has been displayed");
-            }
+        if (Build.VERSION.SDK_INT < 9) {
+            return;
+        }
+
+        if (Camera.getNumberOfCameras() <= 0) {
+            return;
         }
+        // Test GUM notification showing
+        inputAndLoadUrl(GUM_URL);
+        waitForText(GUM_MESSAGE);
+        mAsserter.is(mSolo.searchText(GUM_MESSAGE), true, "GetUserMedia doorhanger has been displayed");
+        mSolo.clickOnButton(GUM_DENY);
+        verifyPageTitle(GUM_PAGE_FAILED);
+
+        inputAndLoadUrl(GUM_URL);
+        waitForText(GUM_MESSAGE);
+        // Cameras don't work on the testing hardware, so stream a tab
+        mSolo.clickOnText("Back facing camera");
+        mSolo.clickOnText("Choose a tab to stream");
+        mSolo.clickOnButton(GUM_ALLOW);
+        mSolo.clickOnText("gUM Test Page");
+        verifyPageTitle(GUM_PAGE_AUDIOVIDEO);
+
+        inputAndLoadUrl(GUM_URL);
+        waitForText(GUM_MESSAGE);
+        mSolo.clickOnText("Back facing camera");
+        mSolo.clickOnText("No Video");
+        mSolo.clickOnButton(GUM_ALLOW);
+        verifyPageTitle(GUM_PAGE_AUDIO);
+
+        inputAndLoadUrl(GUM_URL);
+        waitForText(GUM_MESSAGE);
+        // Cameras don't work on the testing hardware, so stream a tab
+        mSolo.clickOnText("Back facing camera");
+        mSolo.clickOnText("Choose a tab to stream");
+        mSolo.clickOnText("Microphone 1");
+        mSolo.clickOnText("No Audio");
+        mSolo.clickOnButton(GUM_ALLOW);
+        mSolo.clickOnText("gUM Test Page");
+        verifyPageTitle(GUM_PAGE_VIDEO);
     }
 }
--- a/netwerk/base/src/ProxyAutoConfig.cpp
+++ b/netwerk/base/src/ProxyAutoConfig.cpp
@@ -632,19 +632,21 @@ ProxyAutoConfig::SetupJS()
   // huge meaningless strings. this is not on the main thread, so it can't
   // use nsIRUI scheme methods
   bool isDataURI = nsDependentCSubstring(mPACURI, 0, 5).LowerCaseEqualsASCII("data:", 5);
 
   sRunning = this;
   JS::Rooted<JSObject*> global(cx, mJSRuntime->Global());
   JS::CompileOptions options(cx);
   options.setFileAndLine(mPACURI.get(), 1);
-  JS::Rooted<JSScript*> script(cx, JS_CompileScript(cx, global, mPACScript.get(),
-                                                    mPACScript.Length(), options));
-  if (!script || !JS_ExecuteScript(cx, global, script)) {
+  JS::Rooted<JSScript*> script(cx);
+  if (!JS_CompileScript(cx, global, mPACScript.get(),
+                        mPACScript.Length(), options, &script) ||
+      !JS_ExecuteScript(cx, global, script))
+  {
     nsString alertMessage(NS_LITERAL_STRING("PAC file failed to install from "));
     if (isDataURI) {
       alertMessage += NS_LITERAL_STRING("data: URI");
     }
     else {
       alertMessage += NS_ConvertUTF8toUTF16(mPACURI);
     }
     PACLogToConsole(alertMessage);
--- a/testing/mozbase/mozlog/mozlog/structured/formatters/tbplformatter.py
+++ b/testing/mozbase/mozlog/mozlog/structured/formatters/tbplformatter.py
@@ -30,35 +30,41 @@ class TbplFormatter(BaseMachFormatter):
         return "SUITE-START | Running %i tests\n" % len(data["tests"])
 
     def test_start(self, data):
         self.test_start_times[self.test_id(data["test"])] = data["time"]
 
         return "TEST-START | %s\n" % self.id_str(data["test"])
 
     def test_status(self, data):
+        message = "- " + data["message"] if "message" in data else ""
         if "expected" in data:
-            return "TEST-UNEXPECTED-%s | %s | %s | expected %s | %s\n" % (
-                data["status"], self.id_str(data["test"]), data["subtest"], data["expected"],
-                data.get("message", ""))
-        else:
-            return "TEST-%s | %s | %s | %s\n" % (
-                data["status"], self.id_str(data["test"]), data["subtest"], data.get("message", ""))
+            failure_line = "TEST-UNEXPECTED-%s | %s | %s %s" % (
+                data["status"], self.id_str(data["test"]), data["subtest"],
+                message)
+            info_line = "TEST-INFO | expected %s\n" % data["expected"]
+            return "\n".join([failure_line, info_line])
+
+        return "TEST-%s | %s | %s %s\n" % (
+            data["status"], self.id_str(data["test"]), data["subtest"],
+            message)
 
     def test_end(self, data):
         start_time = self.test_start_times.pop(self.test_id(data["test"]))
         time = data["time"] - start_time
 
         if "expected" in data:
-            return "TEST-END UNEXPECTED-%s | %s | expected %s | %s | took %ims\n" % (
-                data["status"], self.id_str(data["test"]), data["expected"],
-                data.get("message", ""), time)
-        else:
-            return "TEST-END %s | %s | took %ims\n" % (
-                data["status"], self.id_str(data["test"]), time)
+            failure_line = "TEST-UNEXPECTED-%s | %s | %s" % (
+                data["status"], self.id_str(data["test"]),
+                data.get("message", ""))
+            info_line = "TEST-INFO expected %s | took %ims\n" % (data["expected"], time)
+            return "\n".join([failure_line, info_line])
+
+        return "TEST-%s | %s | took %ims\n" % (
+            data["status"], self.id_str(data["test"]), time)
 
     def suite_end(self, data):
         start_time = self.suite_start_time
         time = int((data["time"] - start_time) / 1000)
 
         return "SUITE-END | took %is\n" % time
 
     def test_id(self, test_id):
--- a/testing/tps/tps/testrunner.py
+++ b/testing/tps/tps/testrunner.py
@@ -41,17 +41,17 @@ class TempFile(object):
         if os.access(self.filename, os.F_OK):
             os.remove(self.filename)
 
     __del__ = cleanup
 
 
 class TPSTestRunner(object):
 
-    default_env = {
+    extra_env = {
         'MOZ_CRASHREPORTER_DISABLE': '1',
         'GNOME_DISABLE_CRASH_DIALOG': '1',
         'XRE_NO_WINDOWS_CRASH_DIALOG': '1',
         'MOZ_NO_REMOTE': '1',
         'XPCOM_DEBUG_BREAK': 'warn',
     }
 
     default_preferences = {
@@ -346,19 +346,21 @@ class TPSTestRunner(object):
         if self.debug:
             self.preferences.update(self.debug_preferences)
 
     def run_tests(self):
         # delete the logfile if it already exists
         if os.access(self.logfile, os.F_OK):
             os.remove(self.logfile)
 
-        # Make a copy of the default env variables and preferences, and update
-        # them for custom settings
-        self.env = self.default_env.copy()
+        # Copy the system env variables, and update them for custom settings
+        self.env = os.environ.copy()
+        self.env.update(self.extra_env)
+
+        # Update preferences for custom settings
         self.update_preferences()
 
         # Acquire a lock to make sure no other threads are running tests
         # at the same time.
         if self.rlock:
             self.rlock.acquire()
 
         try:
--- a/tools/profiler/ProfileEntry.cpp
+++ b/tools/profiler/ProfileEntry.cpp
@@ -475,20 +475,24 @@ void ThreadProfile::StreamJSObject(JSStr
     b.EndArray();
   b.EndObject();
 }
 
 JSObject* ThreadProfile::ToJSObject(JSContext *aCx)
 {
   JS::RootedValue val(aCx);
   std::stringstream ss;
-  JSStreamWriter b(ss);
-  StreamJSObject(b);
-  NS_ConvertUTF8toUTF16 js_string(nsDependentCString(ss.str().c_str()));
-  JS_ParseJSON(aCx, static_cast<const jschar*>(js_string.get()), js_string.Length(), &val);
+  {
+    // Define a scope to prevent a moving GC during ~JSStreamWriter from
+    // trashing the return value.
+    JSStreamWriter b(ss);
+    StreamJSObject(b);
+    NS_ConvertUTF8toUTF16 js_string(nsDependentCString(ss.str().c_str()));
+    JS_ParseJSON(aCx, static_cast<const jschar*>(js_string.get()), js_string.Length(), &val);
+  }
   return &val.toObject();
 }
 
 PseudoStack* ThreadProfile::GetPseudoStack()
 {
   return mPseudoStack;
 }
 
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -165,20 +165,24 @@ void TableTicker::ToStreamAsJSON(std::os
   JSStreamWriter b(stream);
   StreamJSObject(b);
 }
 
 JSObject* TableTicker::ToJSObject(JSContext *aCx)
 {
   JS::RootedValue val(aCx);
   std::stringstream ss;
-  JSStreamWriter b(ss);
-  StreamJSObject(b);
-  NS_ConvertUTF8toUTF16 js_string(nsDependentCString(ss.str().c_str()));
-  JS_ParseJSON(aCx, static_cast<const jschar*>(js_string.get()), js_string.Length(), &val);
+  {
+    // Define a scope to prevent a moving GC during ~JSStreamWriter from
+    // trashing the return value.
+    JSStreamWriter b(ss);
+    StreamJSObject(b);
+    NS_ConvertUTF8toUTF16 js_string(nsDependentCString(ss.str().c_str()));
+    JS_ParseJSON(aCx, static_cast<const jschar*>(js_string.get()), js_string.Length(), &val);
+  }
   return &val.toObject();
 }
 
 struct SubprocessClosure {
   SubprocessClosure(JSStreamWriter *aWriter)
     : mWriter(aWriter)
   {}
 
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -195,17 +195,19 @@ AndroidBridge::Init(JNIEnv *jEnv)
         jSurfacePointerField = getField("mNativeSurface", "I");
     } else {
         // We don't know how to get this, just set it to 0
         jSurfacePointerField = 0;
     }
 
     jclass eglClass = getClassGlobalRef("com/google/android/gles_jni/EGLSurfaceImpl");
     if (eglClass) {
-        jEGLSurfacePointerField = getField("mEGLSurface", "I");
+        // The pointer type moved to a 'long' in Android L, API version 20
+        const char* jniType = mAPIVersion >= 20 ? "J" : "I";
+        jEGLSurfacePointerField = getField("mEGLSurface", jniType);
     } else {
         jEGLSurfacePointerField = 0;
     }
 
     jChannels = getClassGlobalRef("java/nio/channels/Channels");
     jChannelCreate = jEnv->GetStaticMethodID(jChannels, "newChannel", "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;");
 
     jReadableByteChannel = getClassGlobalRef("java/nio/channels/ReadableByteChannel");
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -2321,17 +2321,17 @@ DrawTitlebarHighlight(NSSize aWindowSize
   [path addClip];
 
   // Now we fill the path with a subtle highlight gradient.
   // We don't use NSGradient because it's 5x to 15x slower than the manual fill,
   // as indicated by the performance test in bug 880620.
   for (CGFloat y = 0; y < aRadius; y += aDevicePixelWidth) {
     CGFloat t = y / aRadius;
     [[NSColor colorWithDeviceWhite:1.0 alpha:0.4 * (1.0 - t)] set];
-    NSRectFill(NSMakeRect(0, y, aWindowSize.width, aDevicePixelWidth));
+    NSRectFillUsingOperation(NSMakeRect(0, y, aWindowSize.width, aDevicePixelWidth), NSCompositeSourceOver);
   }
 
   [NSGraphicsContext restoreGraphicsState];
 }
 
 static CGContextRef
 CreateCGContext(const nsIntSize& aSize)
 {
--- a/widget/cocoa/nsMenuX.mm
+++ b/widget/cocoa/nsMenuX.mm
@@ -31,32 +31,31 @@
 #include "nsIDOMDocument.h"
 #include "nsIDocumentObserver.h"
 #include "nsIComponentManager.h"
 #include "nsIRollupListener.h"
 #include "nsIDOMElement.h"
 #include "nsBindingManager.h"
 #include "nsIServiceManager.h"
 #include "nsXULPopupManager.h"
-#include "nsCxPusher.h"
+#include "mozilla/dom/ScriptSettings.h"
 
 #include "jsapi.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptContext.h"
 #include "nsIXPConnect.h"
 
 #include "mozilla/MouseEvents.h"
 
 using namespace mozilla;
 
 static bool gConstructingMenu = false;
 static bool gMenuMethodsSwizzled = false;
 
 int32_t nsMenuX::sIndexingMenuLevel = 0;
-using mozilla::AutoPushJSContext;
 
 
 //
 // Objective-C class used for representedObject
 //
 
 @implementation MenuItemInfo
 
@@ -411,30 +410,23 @@ void nsMenuX::MenuConstruct()
 
   // bug 365405: Manually wrap the menupopup node to make sure it's bounded
   if (!mXBLAttached) {
     nsresult rv;
     nsCOMPtr<nsIXPConnect> xpconnect =
       do_GetService(nsIXPConnect::GetCID(), &rv);
     if (NS_SUCCEEDED(rv)) {
       nsIDocument* ownerDoc = menuPopup->OwnerDoc();
-      nsCOMPtr<nsIScriptGlobalObject> sgo;
-      if (ownerDoc && (sgo = do_QueryInterface(ownerDoc->GetWindow()))) {
-        nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext();
-        JSObject* global = sgo->GetGlobalJSObject();
-        if (scriptContext && global) {
-          AutoPushJSContext cx(scriptContext->GetNativeContext());
-          if (cx) {
-            nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
-            xpconnect->WrapNative(cx, global,
-                                  menuPopup, NS_GET_IID(nsISupports),
-                                  getter_AddRefs(wrapper));
-            mXBLAttached = true;
-          }
-        }
+      dom::AutoJSAPI jsapi;
+      if (ownerDoc && jsapi.Init(ownerDoc->GetInnerWindow())) {
+        JSContext* cx = jsapi.cx();
+        nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
+        xpconnect->WrapNative(cx, JS::CurrentGlobalOrNull(cx), menuPopup,
+                              NS_GET_IID(nsISupports), getter_AddRefs(wrapper));
+        mXBLAttached = true;
       } 
     }
   }
 
   // Iterate over the kids
   uint32_t count = menuPopup->GetChildCount();
   for (uint32_t i = 0; i < count; i++) {
     nsIContent *child = menuPopup->GetChildAt(i);
--- a/xpcom/reflect/xptinfo/xptiInterfaceInfo.cpp
+++ b/xpcom/reflect/xptinfo/xptiInterfaceInfo.cpp
@@ -357,17 +357,16 @@ xptiInterfaceEntry::GetEntryForParam(uin
         return rv;
     }
 
     xptiInterfaceEntry* theEntry = mTypelib->GetEntryAt(interfaceIndex - 1);
     
     // This can happen if a declared interface is not available at runtime.
     if(!theEntry)
     {
-        NS_WARNING("Declared InterfaceInfo not found");
         *entry = nullptr;
         return NS_ERROR_FAILURE;
     }
 
     *entry = theEntry;
     return NS_OK;
 }