Merge m-c to fx-team, a=merge CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Wed, 27 May 2015 17:23:58 -0700
changeset 246009 242916838c5c24e1bc79cea0932fc7a00bf393d7
parent 246008 1662253c88f7523e573aab4972157f2ab3764b88 (current diff)
parent 245879 baa9c64fea6f0531bd380bfbbca64e3dfce632bd (diff)
child 246010 e56dd3f52961c9a4231f5a04bc25e2387ab6bbf0
push id60333
push userryanvm@gmail.com
push dateThu, 28 May 2015 14:20:47 +0000
treeherdermozilla-inbound@8225a3b75df6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone41.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 CLOSED TREE
dom/manifest/ManifestImageObjectProcessor.jsm
dom/manifest/ManifestObtainer.jsm
dom/manifest/ManifestProcessor.jsm
dom/manifest/ManifestValueExtractor.jsm
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -111,17 +111,19 @@ pref("browser.sessionstore.resume_from_c
 pref("browser.sessionstore.interval", 10000); // milliseconds
 pref("browser.sessionstore.max_tabs_undo", 1);
 
 /* these should help performance */
 pref("mozilla.widget.force-24bpp", true);
 pref("mozilla.widget.use-buffer-pixmap", true);
 pref("mozilla.widget.disable-native-theme", true);
 pref("layout.reflow.synthMouseMove", false);
+#ifndef MOZ_X11
 pref("layers.enable-tiles", true);
+#endif
 pref("layers.low-precision-buffer", true);
 pref("layers.low-precision-opacity", "0.5");
 pref("layers.progressive-paint", true);
 
 /* download manager (don't show the window or alert) */
 pref("browser.download.useDownloadDir", true);
 pref("browser.download.folderList", 1); // Default to ~/Downloads
 pref("browser.download.manager.showAlertOnComplete", false);
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/sources.xml
@@ -10,25 +10,25 @@
   <!--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="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8ca93673869a64e09ed6153c5402896822dfb253"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="05380df3158fa39e1dde1687c0bf11a71f8c6868"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <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="53517af1f57810bef745f03602f407161a475d76"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="d31f6f08740a46e5315c9279b8f987c9d1fce486"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="a32003194f707f66a2d8cdb913ed1869f1926c5d"/>
   <project name="device/common" path="device/common" revision="96d4d2006c4fcb2f19a3fa47ab10cb409faa017b"/>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,25 +10,25 @@
   <!--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="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8ca93673869a64e09ed6153c5402896822dfb253"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="05380df3158fa39e1dde1687c0bf11a71f8c6868"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <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="53517af1f57810bef745f03602f407161a475d76"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="d31f6f08740a46e5315c9279b8f987c9d1fce486"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="83760d213fb3bec7b4117d266fcfbf6fe2ba14ab"/>
   <project name="device/common" path="device/common" revision="6a2995683de147791e516aae2ccb31fdfbe2ad30"/>
--- 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="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <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="8ca93673869a64e09ed6153c5402896822dfb253"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="05380df3158fa39e1dde1687c0bf11a71f8c6868"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="22664edc4c73e5fe8f5095ff1d5549db78a2bc10"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="218a5637399d023f4326e12c8a486dad95403b6c"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- 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,20 +12,20 @@
   <!--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="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8ca93673869a64e09ed6153c5402896822dfb253"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="05380df3158fa39e1dde1687c0bf11a71f8c6868"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="53517af1f57810bef745f03602f407161a475d76"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="d31f6f08740a46e5315c9279b8f987c9d1fce486"/>
   <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"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--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="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8ca93673869a64e09ed6153c5402896822dfb253"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="05380df3158fa39e1dde1687c0bf11a71f8c6868"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <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="53517af1f57810bef745f03602f407161a475d76"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="d31f6f08740a46e5315c9279b8f987c9d1fce486"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="1950e4760fa14688b83cdbb5acaa1af9f82ef434"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="ac6eb97a37035c09fb5ede0852f0881e9aadf9ad"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="737f591c5f95477148d26602c7be56cbea0cdeb9"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="51da9b1981be481b92a59a826d4d78dc73d0989a"/>
   <project name="device/common" path="device/common" revision="798a3664597e6041985feab9aef42e98d458bc3d"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,25 +10,25 @@
   <!--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="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8ca93673869a64e09ed6153c5402896822dfb253"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="05380df3158fa39e1dde1687c0bf11a71f8c6868"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <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="53517af1f57810bef745f03602f407161a475d76"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="d31f6f08740a46e5315c9279b8f987c9d1fce486"/>
   <!-- Stock Android things -->
   <project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="ffc05a232799fe8fcb3e47b7440b52b1fb4244c0"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="337e0ef5e40f02a1ae59b90db0548976c70a7226"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" revision="8af5ff6f5dced9eb5a8127459df6c75d24342204"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" revision="30915518fa7ea07166efedc191a4f40aef516fe7"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" revision="96eee58e3389fb05a835310d6a06a6ba4486097a"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" revision="7c8a46698171aa2e0be09edb43d15a6acf832770"/>
   <project groups="pdk,linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" path="prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" revision="24b2038be8a636fd4a5d21f0abae1e466b07bcf7"/>
--- 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="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <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="8ca93673869a64e09ed6153c5402896822dfb253"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="05380df3158fa39e1dde1687c0bf11a71f8c6868"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="22664edc4c73e5fe8f5095ff1d5549db78a2bc10"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="218a5637399d023f4326e12c8a486dad95403b6c"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--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="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8ca93673869a64e09ed6153c5402896822dfb253"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="05380df3158fa39e1dde1687c0bf11a71f8c6868"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <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="53517af1f57810bef745f03602f407161a475d76"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="d31f6f08740a46e5315c9279b8f987c9d1fce486"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="a32003194f707f66a2d8cdb913ed1869f1926c5d"/>
   <project name="device/common" path="device/common" revision="96d4d2006c4fcb2f19a3fa47ab10cb409faa017b"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "8ca93673869a64e09ed6153c5402896822dfb253", 
+        "git_revision": "05380df3158fa39e1dde1687c0bf11a71f8c6868", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "ce5b9f84c7e253e3615694be152d257995ce2238", 
+    "revision": "fc4f5dfe591a45cb03ae221489f41b56f38c883a", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,20 +12,20 @@
   <!--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="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8ca93673869a64e09ed6153c5402896822dfb253"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="05380df3158fa39e1dde1687c0bf11a71f8c6868"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="53517af1f57810bef745f03602f407161a475d76"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="d31f6f08740a46e5315c9279b8f987c9d1fce486"/>
   <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"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,25 +10,25 @@
   <!--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="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8ca93673869a64e09ed6153c5402896822dfb253"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="05380df3158fa39e1dde1687c0bf11a71f8c6868"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <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="53517af1f57810bef745f03602f407161a475d76"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="d31f6f08740a46e5315c9279b8f987c9d1fce486"/>
   <!-- Stock Android things -->
   <project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="ffc05a232799fe8fcb3e47b7440b52b1fb4244c0"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="337e0ef5e40f02a1ae59b90db0548976c70a7226"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" revision="8af5ff6f5dced9eb5a8127459df6c75d24342204"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" revision="30915518fa7ea07166efedc191a4f40aef516fe7"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" revision="96eee58e3389fb05a835310d6a06a6ba4486097a"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" revision="7c8a46698171aa2e0be09edb43d15a6acf832770"/>
   <project groups="pdk,linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" path="prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" revision="24b2038be8a636fd4a5d21f0abae1e466b07bcf7"/>
--- a/b2g/confvars.sh
+++ b/b2g/confvars.sh
@@ -55,19 +55,16 @@ MOZ_SIMPLEPUSH=1
 MOZ_PAY=1
 MOZ_TOOLKIT_SEARCH=
 MOZ_PLACES=
 MOZ_B2G=1
 
 if test "$OS_TARGET" = "Android"; then
 MOZ_NUWA_PROCESS=1
 MOZ_B2G_LOADER=1
-# Warnings-as-errors cannot be enabled on Lollipop until bug 1119980 is fixed.
-if test "$PLATFORM_SDK_VERSION" -lt 21; then
 MOZ_ENABLE_WARNINGS_AS_ERRORS=1
 fi
-fi
 
 MOZ_JSDOWNLOADS=1
 
 MOZ_BUNDLED_FONTS=1
 
 export JS_GC_SMALL_CHUNK_SIZE=1
--- a/config/config.mk
+++ b/config/config.mk
@@ -232,20 +232,16 @@ else
 OS_LDFLAGS = -DEBUG -OPT:REF
 endif
 endif # MOZ_DMD
 
 endif # MOZ_DEBUG
 
 endif # WINNT && !GNU_CC
 
-ifdef MOZ_GLUE_IN_PROGRAM
-DEFINES += -DMOZ_GLUE_IN_PROGRAM
-endif
-
 #
 # Build using PIC by default
 #
 _ENABLE_PIC=1
 
 # No sense in profiling tools
 ifdef INTERNAL_TOOLS
 NO_PROFILE_GUIDED_OPTIMIZE = 1
--- a/config/recurse.mk
+++ b/config/recurse.mk
@@ -60,19 +60,20 @@ export NO_RECURSE_MAKELEVEL=$(word $(MAK
 endif
 endif
 
 # Use the $(*_dirs) variables available in root.mk
 CURRENT_DIRS := $($(CURRENT_TIER)_dirs)
 
 # Need a list of compile targets because we can't use pattern rules:
 # https://savannah.gnu.org/bugs/index.php?42833
+# Only recurse the paths starting with RECURSE_BASE_DIR when provided.
 .PHONY: $(compile_targets)
 $(compile_targets):
-	$(call SUBMAKE,$(@F),$(@D))
+	$(if $(filter $(RECURSE_BASE_DIR)%,$@),$(call SUBMAKE,$(@F),$(@D)))
 
 # The compile tier has different rules from other tiers.
 ifneq ($(CURRENT_TIER),compile)
 
 # Recursion rule for all directories traversed for all subtiers in the
 # current tier.
 $(addsuffix /$(CURRENT_TIER),$(CURRENT_DIRS)): %/$(CURRENT_TIER):
 	$(call SUBMAKE,$(CURRENT_TIER),$*)
@@ -120,17 +121,18 @@ define CREATE_SUBTIER_TRAVERSAL_RULE
 	$$(LOOP_OVER_DIRS)
 
 endef
 
 $(foreach subtier,$(filter-out compile,$(TIERS)),$(eval $(call CREATE_SUBTIER_TRAVERSAL_RULE,$(subtier))))
 
 ifndef TOPLEVEL_BUILD
 ifdef COMPILE_ENVIRONMENT
-libs:: target host
+compile::
+	@$(MAKE) -C $(DEPTH) compile RECURSE_BASE_DIR=$(relativesrcdir)/
 endif # COMPILE_ENVIRONMENT
 endif
 
 endif # ifeq ($(NO_RECURSE_MAKELEVEL),$(MAKELEVEL))
 
 endif # ifeq (.,$(DEPTH))
 
 recurse:
--- a/configure.in
+++ b/configure.in
@@ -7084,16 +7084,17 @@ MOZ_ARG_ENABLE_BOOL(jemalloc,
 
 case "${OS_TARGET}" in
 Android|WINNT|Darwin)
   MOZ_GLUE_IN_PROGRAM=
   ;;
 *)
   dnl On !Android !Windows !OSX, we only want to link executables against mozglue
   MOZ_GLUE_IN_PROGRAM=1
+  AC_DEFINE(MOZ_GLUE_IN_PROGRAM)
   ;;
 esac
 
 dnl ========================================================
 dnl = Enable dynamic replacement of malloc implementation
 dnl ========================================================
 if test -n "$NIGHTLY_BUILD" -a -n "$MOZ_MEMORY"; then
   # Enable on central for the debugging opportunities it adds.
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -2203,17 +2203,17 @@ nsSameProcessAsyncMessageBase::nsSamePro
                                                              JS::Handle<JSObject*> aCpows,
                                                              nsIPrincipal* aPrincipal)
   : mRuntime(js::GetRuntime(aCx)),
     mMessage(aMessage),
     mCpows(aCx, aCpows),
     mPrincipal(aPrincipal)
 {
   if (aData.mDataLength && !mData.copy(aData.mData, aData.mDataLength)) {
-    NS_RUNTIMEABORT("OOM");
+    NS_ABORT_OOM(aData.mDataLength);
   }
   mClosure = aData.mClosure;
 }
 
 void
 nsSameProcessAsyncMessageBase::ReceiveMessage(nsISupports* aTarget,
                                               nsIFrameLoader* aTargetFrameLoader,
                                               nsFrameMessageManager* aManager)
--- a/dom/base/nsWindowMemoryReporter.cpp
+++ b/dom/base/nsWindowMemoryReporter.cpp
@@ -385,16 +385,21 @@ CollectWindowReports(nsGlobalWindow *aWi
   aWindowTotalSizes->mArenaStats.mRuleNodes
     += windowSizes.mArenaStats.mRuleNodes;
 
   REPORT_SIZE("/layout/style-contexts", windowSizes.mArenaStats.mStyleContexts,
               "Memory used by style contexts within a window.");
   aWindowTotalSizes->mArenaStats.mStyleContexts
     += windowSizes.mArenaStats.mStyleContexts;
 
+  REPORT_SIZE("/layout/style-structs", windowSizes.mArenaStats.mStyleStructs,
+              "Memory used by style structs within a window.");
+  aWindowTotalSizes->mArenaStats.mStyleStructs
+    += windowSizes.mArenaStats.mStyleStructs;
+
   REPORT_SIZE("/layout/style-sets", windowSizes.mLayoutStyleSetsSize,
               "Memory used by style sets within a window.");
   aWindowTotalSizes->mLayoutStyleSetsSize += windowSizes.mLayoutStyleSetsSize;
 
   REPORT_SIZE("/layout/text-runs", windowSizes.mLayoutTextRunsSize,
               "Memory used for text-runs (glyph layout) in the PresShell's "
               "frame tree, within a window.");
   aWindowTotalSizes->mLayoutTextRunsSize += windowSizes.mLayoutTextRunsSize;
@@ -591,16 +596,20 @@ nsWindowMemoryReporter::CollectReports(n
   REPORT("window-objects/layout/rule-nodes",
          windowTotalSizes.mArenaStats.mRuleNodes,
          "This is the sum of all windows' 'layout/rule-nodes' numbers.");
 
   REPORT("window-objects/layout/style-contexts",
          windowTotalSizes.mArenaStats.mStyleContexts,
          "This is the sum of all windows' 'layout/style-contexts' numbers.");
 
+  REPORT("window-objects/layout/style-structs",
+         windowTotalSizes.mArenaStats.mStyleStructs,
+         "This is the sum of all windows' 'layout/style-structs' numbers.");
+
   REPORT("window-objects/layout/style-sets", windowTotalSizes.mLayoutStyleSetsSize,
          "This is the sum of all windows' 'layout/style-sets' numbers.");
 
   REPORT("window-objects/layout/text-runs", windowTotalSizes.mLayoutTextRunsSize,
          "This is the sum of all windows' 'layout/text-runs' numbers.");
 
   REPORT("window-objects/layout/pres-contexts", windowTotalSizes.mLayoutPresContextSize,
          "This is the sum of all windows' 'layout/pres-contexts' numbers.");
--- a/dom/bluetooth/ObexBase.h
+++ b/dom/bluetooth/ObexBase.h
@@ -229,16 +229,78 @@ public:
         *aRetTarget = new uint8_t[mHeaders[i]->mDataLength];
         memcpy(*aRetTarget, ptr, mHeaders[i]->mDataLength);
         *aRetTargetLength = mHeaders[i]->mDataLength;
         return;
       }
     }
   }
 
+  uint32_t GetConnectionId() const
+  {
+    int length = mHeaders.Length();
+
+    for (int i = 0; i < length; ++i) {
+      if (mHeaders[i]->mId == ObexHeaderId::ConnectionId) {
+        uint32_t* id = (uint32_t *) mHeaders[i]->mData.get();
+        return *id;
+      }
+    }
+
+    // According to OBEX spec., the value 0xFFFFFFFF is reserved and it's
+    // considered invalid for Connection ID.
+    return 0xFFFFFFFF;
+  }
+
+  /**
+   * Get a specified parameter from the 'Application Parameters' header.
+   *
+   * @param aTagId      [in]  The tag ID of parameter which is defined by
+   *                          applications or upper protocol layer.
+   * @param aRetBuf     [out] The buffer which is used to return the parameter.
+   * @param aBufferSize [in]  The size of the given buffer.
+   *
+   * @return a boolean value to indicate whether the given paramter exists.
+   */
+  bool GetAppParameter(uint8_t aTagId, uint8_t* aRetBuf, int aBufferSize) const
+  {
+    int length = mHeaders.Length();
+
+    for (int i = 0; i < length; ++i) {
+      // Parse the 'Application Parameters' header.
+      if (mHeaders[i]->mId == ObexHeaderId::AppParameters) {
+        uint8_t* ptr = mHeaders[i]->mData.get();
+        int dataLen = mHeaders[i]->mDataLength;
+
+        // An application parameters header may contain more than one
+        // [tag]-[length]-[value] triplet. The [tag] and [length] fields are
+        // each one byte in length.
+        uint8_t tagId;
+        uint8_t offset = 0;
+        do {
+          tagId = *(ptr + offset++);
+
+          // The length of value field, it should not exceed 255.
+          uint8_t paramLen = *(ptr + offset++);
+
+          if (tagId == aTagId) {
+            memcpy(aRetBuf, ptr + offset, paramLen < aBufferSize ? paramLen
+                                                                 : aBufferSize);
+            return true;
+          }
+
+          offset += paramLen;
+        } while (offset < dataLen);
+      }
+    }
+
+    // The specified parameter don't exist in 'Application Parameters' header.
+    return false;
+  }
+
   bool Has(ObexHeaderId aId) const
   {
     int length = mHeaders.Length();
     for (int i = 0; i < length; ++i) {
       if (mHeaders[i]->mId == aId) {
         return true;
       }
     }
--- a/dom/bluetooth/bluedroid/BluetoothPbapManager.h
+++ b/dom/bluetooth/bluedroid/BluetoothPbapManager.h
@@ -9,16 +9,38 @@
 
 #include "BluetoothCommon.h"
 #include "BluetoothProfileManagerBase.h"
 #include "BluetoothSocketObserver.h"
 #include "mozilla/ipc/SocketBase.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
+/*
+ * Defined in section 6.2.1 "Application Parameters Header", PBAP ver 1.2
+ */
+enum AppParameterTag {
+  Order                   = 0x01,
+  SearchValue             = 0x02,
+  SearchProperty          = 0x03,
+  MaxListCount            = 0x04,
+  ListStartOffset         = 0x05,
+  PropertySelector        = 0x06,
+  Format                  = 0x07,
+  PhonebookSize           = 0x08,
+  NewMissedCalls          = 0x09,
+  PrimaryVersionCounter   = 0x0A,
+  SecondaryVersionCounter = 0x0B,
+  vCardSelector           = 0x0C,
+  DatabaseIdentifier      = 0x0D,
+  vCardSelectorOperator   = 0x0E,
+  ResetNewMissedCalls     = 0x0F,
+  PbapSupportedFeatures   = 0x10
+};
+
 class BluetoothSocket;
 class ObexHeaderSet;
 
 class BluetoothPbapManager : public BluetoothSocketObserver
                           , public BluetoothProfileManagerBase
 {
 public:
   BT_DECL_PROFILE_MGR_BASE
--- a/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
@@ -2875,17 +2875,20 @@ BluetoothServiceBluedroid::BondStateChan
     if (!sAdapterBondedAddressArray.Contains(remoteBdAddr)) {
       sAdapterBondedAddressArray.AppendElement(remoteBdAddr);
     }
 
     // We don't assert |!deviceName.IsEmpty()| here since empty string is
     // also a valid name. According to Bluetooth Core Spec. v3.0 - Sec. 6.22,
     // "a valid Bluetooth name is a UTF-8 encoding string which is up to 248
     // bytes in length."
-    MOZ_ASSERT(nameExists);
+    // Furthermore, we don't assert |nameExists| here since it's expected to be
+    // 'false' if remote device is using "SSP just works without user
+    // interaction" or "legacy pairing with auto-pairing".
+
     BT_APPEND_NAMED_VALUE(propertiesArray, "Name", deviceName);
   }
 
   // Notify device of attribute changed
   BT_APPEND_NAMED_VALUE(propertiesArray, "Paired", bonded);
   DistributeSignal(NS_LITERAL_STRING("PropertyChanged"),
                    aRemoteBdAddr,
                    BluetoothValue(propertiesArray));
--- a/dom/canvas/test/test_canvas.html
+++ b/dom/canvas/test/test_canvas.html
@@ -21581,16 +21581,17 @@ isPixel(ctx, 50,25, 0,255,0,255, 0);
 }
 
 </script>
 
 <script>
 
 function asyncTestsDone() {
 	if (isDone_test_2d_drawImage_animated_apng &&
+		isDone_test_2d_pattern_animated_gif &&
 		isDone_test_2d_drawImage_animated_gif) {
 		SimpleTest.finish();
 	} else {
 		setTimeout(asyncTestsDone, 500);
 	}
  }
  
 function runTests() {
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -4792,18 +4792,20 @@ EventStateManager::SetContentState(nsICo
     {
       const nsStyleUserInterface* ui = mCurrentTarget->StyleUserInterface();
       if (ui->mUserInput == NS_STYLE_USER_INPUT_NONE)
         return false;
     }
 
     if (aState == NS_EVENT_STATE_ACTIVE) {
       // Editable content can never become active since their default actions
-      // are disabled.
-      if (aContent && aContent->IsEditable()) {
+      // are disabled.  Watch out for editable content in native anonymous
+      // subtrees though, as they belong to text controls.
+      if (aContent && aContent->IsEditable() &&
+          !aContent->IsInNativeAnonymousSubtree()) {
         aContent = nullptr;
       }
       if (aContent != mActiveContent) {
         notifyContent1 = aContent;
         notifyContent2 = mActiveContent;
         mActiveContent = aContent;
       }
     } else {
--- a/dom/events/test/chrome.ini
+++ b/dom/events/test/chrome.ini
@@ -2,18 +2,18 @@
 skip-if = buildapp == 'b2g'
 support-files =
   bug415498-doc1.html
   bug415498-doc2.html
   bug591249_iframe.xul
   bug602962.xul
   file_bug679494.html
   window_bug617528.xul
+  test_bug336682.js
 
-[test_bug336682.js]
 [test_bug336682_2.xul]
 [test_bug368835.html]
 [test_bug415498.xul]
 [test_bug524674.xul]
 [test_bug586961.xul]
 [test_bug591249.xul]
 [test_bug602962.xul]
 [test_bug617528.xul]
--- a/dom/events/test/test_bug924087.html
+++ b/dom/events/test/test_bug924087.html
@@ -7,30 +7,39 @@ https://bugzilla.mozilla.org/show_bug.cg
   <title>Test for Bug 924087</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <div contenteditable><a id="editable" href="#">editable link</a></div>
 <a id="noneditable" href="#">non-editable link</a>
+<input>
+<textarea></textarea>
 <pre id="test">
 <script type="application/javascript;version=1.8">
 
 /** Test for Bug 924087 **/
 SimpleTest.waitForExplicitFinish();
 SimpleTest.waitForFocus(function() {
   var editable = document.querySelector("#editable");
   var noneditable = document.querySelector("#noneditable");
+  var input = document.querySelector("input");
+  var textarea = document.querySelector("textarea");
   synthesizeMouseAtCenter(noneditable, {type:"mousedown"});
   is(document.querySelector(":active:link"), noneditable, "Normal links should become :active");
   synthesizeMouseAtCenter(noneditable, {type:"mouseup"});
   synthesizeMouseAtCenter(editable, {type:"mousedown"});
   is(document.querySelector(":active:link"), null, "Editable links should not become :active");
   synthesizeMouseAtCenter(editable, {type:"mouseup"});
+  [input, textarea].forEach(textbox => {
+    synthesizeMouseAtCenter(textbox, {type:"mousedown"});
+    is(document.querySelector(textbox.localName + ":active"), textbox, "The textbox should become :active");
+    synthesizeMouseAtCenter(textbox, {type:"mouseup"});
+  });
   SimpleTest.finish();
 });
 
 </script>
 </pre>
 
 </body>
 </html>
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -2650,20 +2650,19 @@ ContentChild::RecvStopProfiler()
 {
     profiler_stop();
     return true;
 }
 
 bool
 ContentChild::RecvGetProfile(nsCString* aProfile)
 {
-    char* profile = profiler_get_profile();
+    UniquePtr<char[]> profile = profiler_get_profile();
     if (profile) {
-        *aProfile = nsCString(profile, strlen(profile));
-        free(profile);
+        *aProfile = nsCString(profile.get(), strlen(profile.get()));
     } else {
         *aProfile = EmptyCString();
     }
     return true;
 }
 
 bool
 ContentChild::RecvLoadPluginResult(const uint32_t& aPluginId, const bool& aResult)
--- a/dom/ipc/CrashReporterParent.cpp
+++ b/dom/ipc/CrashReporterParent.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set sw=4 ts=8 et 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 "CrashReporterParent.h"
+#include "mozilla/Snprintf.h"
 #include "mozilla/dom/ContentParent.h"
 #include "nsXULAppAPI.h"
 #include <time.h>
 
 #include "mozilla/Telemetry.h"
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
@@ -121,17 +122,17 @@ CrashReporterParent::GenerateChildData(c
             break;
         default:
             NS_ERROR("unknown process type");
             break;
     }
     mNotes.Put(NS_LITERAL_CSTRING("ProcessType"), type);
 
     char startTime[32];
-    sprintf(startTime, "%lld", static_cast<long long>(mStartTime));
+    snprintf_literal(startTime, "%lld", static_cast<long long>(mStartTime));
     mNotes.Put(NS_LITERAL_CSTRING("StartupTime"), nsDependentCString(startTime));
 
     if (!mAppNotes.IsEmpty())
         mNotes.Put(NS_LITERAL_CSTRING("Notes"), mAppNotes);
 
     bool ret = CrashReporter::AppendExtraData(mChildDumpID, mNotes);
     if (ret && processNotes)
         ret = CrashReporter::AppendExtraData(mChildDumpID, *processNotes);
--- a/dom/ipc/manifestMessages.js
+++ b/dom/ipc/manifestMessages.js
@@ -1,36 +1,29 @@
 /* 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/.*/
 /*
  * Manifest obtainer frame script implementation of:
- * http://w3c.github.io/manifest/#obtaining
+ * http://www.w3.org/TR/appmanifest/#obtaining
  *
  * It searches a top-level browsing context for
  * a <link rel=manifest> element. Then fetches
  * and processes the linked manifest.
  *
  * BUG: https://bugzilla.mozilla.org/show_bug.cgi?id=1083410
- * exported ManifestObtainer
  */
-/*globals content, ManifestProcessor, XPCOMUtils, sendAsyncMessage, addMessageListener, Components*/
+/*globals content, sendAsyncMessage, addMessageListener, Components*/
 'use strict';
 const {
   utils: Cu
 } = Components;
-
-Cu.import('resource://gre/modules/XPCOMUtils.jsm');
-
-XPCOMUtils.defineLazyModuleGetter(this, 'ManifestProcessor',
-  'resource://gre/modules/ManifestProcessor.jsm');
-XPCOMUtils.defineLazyModuleGetter(this, 'ManifestObtainer',
-  'resource://gre/modules/ManifestObtainer.jsm');
-XPCOMUtils.defineLazyModuleGetter(this, 'BrowserUtils',
-  'resource://gre/modules/BrowserUtils.jsm');
+const {
+  ManifestProcessor
+} = Cu.import('resource://gre/modules/WebManifest.jsm', {});
 
 addMessageListener('DOM:ManifestObtainer:Obtain', (aMsg) => {
   fetchManifest()
     .then(
       manifest => sendAsyncMessage('DOM:ManifestObtainer:Obtain', {
         success: true,
         result: manifest,
         msgId: aMsg.data.msgId
rename from dom/manifest/ManifestImageObjectProcessor.jsm
rename to dom/manifest/ImageObjectProcessor.js
--- a/dom/manifest/ManifestImageObjectProcessor.jsm
+++ b/dom/manifest/ImageObjectProcessor.js
@@ -1,62 +1,60 @@
 /* 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/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 /*
- * ManifestImageObjectProcessor
+ * ImageObjectProcessor
  * Implementation of Image Object processing algorithms from:
- * http://www.w3.org/2008/webapps/manifest/#image-object-and-its-members
+ * http://www.w3.org/TR/appmanifest/#image-object-and-its-members
  *
- * This is intended to be used in conjunction with ManifestProcessor.jsm
+ * This is intended to be used in conjunction with ManifestProcessor.js
  *
  * Creates an object to process Image Objects as defined by the
  * W3C specification. This is used to process things like the
  * icon member and the splash_screen member.
  *
  * Usage:
  *
  *   .process(aManifest, aBaseURL, aMemberName);
  *
  */
 /*exported EXPORTED_SYMBOLS*/
-/*globals Components*/
+/*globals Components */
 'use strict';
-this.EXPORTED_SYMBOLS = ['ManifestImageObjectProcessor']; // jshint ignore:line
-const imports = {};
 const {
   utils: Cu,
-  classes: Cc,
-  interfaces: Ci
+  interfaces: Ci,
+  classes: Cc
 } = Components;
-Cu.import('resource://gre/modules/XPCOMUtils.jsm');
+
 Cu.importGlobalProperties(['URL']);
-imports.netutil = Cc['@mozilla.org/network/util;1']
+const netutil = Cc['@mozilla.org/network/util;1']
   .getService(Ci.nsINetUtil);
 
-function ManifestImageObjectProcessor(aConsole, aExtractor) {
+function ImageObjectProcessor(aConsole, aExtractor) {
   this.console = aConsole;
   this.extractor = aExtractor;
 }
 
 // Static getters
-Object.defineProperties(ManifestImageObjectProcessor, {
+Object.defineProperties(ImageObjectProcessor, {
   'decimals': {
     get: function() {
       return /^\d+$/;
     }
   },
   'anyRegEx': {
     get: function() {
       return new RegExp('any', 'i');
     }
   }
 });
 
-ManifestImageObjectProcessor.prototype.process = function(
+ImageObjectProcessor.prototype.process = function(
   aManifest, aBaseURL, aMemberName
 ) {
   const spec = {
     objectName: 'manifest',
     object: aManifest,
     property: aMemberName,
     expectedType: 'array',
     trim: false
@@ -89,17 +87,17 @@ ManifestImageObjectProcessor.prototype.p
       objectName: 'image',
       object: aImage,
       property: 'type',
       expectedType: 'string',
       trim: true
     };
     let value = extractor.extractValue(spec);
     if (value) {
-      value = imports.netutil.parseContentType(value, charset, hadCharset);
+      value = netutil.parseContentType(value, charset, hadCharset);
     }
     return value || undefined;
   }
 
   function processDensityMember(aImage) {
     const value = parseFloat(aImage.density);
     const validNum = Number.isNaN(value) || value === +Infinity || value <=
       0;
@@ -139,36 +137,37 @@ ManifestImageObjectProcessor.prototype.p
       value.split(/\s+/)
         .filter(isValidSizeValue)
         .forEach(size => sizes.add(size));
     }
     return sizes;
     // Implementation of HTML's link@size attribute checker.
     function isValidSizeValue(aSize) {
       const size = aSize.toLowerCase();
-      if (ManifestImageObjectProcessor.anyRegEx.test(aSize)) {
+      if (ImageObjectProcessor.anyRegEx.test(aSize)) {
         return true;
       }
       if (!size.includes('x') || size.indexOf('x') !== size.lastIndexOf('x')) {
         return false;
       }
       // Split left of x for width, after x for height.
       const widthAndHeight = size.split('x');
       const w = widthAndHeight.shift();
       const h = widthAndHeight.join('x');
       const validStarts = !w.startsWith('0') && !h.startsWith('0');
-      const validDecimals = ManifestImageObjectProcessor.decimals.test(w + h);
+      const validDecimals = ImageObjectProcessor.decimals.test(w + h);
       return (validStarts && validDecimals);
     }
   }
 
   function processBackgroundColorMember(aImage) {
     const spec = {
       objectName: 'image',
       object: aImage,
       property: 'background_color',
       expectedType: 'string',
       trim: true
     };
     return extractor.extractColorValue(spec);
   }
 };
-this.ManifestImageObjectProcessor = ManifestImageObjectProcessor; // jshint ignore:line
+this.ImageObjectProcessor = ImageObjectProcessor; // jshint ignore:line
+this.EXPORTED_SYMBOLS = ['ImageObjectProcessor']; // jshint ignore:line
rename from dom/manifest/ManifestObtainer.jsm
rename to dom/manifest/ManifestObtainer.js
--- a/dom/manifest/ManifestObtainer.jsm
+++ b/dom/manifest/ManifestObtainer.js
@@ -16,18 +16,16 @@
  *   'chrome://global/content/manifestMessages.js'
  *
  * Which is injected into every browser instance via browser.js.
  *
  * BUG: https://bugzilla.mozilla.org/show_bug.cgi?id=1083410
  * exported ManifestObtainer
  */
 'use strict';
-this.EXPORTED_SYMBOLS = ['ManifestObtainer'];
-
 const MSG_KEY = 'DOM:ManifestObtainer:Obtain';
 let messageCounter = 0;
 // FIXME: Ideally, we would store a reference to the
 //        message manager in a weakmap instead of needing a
 //        browserMap. However, trying to store a messageManager
 //        results in a TypeError because of:
 //        https://bugzilla.mozilla.org/show_bug.cgi?id=888600
 const browsersMap = new WeakMap();
@@ -78,8 +76,10 @@ ManifestObtainer.prototype = {
     function toError(aErrorClone) {
       const error = new Error();
       Object.getOwnPropertyNames(aErrorClone)
         .forEach(name => error[name] = aErrorClone[name]);
       return error;
     }
   }
 };
+this.ManifestObtainer = ManifestObtainer; // jshint ignore:line
+this.EXPORTED_SYMBOLS = ['ManifestObtainer']; // jshint ignore:line
rename from dom/manifest/ManifestProcessor.jsm
rename to dom/manifest/ManifestProcessor.js
--- a/dom/manifest/ManifestProcessor.jsm
+++ b/dom/manifest/ManifestProcessor.js
@@ -7,53 +7,50 @@
  * http://www.w3.org/2008/webapps/manifest/
  *
  * Creates manifest processor that lets you process a JSON file
  * or individual parts of a manifest object. A manifest is just a
  * standard JS object that has been cleaned up.
  *
  *   .process({jsonText,manifestURL,docURL});
  *
- * Depends on ManifestImageObjectProcessor to process things like
+ * Depends on ImageObjectProcessor to process things like
  * icons and splash_screens.
  *
  * TODO: The constructor should accept the UA's supported orientations.
  * TODO: The constructor should accept the UA's supported display modes.
  * TODO: hook up developer tools to console. (1086997).
  */
-/*exported EXPORTED_SYMBOLS */
-/*JSLint options in comment below: */
-/*globals Components, XPCOMUtils, Intl*/
+/*globals Components*/
 'use strict';
-this.EXPORTED_SYMBOLS = ['ManifestProcessor']; // jshint ignore:line
-const imports = {};
 const {
-  utils: Cu
+  utils: Cu,
+  interfaces: Ci,
+  classes: Cc
 } = Components;
-Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.importGlobalProperties(['URL']);
-XPCOMUtils.defineLazyModuleGetter(imports, 'Services',
-  'resource://gre/modules/Services.jsm');
 const displayModes = new Set(['fullscreen', 'standalone', 'minimal-ui',
   'browser'
 ]);
 const orientationTypes = new Set(['any', 'natural', 'landscape', 'portrait',
   'portrait-primary', 'portrait-secondary', 'landscape-primary',
   'landscape-secondary'
 ]);
 const {
   ConsoleAPI
-} = Cu.import('resource://gre/modules/devtools/Console.jsm');
+} = Cu.import('resource://gre/modules/devtools/Console.jsm', {});
+// ValueExtractor is used by the various processors to get values
+// from the manifest and to report errors.
 const {
-  ManifestImageObjectProcessor: ImgObjProcessor
-} = Cu.import('resource://gre/modules/ManifestImageObjectProcessor.jsm');
-
+  ValueExtractor
+} = Cu.import('resource://gre/modules/ValueExtractor.js', {});
+// ImageObjectProcessor is used to process things like icons and images
 const {
-  ManifestValueExtractor
-} = Cu.import('resource://gre/modules/ManifestValueExtractor.jsm');
+  ImageObjectProcessor
+} = Cu.import('resource://gre/modules/ImageObjectProcessor.js', {});
 
 function ManifestProcessor() {}
 
 // Static getters
 Object.defineProperties(ManifestProcessor, {
   'defaultDisplayMode': {
     get: function() {
       return 'browser';
@@ -70,193 +67,192 @@ Object.defineProperties(ManifestProcesso
     }
   }
 });
 
 ManifestProcessor.prototype = {
   // process() method processes JSON text into a clean manifest
   // that conforms with the W3C specification. Takes an object
   // expecting the following dictionary items:
-  //  * aJsonText: the JSON string to be processed.
-  //  * aManifestURL: the URL of the manifest, to resolve URLs.
-  //  * aDocURL: the URL of the owner doc, for security checks
+  //  * jsonText: the JSON string to be processed.
+  //  * manifestURL: the URL of the manifest, to resolve URLs.
+  //  * docURL: the URL of the owner doc, for security checks
   process({
-    jsonText: aJsonText,
+    jsonText,
     manifestURL: aManifestURL,
     docURL: aDocURL
   }) {
     const console = new ConsoleAPI({
       prefix: 'Web Manifest: '
     });
     const manifestURL = new URL(aManifestURL);
     const docURL = new URL(aDocURL);
     let rawManifest = {};
     try {
-      rawManifest = JSON.parse(aJsonText);
+      rawManifest = JSON.parse(jsonText);
     } catch (e) {}
     if (typeof rawManifest !== 'object' || rawManifest === null) {
       let msg = 'Manifest needs to be an object.';
       console.warn(msg);
       rawManifest = {};
     }
-    const extractor = new ManifestValueExtractor(console);
-    const imgObjProcessor = new ImgObjProcessor(console, extractor);
+    const extractor = new ValueExtractor(console);
+    const imgObjProcessor = new ImageObjectProcessor(console, extractor);
     const processedManifest = {
-      'lang': processLangMember(rawManifest),
-      'start_url': processStartURLMember(rawManifest, manifestURL, docURL),
-      'display': processDisplayMember(rawManifest),
-      'orientation': processOrientationMember(rawManifest),
-      'name': processNameMember(rawManifest),
+      'lang': processLangMember(),
+      'start_url': processStartURLMember(),
+      'display': processDisplayMember(),
+      'orientation': processOrientationMember(),
+      'name': processNameMember(),
       'icons': imgObjProcessor.process(
         rawManifest, manifestURL, 'icons'
       ),
       'splash_screens': imgObjProcessor.process(
         rawManifest, manifestURL, 'splash_screens'
       ),
-      'short_name': processShortNameMember(rawManifest),
-      'theme_color': processThemeColorMember(rawManifest),
+      'short_name': processShortNameMember(),
+      'theme_color': processThemeColorMember(),
     };
-    processedManifest.scope = processScopeMember(rawManifest, manifestURL,
-      docURL, new URL(processedManifest['start_url'])); // jshint ignore:line
-
+    processedManifest.scope = processScopeMember();
     return processedManifest;
 
-    function processNameMember(aManifest) {
+    function processNameMember() {
       const spec = {
         objectName: 'manifest',
-        object: aManifest,
+        object: rawManifest,
         property: 'name',
         expectedType: 'string',
         trim: true
       };
       return extractor.extractValue(spec);
     }
 
-    function processShortNameMember(aManifest) {
+    function processShortNameMember() {
       const spec = {
         objectName: 'manifest',
-        object: aManifest,
+        object: rawManifest,
         property: 'short_name',
         expectedType: 'string',
         trim: true
       };
       return extractor.extractValue(spec);
     }
 
-    function processOrientationMember(aManifest) {
+    function processOrientationMember() {
       const spec = {
         objectName: 'manifest',
-        object: aManifest,
+        object: rawManifest,
         property: 'orientation',
         expectedType: 'string',
         trim: true
       };
       const value = extractor.extractValue(spec);
       if (ManifestProcessor.orientationTypes.has(value)) {
         return value;
       }
       // The spec special-cases orientation to return the empty string.
       return '';
     }
 
-    function processDisplayMember(aManifest) {
+    function processDisplayMember() {
       const spec = {
         objectName: 'manifest',
-        object: aManifest,
+        object: rawManifest,
         property: 'display',
         expectedType: 'string',
         trim: true
       };
       const value = extractor.extractValue(spec);
       if (ManifestProcessor.displayModes.has(value)) {
         return value;
       }
       return ManifestProcessor.defaultDisplayMode;
     }
 
-    function processScopeMember(aManifest, aManifestURL, aDocURL, aStartURL) {
+    function processScopeMember() {
       const spec = {
         objectName: 'manifest',
-        object: aManifest,
+        object: rawManifest,
         property: 'scope',
         expectedType: 'string',
         trim: false
       };
       let scopeURL;
+      const startURL = new URL(processedManifest.start_url);
       const value = extractor.extractValue(spec);
       if (value === undefined || value === '') {
         return undefined;
       }
       try {
-        scopeURL = new URL(value, aManifestURL);
+        scopeURL = new URL(value, manifestURL);
       } catch (e) {
         let msg = 'The URL of scope is invalid.';
         console.warn(msg);
         return undefined;
       }
-      if (scopeURL.origin !== aDocURL.origin) {
+      if (scopeURL.origin !== docURL.origin) {
         let msg = 'Scope needs to be same-origin as Document.';
         console.warn(msg);
         return undefined;
       }
       // If start URL is not within scope of scope URL:
-      let isSameOrigin = aStartURL && aStartURL.origin !== scopeURL.origin;
-      if (isSameOrigin || !aStartURL.pathname.startsWith(scopeURL.pathname)) {
+      let isSameOrigin = startURL && startURL.origin !== scopeURL.origin;
+      if (isSameOrigin || !startURL.pathname.startsWith(scopeURL.pathname)) {
         let msg =
           'The start URL is outside the scope, so scope is invalid.';
         console.warn(msg);
         return undefined;
       }
       return scopeURL.href;
     }
 
-    function processStartURLMember(aManifest, aManifestURL, aDocURL) {
+    function processStartURLMember() {
       const spec = {
         objectName: 'manifest',
-        object: aManifest,
+        object: rawManifest,
         property: 'start_url',
         expectedType: 'string',
         trim: false
       };
-      let result = new URL(aDocURL).href;
+      let result = new URL(docURL).href;
       const value = extractor.extractValue(spec);
       if (value === undefined || value === '') {
         return result;
       }
       let potentialResult;
       try {
-        potentialResult = new URL(value, aManifestURL);
+        potentialResult = new URL(value, manifestURL);
       } catch (e) {
         console.warn('Invalid URL.');
         return result;
       }
-      if (potentialResult.origin !== aDocURL.origin) {
+      if (potentialResult.origin !== docURL.origin) {
         let msg = 'start_url must be same origin as document.';
         console.warn(msg);
       } else {
         result = potentialResult.href;
       }
       return result;
     }
 
-    function processThemeColorMember(aManifest) {
+    function processThemeColorMember() {
       const spec = {
         objectName: 'manifest',
-        object: aManifest,
+        object: rawManifest,
         property: 'theme_color',
         expectedType: 'string',
         trim: true
       };
       return extractor.extractColorValue(spec);
     }
 
-    function processLangMember(aManifest) {
+    function processLangMember() {
       const spec = {
         objectName: 'manifest',
-        object: aManifest,
+        object: rawManifest,
         property: 'lang',
         expectedType: 'string',
         trim: true
       };
       let tag = extractor.extractValue(spec);
       // TODO: Check if tag is structurally valid.
       //       Cannot do this because we don't support Intl API on Android.
       //       https://bugzilla.mozilla.org/show_bug.cgi?id=864843
@@ -265,8 +261,9 @@ ManifestProcessor.prototype = {
       //       Can't do this today because there is no direct means to
       //       access canonicalization algorithms through Intl API.
       //       https://github.com/tc39/ecma402/issues/5
       return tag;
     }
   }
 };
 this.ManifestProcessor = ManifestProcessor; // jshint ignore:line
+this.EXPORTED_SYMBOLS = ['ManifestProcessor']; // jshint ignore:line
rename from dom/manifest/ManifestValueExtractor.jsm
rename to dom/manifest/ValueExtractor.js
--- a/dom/manifest/ManifestValueExtractor.jsm
+++ b/dom/manifest/ValueExtractor.js
@@ -1,43 +1,38 @@
 /* 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 https://www.mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 /*
  * Helper functions extract values from manifest members
  * and reports conformance violations.
  */
 /*globals Components*/
 'use strict';
-const imports = {};
 const {
   classes: Cc,
   interfaces: Ci
 } = Components;
-imports.DOMUtils = Cc['@mozilla.org/inspector/dom-utils;1']
-  .getService(Ci.inIDOMUtils);
 
-this.EXPORTED_SYMBOLS = ['ManifestValueExtractor']; // jshint ignore:line
-
-function ManifestValueExtractor(aConsole) {
+function ValueExtractor(aConsole) {
   this.console = aConsole;
 }
 
-ManifestValueExtractor.prototype = {
+ValueExtractor.prototype = {
   // This function takes a 'spec' object and destructures
   // it to extract a value. If the value is of th wrong type, it
   // warns the developer and returns undefined.
   //  expectType: is the type of a JS primitive (string, number, etc.)
   //  object: is the object from which to extract the value.
   //  objectName: string used to construct the developer warning.
   //  property: the name of the property being extracted.
   //  trim: boolean, if the value should be trimmed (used by string type).
   extractValue({
-    expectedType, object, objectName, property, trim
-  }) {
+      expectedType, object, objectName, property, trim
+    }) {
     const value = object[property];
     const isArray = Array.isArray(value);
     // We need to special-case "array", as it's not a JS primitive.
     const type = (isArray) ? 'array' : typeof value;
     if (type !== expectedType) {
       if (type !== 'undefined') {
         let msg = `Expected the ${objectName}'s ${property} `;
         msg += `member to be a ${expectedType}.`;
@@ -49,20 +44,22 @@ ManifestValueExtractor.prototype = {
     const shouldTrim = expectedType === 'string' && value && trim;
     if (shouldTrim) {
       return value.trim() || undefined;
     }
     return value;
   },
   extractColorValue(spec) {
     const value = this.extractValue(spec);
+    const DOMUtils = Cc['@mozilla.org/inspector/dom-utils;1']
+      .getService(Ci.inIDOMUtils);
     let color;
-    if (imports.DOMUtils.isValidCSSColor(value)) {
+    if (DOMUtils.isValidCSSColor(value)) {
       color = value;
-    } else {
+    } else if (value) {
       const msg = `background_color: ${value} is not a valid CSS color.`;
       this.console.warn(msg);
     }
     return color;
   }
 };
-
-this.ManifestValueExtractor = ManifestValueExtractor; // jshint ignore:line
+this.ValueExtractor = ValueExtractor; // jshint ignore:line
+this.EXPORTED_SYMBOLS = ['ValueExtractor']; // jshint ignore:line
new file mode 100644
--- /dev/null
+++ b/dom/manifest/WebManifest.jsm
@@ -0,0 +1,19 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+/*exported EXPORTED_SYMBOLS, ManifestProcessor, ManifestObtainer*/
+/*globals Components */
+'use strict';
+const {
+  utils: Cu
+} = Components;
+
+this.EXPORTED_SYMBOLS = [
+  'ManifestObtainer',
+  'ManifestProcessor'
+];
+
+// Export public interfaces
+for (let symbl of EXPORTED_SYMBOLS) {
+  Cu.import(`resource://gre/modules/${symbl}.js`);
+}
--- a/dom/manifest/moz.build
+++ b/dom/manifest/moz.build
@@ -1,15 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXTRA_JS_MODULES += [
-    'ManifestImageObjectProcessor.jsm',
-    'ManifestObtainer.jsm',
-    'ManifestProcessor.jsm',
-    'ManifestValueExtractor.jsm'
+    'ImageObjectProcessor.js',
+    'ManifestObtainer.js',
+    'ManifestProcessor.js',
+    'ValueExtractor.js',
+    'WebManifest.jsm'
 ]
 
 MOCHITEST_MANIFESTS += ['test/mochitest.ini']
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
--- a/dom/manifest/test/browser_ManifestObtainer_obtain.js
+++ b/dom/manifest/test/browser_ManifestObtainer_obtain.js
@@ -1,12 +1,15 @@
 //Used by JSHint:
-/*global Cu, ManifestObtainer, BrowserTestUtils, add_task, SpecialPowers, todo_is, gBrowser, Assert*/
+/*global Cu, BrowserTestUtils, add_task, SpecialPowers, gBrowser, Assert*/
 'use strict';
-Cu.import('resource://gre/modules/ManifestObtainer.jsm', this);
+const {
+  ManifestObtainer
+} = Cu.import('resource://gre/modules/WebManifest.jsm', {});
+
 requestLongerTimeout(4); // e10s tests take time.
 const defaultURL =
   'http://example.org/tests/dom/manifest/test/resource.sjs';
 const remoteURL =
   'http://mochi.test:8888/tests/dom/manifest/test/resource.sjs';
 const tests = [
   // Fetch tests.
   {
@@ -184,17 +187,17 @@ add_task(function*() {
   ];
   // Once all the pages have loaded, run a bunch of tests in "parallel".
   yield Promise.all((
     for (browser of browsers) BrowserTestUtils.browserLoaded(browser)
   ));
   // Flood random browsers with requests. Once promises settle, check that
   // responses all pass.
   const results = yield Promise.all((
-    for (browser of randBrowsers(browsers, 1000)) obtainer.obtainManifest(browser)
+    for (browser of randBrowsers(browsers, 100)) obtainer.obtainManifest(browser)
   ));
   const expected = 'Expect every manifest to have name equal to `pass`.';
   const pass = results.every(manifest => manifest.name === 'pass');
   Assert.ok(pass, expected);
   //cleanup
   browsers
     .map(browser => gBrowser.getTabForBrowser(browser))
     .forEach(tab => gBrowser.removeTab(tab));
--- a/dom/manifest/test/common.js
+++ b/dom/manifest/test/common.js
@@ -1,20 +1,22 @@
 /**
  * Common infrastructure for manifest tests.
  **/
 
 'use strict';
-const bsp = SpecialPowers.Cu.import('resource://gre/modules/ManifestProcessor.jsm'),
-  processor = new bsp.ManifestProcessor(),
-  manifestURL = new URL(document.location.origin + '/manifest.json'),
-  docURL = document.location,
-  seperators = '\u2028\u2029\u0020\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000',
-  lineTerminators = '\u000D\u000A\u2028\u2029',
-  whiteSpace = `${seperators}${lineTerminators}`,
-  typeTests = [1, null, {},
-    [], false
-  ],
-  data = {
-    jsonText: '{}',
-    manifestURL: manifestURL,
-    docURL: docURL
-  };
+const {
+  ManifestProcessor
+} = SpecialPowers.Cu.import('resource://gre/modules/WebManifest.jsm');
+const processor = new ManifestProcessor();
+const manifestURL = new URL(document.location.origin + '/manifest.json');
+const docURL = document.location;
+const seperators = '\u2028\u2029\u0020\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000';
+const lineTerminators = '\u000D\u000A\u2028\u2029';
+const whiteSpace = `${seperators}${lineTerminators}`;
+const typeTests = [1, null, {},
+  [], false
+];
+const data = {
+  jsonText: '{}',
+  manifestURL: manifestURL,
+  docURL: docURL
+};
--- a/dom/media/AudioStream.cpp
+++ b/dom/media/AudioStream.cpp
@@ -7,16 +7,17 @@
 #include <math.h>
 #include <string.h>
 #include "mozilla/Logging.h"
 #include "prdtoa.h"
 #include "AudioStream.h"
 #include "VideoUtils.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Mutex.h"
+#include "mozilla/Snprintf.h"
 #include <algorithm>
 #include "mozilla/Telemetry.h"
 #include "soundtouch/SoundTouch.h"
 #include "Latency.h"
 #include "CubebUtils.h"
 #include "nsPrintfCString.h"
 #ifdef XP_MACOSX
 #include <sys/sysctl.h>
@@ -259,17 +260,17 @@ static void SetUint32LE(uint8_t* aDest, 
 }
 
 static FILE*
 OpenDumpFile(AudioStream* aStream)
 {
   if (!getenv("MOZ_DUMP_AUDIO"))
     return nullptr;
   char buf[100];
-  sprintf(buf, "dumped-audio-%d.wav", gDumpedAudioCount);
+  snprintf_literal(buf, "dumped-audio-%d.wav", gDumpedAudioCount);
   FILE* f = fopen(buf, "wb");
   if (!f)
     return nullptr;
   ++gDumpedAudioCount;
 
   uint8_t header[] = {
     // RIFF header
     0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45,
--- a/dom/media/MediaRecorder.cpp
+++ b/dom/media/MediaRecorder.cpp
@@ -633,24 +633,23 @@ private:
     // ExtractRunnable/DestroyRunnable will take the response to
     // end the session.
     mNeedSessionEndTask = false;
   }
   // application should get blob and onstop event
   void DoSessionEndTask(nsresult rv)
   {
     MOZ_ASSERT(NS_IsMainThread());
+    CleanupStreams();
     if (NS_FAILED(rv)) {
       nsCOMPtr<nsIRunnable> runnable =
         NS_NewRunnableMethodWithArg<nsresult>(mRecorder,
                                               &MediaRecorder::NotifyError, rv);
       NS_DispatchToMainThread(runnable);
     }
-
-    CleanupStreams();
     if (NS_FAILED(NS_DispatchToMainThread(new EncoderErrorNotifierRunnable(this)))) {
       MOZ_ASSERT(false, "NS_DispatchToMainThread EncoderErrorNotifierRunnable failed");
     }
     if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
       MOZ_ASSERT(false, "NS_DispatchToMainThread PushBlobRunnable failed");
     }
     if (NS_FAILED(NS_DispatchToMainThread(new DestroyRunnable(this)))) {
       MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -309,16 +309,19 @@ if CONFIG['MOZ_WEBRTC']:
 DEFINES['MOZILLA_INTERNAL_API'] = True
 
 if CONFIG['MOZ_OMX_DECODER']:
     DEFINES['MOZ_OMX_DECODER'] = True
 
 if CONFIG['ANDROID_VERSION'] > '15':
     DEFINES['MOZ_OMX_WEBM_DECODER'] = True
 
+if CONFIG['MOZ_GONK_MEDIACODEC']:
+    DEFINES['MOZ_GONK_MEDIACODEC'] = True
+
 CFLAGS += CONFIG['GSTREAMER_CFLAGS']
 CXXFLAGS += CONFIG['GSTREAMER_CFLAGS']
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 # Suppress some GCC warnings being treated as errors:
 #  - about attributes on forward declarations for types that are already
 #    defined, which complains about an important MOZ_EXPORT for android::AString
--- a/dom/media/test/test_mediarecorder_unsupported_src.html
+++ b/dom/media/test/test_mediarecorder_unsupported_src.html
@@ -21,16 +21,23 @@ function startTest() {
       // 4. onstop
       var callbackStep = 0;
       var mediaRecorder = new MediaRecorder(stream);
 
       is(mediaRecorder.stream, stream, 'Stream should be provided on creation');
 
       mediaRecorder.onerror = function (e) {
         callbackStep++;
+        if (callbackStep == 1) {
+          try {
+            mediaRecorder.pause();
+          } catch(e) {
+            ok(false, 'Should not get exception in pause call.');
+          }
+        }
         ok(callbackStep < 3, 'onerror callback fired as expected.');
         is(e.name, 'GenericError', 'Error name should be GenericError.');
         is(mediaRecorder.mimeType, '', 'mimetype should be empty');
         is(mediaRecorder.state, 'recording', 'state is recording');
         info('onerror callback fired');
       }
 
       mediaRecorder.onwarning = function () {
@@ -55,36 +62,31 @@ function startTest() {
         SimpleTest.finish();
       };
 
       try {
         mediaRecorder.start();
       } catch(e) {
         ok(false, 'Should not get exception in start call.');
       }
-
-      try {
-        mediaRecorder.pause();
-      } catch(e) {
-        ok(false, 'Should not get exception in pause call.');
-      }
     },
     function(err) {
       ok(false, 'Unexpected error fired with: ' + err);
       SimpleTest.finish();
     }
   );
 }
 
 SimpleTest.waitForExplicitFinish();
 
 // In order to generate an "unsupported stream", pref off video encoding to
 // make the platform support audio encoding only.
 SpecialPowers.pushPrefEnv(
   {
     "set": [
-      ["media.encoder.webm.enabled", false]
+      ["media.encoder.webm.enabled", false],
+      ["media.encoder.omx.enabled", false]
     ]
   }, startTest);
 
 </script>
 </head>
 </html>
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -2561,17 +2561,16 @@ PluginModuleChild::RecvStopProfiler()
 {
     profiler_stop();
     return true;
 }
 
 bool
 PluginModuleChild::AnswerGetProfile(nsCString* aProfile)
 {
-    char* profile = profiler_get_profile();
+    UniquePtr<char[]> profile = profiler_get_profile();
     if (profile != nullptr) {
-        *aProfile = nsCString(profile, strlen(profile));
-        free(profile);
+        *aProfile = nsCString(profile.get(), strlen(profile.get()));
     } else {
         *aProfile = nsCString("", 0);
     }
     return true;
 }
--- a/dom/system/gonk/GonkGPSGeolocationProvider.cpp
+++ b/dom/system/gonk/GonkGPSGeolocationProvider.cpp
@@ -504,47 +504,76 @@ GonkGPSGeolocationProvider::RequestSetID
       }
     }
   }
 
   NS_ConvertUTF16toUTF8 idBytes(id);
   mAGpsRilInterface->set_set_id(type, idBytes.get());
 }
 
+namespace {
+int
+ConvertToGpsRefLocationType(const nsAString& aConnectionType)
+{
+  const char* GSM_TYPES[] = { "gsm", "gprs", "edge" };
+  const char* UMTS_TYPES[] = { "umts", "hspda", "hsupa", "hspa", "hspa+" };
+
+  for (auto type: GSM_TYPES) {
+    if (aConnectionType.EqualsASCII(type)) {
+        return AGPS_REF_LOCATION_TYPE_GSM_CELLID;
+    }
+  }
+
+  for (auto type: UMTS_TYPES) {
+    if (aConnectionType.EqualsASCII(type)) {
+      return AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
+    }
+  }
+
+  if (gDebug_isLoggingEnabled) {
+    nsContentUtils::LogMessageToConsole("geo: Unsupported connection type %s\n",
+                                        NS_ConvertUTF16toUTF8(aConnectionType).get());
+  }
+  return AGPS_REF_LOCATION_TYPE_GSM_CELLID;
+}
+} // anonymous namespace
+
 void
 GonkGPSGeolocationProvider::SetReferenceLocation()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!mRadioInterface ||
       !mAGpsRilInterface) {
     return;
   }
 
   AGpsRefLocation location;
 
-  // TODO: Bug 772750 - get mobile connection technology from rilcontext
-  location.type = AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
-
   nsCOMPtr<nsIMobileConnectionService> service =
     do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
   if (!service) {
     NS_WARNING("Cannot get MobileConnectionService");
     return;
   }
 
   nsCOMPtr<nsIMobileConnection> connection;
-  // TODO: Bug 878748 - B2G GPS: acquire correct RadioInterface instance in
-  // MultiSIM configuration
-  service->GetItemByServiceId(0 /* Client Id */, getter_AddRefs(connection));
+  service->GetItemByServiceId(mRilDataServiceId, getter_AddRefs(connection));
   NS_ENSURE_TRUE_VOID(connection);
 
   nsCOMPtr<nsIMobileConnectionInfo> voice;
   connection->GetVoice(getter_AddRefs(voice));
   if (voice) {
+    nsAutoString connectionType;
+    nsresult rv = voice->GetType(connectionType);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+    location.type = ConvertToGpsRefLocationType(connectionType);
+
     nsCOMPtr<nsIMobileNetworkInfo> networkInfo;
     voice->GetNetwork(getter_AddRefs(networkInfo));
     if (networkInfo) {
       nsresult result;
       nsAutoString mcc, mnc;
 
       networkInfo->GetMcc(mcc);
       networkInfo->GetMnc(mnc);
@@ -1021,19 +1050,17 @@ GonkGPSGeolocationProvider::Observe(nsIS
           do {
             nsCOMPtr<nsIMobileConnectionService> service =
               do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
             if (!service) {
               break;
             }
 
             nsCOMPtr<nsIMobileConnection> connection;
-            // TODO: Bug 878748 - B2G GPS: acquire correct RadioInterface instance in
-            // MultiSIM configuration
-            service->GetItemByServiceId(0 /* Client Id */, getter_AddRefs(connection));
+            service->GetItemByServiceId(mRilDataServiceId, getter_AddRefs(connection));
             if (!connection) {
               break;
             }
 
             nsCOMPtr<nsIMobileConnectionInfo> voice;
             connection->GetVoice(getter_AddRefs(voice));
             if (voice) {
               voice->GetRoaming(&roaming);
--- a/dom/wifi/WifiHotspotUtils.cpp
+++ b/dom/wifi/WifiHotspotUtils.cpp
@@ -9,16 +9,17 @@
 #include <errno.h>
 #include <string.h>
 #include <dirent.h>
 #include <stdlib.h>
 #include <cutils/properties.h>
 
 #include "prinit.h"
 #include "mozilla/Assertions.h"
+#include "mozilla/Snprintf.h"
 #include "nsDebug.h"
 #include "nsPrintfCString.h"
 
 static void* sWifiHotspotUtilsLib;
 static PRCallOnceType sInitWifiHotspotUtilsLib;
 // Socket pair used to exit from a blocking read.
 static struct wpa_ctrl* ctrl_conn;
 static const char *ctrl_iface_dir = "/data/misc/wifi/hostapd";
@@ -172,16 +173,16 @@ int32_t WifiHotspotUtils::do_wifi_hostap
   int stations = 0;
   size_t addrLen = sizeof(addr);
 
   if (sendCommand(ctrl_conn, "STA-FIRST", addr, &addrLen)) {
     return 0;
   }
   stations++;
 
-  sprintf(cmd, "STA-NEXT %s", addr);
+  snprintf_literal(cmd, "STA-NEXT %s", addr);
   while (sendCommand(ctrl_conn, cmd, addr, &addrLen) == 0) {
     stations++;
-    sprintf(cmd, "STA-NEXT %s", addr);
+    snprintf_literal(cmd, "STA-NEXT %s", addr);
   }
 
   return stations;
 }
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -996,47 +996,42 @@ private:
       rv = ssm->GetChannelResultPrincipal(channel, getter_AddRefs(channelPrincipal));
       NS_ENSURE_SUCCESS(rv, rv);
 
       nsCOMPtr<nsILoadGroup> channelLoadGroup;
       rv = channel->GetLoadGroup(getter_AddRefs(channelLoadGroup));
       NS_ENSURE_SUCCESS(rv, rv);
       MOZ_ASSERT(channelLoadGroup);
 
-      // See if this is a resource URI. Since JSMs usually come from resource://
-      // URIs we're currently considering all URIs with the URI_IS_UI_RESOURCE
-      // flag as valid for creating privileged workers.
-      if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) {
-        bool isResource;
-        rv = NS_URIChainHasFlags(finalURI,
-                                 nsIProtocolHandler::URI_IS_UI_RESOURCE,
-                                 &isResource);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        if (isResource) {
-          rv = ssm->GetSystemPrincipal(getter_AddRefs(channelPrincipal));
-          NS_ENSURE_SUCCESS(rv, rv);
-        }
-      }
-
       // If the load principal is the system principal then the channel
       // principal must also be the system principal (we do not allow chrome
       // code to create workers with non-chrome scripts). Otherwise this channel
       // principal must be same origin with the load principal (we check again
       // here in case redirects changed the location of the script).
       if (nsContentUtils::IsSystemPrincipal(loadPrincipal)) {
         if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) {
-          return NS_ERROR_DOM_BAD_URI;
+          // See if this is a resource URI. Since JSMs usually come from
+          // resource:// URIs we're currently considering all URIs with the
+          // URI_IS_UI_RESOURCE flag as valid for creating privileged workers.
+          bool isResource;
+          rv = NS_URIChainHasFlags(finalURI,
+                                   nsIProtocolHandler::URI_IS_UI_RESOURCE,
+                                   &isResource);
+          NS_ENSURE_SUCCESS(rv, rv);
+
+          if (isResource) {
+            // Assign the system principal to the resource:// worker only if it
+            // was loaded from code using the system principal.
+            channelPrincipal = loadPrincipal;
+          } else {
+            return NS_ERROR_DOM_BAD_URI;
+          }
         }
       }
       else  {
-        nsCString scheme;
-        rv = finalURI->GetScheme(scheme);
-        NS_ENSURE_SUCCESS(rv, rv);
-
         // We exempt data urls and other URI's that inherit their
         // principal again.
         if (NS_FAILED(loadPrincipal->CheckMayLoad(finalURI, false, true))) {
           return NS_ERROR_DOM_BAD_URI;
         }
       }
 
       // The principal can change, but it should still match the original
--- a/extensions/auth/nsHttpNegotiateAuth.cpp
+++ b/extensions/auth/nsHttpNegotiateAuth.cpp
@@ -32,16 +32,17 @@
 #include "nsNetCID.h"
 #include "plbase64.h"
 #include "plstr.h"
 #include "prprf.h"
 #include "mozilla/Logging.h"
 #include "prmem.h"
 #include "prnetdb.h"
 #include "mozilla/Likely.h"
+#include "mozilla/Snprintf.h"
 
 //-----------------------------------------------------------------------------
 
 static const char kNegotiate[] = "Negotiate";
 static const char kNegotiateAuthTrustedURIs[] = "network.negotiate-auth.trusted-uris";
 static const char kNegotiateAuthDelegationURIs[] = "network.negotiate-auth.delegation-uris";
 static const char kNegotiateAuthAllowProxies[] = "network.negotiate-auth.allow-proxies";
 static const char kNegotiateAuthAllowNonFqdn[] = "network.negotiate-auth.allow-non-fqdn";
@@ -271,21 +272,22 @@ nsHttpNegotiateAuth::GenerateCredentials
     free(outToken);
 
     if (!encoded_token)
         return NS_ERROR_OUT_OF_MEMORY;
 
     LOG(("  Sending a token of length %d\n", outTokenLen));
 
     // allocate a buffer sizeof("Negotiate" + " " + b64output_token + "\0")
-    *creds = (char *) moz_xmalloc(kNegotiateLen + 1 + strlen(encoded_token) + 1);
+    const int bufsize = kNegotiateLen + 1 + strlen(encoded_token) + 1;
+    *creds = (char *) moz_xmalloc(bufsize);
     if (MOZ_UNLIKELY(!*creds))
         rv = NS_ERROR_OUT_OF_MEMORY;
     else
-        sprintf(*creds, "%s %s", kNegotiate, encoded_token);
+        snprintf(*creds, bufsize, "%s %s", kNegotiate, encoded_token);
 
     PR_Free(encoded_token);
     return rv;
 }
 
 bool
 nsHttpNegotiateAuth::TestBoolPref(const char *pref)
 {
--- a/extensions/spellcheck/locales/en-US/hunspell/README_en_US.txt
+++ b/extensions/spellcheck/locales/en-US/hunspell/README_en_US.txt
@@ -1,11 +1,11 @@
 en_US-mozilla Hunspell Dictionary
-Generated from SCOWL Version 2015.04.24
-Fri May  8 09:52:28 EDT 2015
+Generated from SCOWL Version 2015.05.18
+Wed May 27 08:05:38 EDT 2015
 
 http://wordlist.sourceforge.net
 
 README file for English Hunspell dictionaries derived from SCOWL.
 
 These dictionaries are created using the speller/make-hunspell-dict
 script in SCOWL.
 
@@ -306,9 +306,9 @@ from the Ispell distribution they are un
   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   POSSIBILITY OF SUCH DAMAGE.
 
-Build Date: Fri May  8 09:52:28 EDT 2015
+Build Date: Wed May 27 08:05:38 EDT 2015
--- a/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/5-mozilla-added
+++ b/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/5-mozilla-added
@@ -12172,16 +12172,19 @@ codecs
 codon
 codon's
 codons
 coli
 colonoscope
 colonoscope's
 colonoscopes
 commenters
+composited
+compositeness
+compositing
 concurrent's
 concurrents
 concuss's
 condole's
 conferable
 config
 config's
 configs
@@ -12359,16 +12362,17 @@ donators
 durian
 durian's
 durians
 eBook
 eBook's
 eBooks
 eCommerce
 eCommerce's
+eldritch
 elicitor
 elicitor's
 elicitors
 encyclopaedia
 enqueue
 enqueued
 enqueues
 enqueuing
@@ -12924,17 +12928,16 @@ testsuite's
 testsuites
 textbox
 textbox's
 textboxes
 thaliana
 therebetween
 theremin's
 theremins
-timeline
 timeline's
 timelines
 toolbar's
 toolbars
 traceur
 traceur's
 traceurs
 trackback
--- a/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/orig/README_en_US.txt
+++ b/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/orig/README_en_US.txt
@@ -1,11 +1,11 @@
 en_US Hunspell Dictionary
-Generated from SCOWL Version 2015.04.24
-Fri May  8 09:52:07 EDT 2015
+Generated from SCOWL Version 2015.05.18
+Wed May 27 08:05:20 EDT 2015
 
 http://wordlist.sourceforge.net
 
 README file for English Hunspell dictionaries derived from SCOWL.
 
 These dictionaries are created using the speller/make-hunspell-dict
 script in SCOWL.
 
@@ -306,10 +306,10 @@ from the Ispell distribution they are un
   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   POSSIBILITY OF SUCH DAMAGE.
 
-Build Date: Fri May  8 09:52:07 EDT 2015
+Build Date: Wed May 27 08:05:20 EDT 2015
 Wordlist Command: mk-list --accents=strip en_US 60
--- a/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/orig/en_US.aff
+++ b/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/orig/en_US.aff
@@ -107,23 +107,24 @@ SFX M   0     's         .
 SFX B Y 3
 SFX B   0     able       [^aeiou]
 SFX B   0     able       ee
 SFX B   e     able       [^aeiou]e
 
 SFX L Y 1
 SFX L   0     ment       .
 
-REP 88
+REP 90
 REP a ei
 REP ei a
 REP a ey
 REP ey a
 REP ai ie
 REP ie ai
+REP alot a_lot
 REP are air
 REP are ear
 REP are eir
 REP air are
 REP air ere
 REP ere air
 REP ere ear
 REP ere eir
@@ -196,8 +197,9 @@ REP ear air
 REP air ear
 REP w qu
 REP qu w
 REP z ss
 REP ss z
 REP shun tion
 REP shun sion
 REP shun cion
+REP size cise
--- a/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/orig/en_US.dic
+++ b/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/orig/en_US.dic
@@ -1,9 +1,9 @@
-48722
+48743
 0/nm
 0th/pt
 1/n1
 1st/p
 1th/tc
 2/nm
 2nd/p
 2th/tc
@@ -11516,16 +11516,17 @@ amide/MS
 amidships
 amigo/MS
 amino
 amiss
 amity/M
 ammeter/SM
 ammo/M
 ammonia/M
+ammonium
 ammunition/M
 amnesia/M
 amnesiac/MS
 amnesic/SM
 amnesty/GDSM
 amniocenteses
 amniocentesis/M
 amnion/MS
@@ -13752,16 +13753,17 @@ biographic
 biographical/Y
 biography/SM
 biol
 biologic
 biological/Y
 biologist/MS
 biology/M
 biomass/M
+biomedical
 bionic/S
 bionically
 bionics/M
 biophysical
 biophysicist/MS
 biophysics/M
 biopic/MS
 biopsy/GDSM
@@ -20372,16 +20374,17 @@ doorplate/SM
 doorpost/S
 doorstep/MS
 doorstepped
 doorstepping
 doorstop/MS
 doorway/SM
 dooryard/MS
 dopa/M
+dopamine
 dope/MZGDRS
 doper/M
 dopey
 dopier
 dopiest
 dopiness/M
 doping/M
 doppelganger/S
@@ -21475,16 +21478,17 @@ endocrinology/M
 endogenous/Y
 endorphin/M
 endorse/LZGDRS
 endorsement/MS
 endorser/M
 endoscope/MS
 endoscopic
 endoscopy/M
+endothelial
 endothermic
 endow/SDLG
 endowment/MS
 endpoint/SM
 endue/DSG
 endurable/U
 endurance/M
 endure/DSBG
@@ -21706,16 +21710,18 @@ epilogue/MS
 epinephrine/M
 epiphany/SM
 episcopacy/M
 episcopal
 episcopate/M
 episode/SM
 episodic
 episodically
+epistemic
+epistemological
 epistemology
 epistle/SM
 epistolary
 epitaph/M
 epitaphs
 epithelial
 epithelium/M
 epithet/SM
@@ -25928,16 +25934,17 @@ headwaters/M
 headway/M
 headwind/SM
 headword/SM
 heady/RTP
 heal/DRHZGS
 healed/U
 healer/M
 health/M
+healthcare
 healthful/PY
 healthfulness/M
 healthily/U
 healthiness/UM
 healthy/UTRP
 heap/MDGS
 hear/AHGJS
 heard/AU
@@ -30365,16 +30372,17 @@ macrame/M
 macro/SM
 macrobiotic/S
 macrobiotics/M
 macrocosm/SM
 macroeconomic/S
 macroeconomics/M
 macrology/S
 macron/MS
+macrophages
 macroscopic
 mad/SMYP
 madam/SM
 madame/M
 madcap/MS
 madden/DGS
 maddening/Y
 madder/MS
@@ -31745,16 +31753,19 @@ misunderstand/SGJ
 misunderstanding/M
 misunderstood
 misuse/DSMG
 mite/MZRS
 miter/MDG
 mitigate/DSGN
 mitigated/U
 mitigation/M
+mitochondria
+mitochondrial
+mitochondrion
 mitoses
 mitosis/M
 mitotic
 mitt/MNSX
 mitten/M
 mix/ZGMDRSB
 mixed/U
 mixer/M
@@ -31785,16 +31796,17 @@ moccasin/SM
 mocha/M
 mock/DRSZG
 mocker/M
 mockery/SM
 mocking/Y
 mockingbird/SM
 mod/STM
 modal/SM
+modality/S
 modded
 modding
 mode/MS
 model/ZGSJMDR
 modeler/M
 modeling/M
 modem/SM
 moderate/MYGNPDS
@@ -32426,16 +32438,17 @@ muzak
 muzzily
 muzzle/DSMG
 muzzy/P
 my
 mycologist/SM
 mycology/M
 myelitis/M
 myna/MS
+myocardial
 myocardium
 myopia/M
 myopic
 myopically
 myriad/SM
 myrmidon/MS
 myrrh/M
 myrtle/SM
@@ -35293,16 +35306,17 @@ peppercorn/SM
 peppermint/SM
 pepperoni/MS
 peppery
 peppiness/M
 pepping
 peppy/TPR
 pepsin/M
 peptic/MS
+peptide/S
 peradventure/M
 perambulate/XGNDS
 perambulation/M
 perambulator/MS
 percale/MS
 perceive/BGDS
 perceived/U
 percent/MS
@@ -35351,16 +35365,17 @@ perform/SDRZG
 performance/SM
 performed/U
 performer/M
 perfume/DRSMZG
 perfumer/M
 perfumery/SM
 perfunctorily
 perfunctory
+perfusion
 pergola/SM
 perhaps
 pericardia
 pericardial
 pericardium/M
 perigee/SM
 perihelia
 perihelion/M
@@ -36234,17 +36249,17 @@ podding
 podiatrist/SM
 podiatry/M
 podium/SM
 poem/MS
 poesy/M
 poet/MS
 poetaster/MS
 poetess/MS
-poetic
+poetic/S
 poetical/Y
 poetry/M
 pogrom/SM
 poi/M
 poignancy/M
 poignant/Y
 poinciana/SM
 poinsettia/SM
@@ -38176,16 +38191,17 @@ rcpt
 rd
 re/DSMYTGVJ
 reach/MDSGB
 reachable/U
 reacquire/DSG
 react/V
 reactant/SM
 reactionary/SM
+reactivity
 read/ZGMRBJS
 readability/SM
 reader/M
 readership/SM
 readily
 readiness/M
 reading/M
 readmitted
@@ -41298,16 +41314,17 @@ sit/S
 sitar/SM
 sitarist/MS
 sitcom/SM
 site/MGDS
 sitter/SM
 sitting/SM
 situate/DSXGN
 situation/M
+situational
 six/MSH
 sixfold
 sixpence/MS
 sixshooter/M
 sixteen/SMH
 sixteenth/M
 sixteenths
 sixth/M
@@ -42836,16 +42853,17 @@ stemmed
 stemming
 stemware/M
 stench/MS
 stencil/GMDS
 steno/SM
 stenographer/SM
 stenographic
 stenography/M
+stenosis
 stentorian
 step/IMS
 stepbrother/SM
 stepchild/M
 stepchildren
 stepdad/MS
 stepdaughter/SM
 stepfather/SM
@@ -44987,16 +45005,17 @@ timberland/M
 timberline/MS
 timbre/SM
 timbrel/SM
 time/MYZGJDRS
 timekeeper/MS
 timekeeping/M
 timeless/PY
 timelessness/M
+timeline
 timeliness/UM
 timely/UPRT
 timeout/SM
 timepiece/MS
 timer/M
 timescale/S
 timeserver/SM
 timeserving/M
@@ -47006,16 +47025,17 @@ vermin/M
 verminous
 vermouth/M
 vernacular/MS
 vernal
 vernier/SM
 veronica/M
 verruca/SM
 verrucae
+versa
 versatile
 versatility/M
 verse/AFNGMSDX
 versed/U
 versification/M
 versifier/M
 versify/ZGNDRS
 version/AFIMS
@@ -47563,16 +47583,17 @@ wast
 wastage/M
 waste/DRSMZG
 wastebasket/MS
 wasteful/PY
 wastefulness/M
 wasteland/SM
 wastepaper/M
 waster/M
+wastewater
 wastrel/SM
 watch/BZGMDRS
 watchband/MS
 watchdog/SM
 watcher/M
 watchful/YP
 watchfulness/M
 watchmaker/MS
--- a/extensions/spellcheck/locales/en-US/hunspell/en-US.aff
+++ b/extensions/spellcheck/locales/en-US/hunspell/en-US.aff
@@ -105,23 +105,24 @@ SFX M   0     's         .
 SFX B Y 3
 SFX B   0     able       [^aeiou]
 SFX B   0     able       ee
 SFX B   e     able       [^aeiou]e
 
 SFX L Y 1
 SFX L   0     ment       .
 
-REP 88
+REP 90
 REP a ei
 REP ei a
 REP a ey
 REP ey a
 REP ai ie
 REP ie ai
+REP alot a_lot
 REP are air
 REP are ear
 REP are eir
 REP air are
 REP air ere
 REP ere air
 REP ere ear
 REP ere eir
@@ -194,8 +195,9 @@ REP ear air
 REP air ear
 REP w qu
 REP qu w
 REP z ss
 REP ss z
 REP shun tion
 REP shun sion
 REP shun cion
+REP size cise
--- a/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
+++ b/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
@@ -1,9 +1,9 @@
-54817
+54837
 0/nm
 0th/pt
 1/n1
 1st/p
 1th/tc
 2/nm
 2nd/p
 2th/tc
@@ -16878,17 +16878,16 @@ action/ASM
 actionable
 activate/ICANGSD
 activation/ICAM
 activator/MS
 active/KIMYS
 activeness/M
 activism/M
 activist/MS
-activity's/A
 activity/ISM
 actor/AMS
 actress/MS
 actual/Y
 actuality/SM
 actualization/M
 actualize/GDS
 actuarial
@@ -17597,16 +17596,17 @@ amide/MS
 amidships
 amigo/MS
 amino
 amiss
 amity/M
 ammeter/SM
 ammo/M
 ammonia/M
+ammonium
 ammunition/M
 amnesia/M
 amnesiac/MS
 amnesic/SM
 amnesty/GDSM
 amniocenteses
 amniocentesis/M
 amnion/MS
@@ -19848,16 +19848,17 @@ biographical/Y
 biography/SM
 bioinformatic/MS
 biol
 biologic
 biological/Y
 biologist/MS
 biology/M
 biomass/M
+biomedical
 bionic/S
 bionically
 bionics/M
 biophysical
 biophysicist/MS
 biophysics/M
 biopic/MS
 biopsy/GDSM
@@ -23329,17 +23330,17 @@ complimentary/U
 comply/NDSXG
 compo/S
 component/SM
 comport/LSGD
 comportment/M
 compose/AECGSD
 composedly
 composer/MS
-composite/MYNXSDGP
+composite/MYGNXPDS
 composition/CM
 compositor/SM
 compost/SGMD
 composure/EM
 compote/SM
 compound/GMDBS
 compounded/U
 comprehend/SDG
@@ -26461,16 +26462,17 @@ doorplate/SM
 doorpost/S
 doorstep/MS
 doorstepped
 doorstepping
 doorstop/MS
 doorway/SM
 dooryard/MS
 dopa/M
+dopamine
 dope/MZGDRS
 doper/M
 dopey
 dopier
 dopiest
 dopiness/M
 doping/M
 doppelganger/S
@@ -27214,16 +27216,17 @@ elate/DSGN
 elated/Y
 elation/M
 elbow/SMDG
 elbowroom/M
 elder/SMY
 elderberry/SM
 eldercare/M
 eldest
+eldritch
 elect/ASMDGV
 electable
 election/AMS
 electioneer/DGS
 elective/MS
 elector/MS
 electoral/Y
 electorate/MS
@@ -27560,16 +27563,17 @@ endocrinology/M
 endogenous/Y
 endorphin/M
 endorse/LZGDRS
 endorsement/MS
 endorser/M
 endoscope/MS
 endoscopic
 endoscopy/M
+endothelial
 endothermic
 endow/SDLG
 endowment/MS
 endpoint/SM
 endue/DSG
 endurable/U
 endurance/M
 endure/DSBG
@@ -27792,16 +27796,18 @@ epilogue/MS
 epinephrine/M
 epiphany/SM
 episcopacy/M
 episcopal
 episcopate/M
 episode/SM
 episodic
 episodically
+epistemic
+epistemological
 epistemology
 epistle/SM
 epistolary
 epitaph/M
 epitaphs
 epithelial
 epithelium/M
 epithet/SM
@@ -32009,16 +32015,17 @@ headwaters/M
 headway/M
 headwind/SM
 headword/SM
 heady/RTP
 heal/DRHZGS
 healed/U
 healer/M
 health/M
+healthcare
 healthful/PY
 healthfulness/M
 healthily/U
 healthiness/UM
 healthy/UTRP
 heap/MDGS
 hear/AHGJS
 heard/AU
@@ -36445,16 +36452,17 @@ macrame/M
 macro/SM
 macrobiotic/S
 macrobiotics/M
 macrocosm/SM
 macroeconomic/S
 macroeconomics/M
 macrology/S
 macron/MS
+macrophages
 macroscopic
 mad/SMYP
 madam/SM
 madame/M
 madcap/MS
 madden/DGS
 maddening/Y
 madder/MS
@@ -37825,16 +37833,19 @@ misunderstand/SGJ
 misunderstanding/M
 misunderstood
 misuse/DSMG
 mite/MZRS
 miter/MDG
 mitigate/XDSGN
 mitigated/U
 mitigation/M
+mitochondria
+mitochondrial
+mitochondrion
 mitoses
 mitosis/M
 mitotic
 mitt/MNSX
 mitten/M
 mix/ZGMDRSB
 mixed/U
 mixer/M
@@ -37864,16 +37875,17 @@ moccasin/SM
 mocha/M
 mock/DRSZG
 mocker/M
 mockery/SM
 mocking/Y
 mockingbird/SM
 mod/STM
 modal/SM
+modality/S
 modded
 modding
 mode/MS
 model/ZGSJMDR
 modeler/M
 modeling/M
 modeller/MS
 modelling/MS
@@ -38511,16 +38523,17 @@ muzzily
 muzzle/DSMG
 muzzy/P
 my
 mySimon
 mycologist/SM
 mycology/M
 myelitis/M
 myna/MS
+myocardial
 myocardium
 myopia/M
 myopic
 myopically
 myriad/SM
 myrmidon/MS
 myrrh/M
 myrtle/SM
@@ -41383,16 +41396,17 @@ peppercorn/SM
 peppermint/SM
 pepperoni/MS
 peppery
 peppiness/M
 pepping
 peppy/TPR
 pepsin/M
 peptic/MS
+peptide/S
 peradventure/M
 perambulate/XGNDS
 perambulation/M
 perambulator/MS
 percale/MS
 perceive/BGDS
 perceived/U
 percent/MS
@@ -41441,16 +41455,17 @@ perform/SDRZG
 performance/SM
 performed/U
 performer/M
 perfume/DRSMZG
 perfumer/M
 perfumery/SM
 perfunctorily
 perfunctory
+perfusion
 pergola/SM
 perhaps
 pericardia
 pericardial
 pericardium/M
 perigee/SM
 perihelia
 perihelion/M
@@ -42326,17 +42341,17 @@ podding
 podiatrist/SM
 podiatry/M
 podium/SM
 poem/MS
 poesy/M
 poet/MS
 poetaster/MS
 poetess/MS
-poetic
+poetic/S
 poetical/Y
 poetry/M
 pogrom/SM
 poi/M
 poignancy/M
 poignant/Y
 poinciana/SM
 poinsettia/SM
@@ -44271,16 +44286,17 @@ rcpt
 rd
 re/DSMYTGVJ
 reach/MDSGB
 reachable/U
 reacquire/DSG
 react/V
 reactant/SM
 reactionary/SM
+reactivity/M
 read/ZGMRBJS
 readability/SM
 reader/M
 readership/SM
 readily
 readiness/M
 reading/M
 readmitted
@@ -47400,16 +47416,17 @@ sitar/SM
 sitarist/MS
 sitcom/SM
 site/MGDS
 sitemap/SM
 sitter/SM
 sitting/SM
 situate/DSXGN
 situation/M
+situational
 six/MSH
 sixfold
 sixpence/MS
 sixshooter/M
 sixteen/SMH
 sixteenth/M
 sixteenths
 sixth/M
@@ -48935,16 +48952,17 @@ stemmed
 stemming
 stemware/M
 stench/MS
 stencil/GMDS
 steno/SM
 stenographer/SM
 stenographic
 stenography/M
+stenosis
 stent/SM
 stentorian
 step/IMS
 stepbrother/SM
 stepchild/M
 stepchildren
 stepdad/MS
 stepdaughter/SM
@@ -53106,16 +53124,17 @@ vermin/M
 verminous
 vermouth/M
 vernacular/MS
 vernal
 vernier/SM
 veronica/M
 verruca/SM
 verrucae
+versa
 versatile
 versatility/M
 verse/AFNGMSDX
 versed/U
 versification/M
 versifier/M
 versify/ZGNDRS
 version/AFIMS
@@ -53664,16 +53683,17 @@ wast
 wastage/M
 waste/DRSMZG
 wastebasket/MS
 wasteful/PY
 wastefulness/M
 wasteland/SM
 wastepaper/M
 waster/M
+wastewater
 wastrel/SM
 watch/BZGMDRS
 watchband/MS
 watchdog/SM
 watcher/M
 watchful/YP
 watchfulness/M
 watchmaker/MS
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -727,16 +727,17 @@ struct ParamTraits<mozilla::layers::Fram
     WriteParam(aMsg, aParam.mScrollGeneration);
     WriteParam(aMsg, aParam.mExtraResolution);
     WriteParam(aMsg, aParam.mBackgroundColor);
     WriteParam(aMsg, aParam.mDoSmoothScroll);
     WriteParam(aMsg, aParam.mSmoothScrollOffset);
     WriteParam(aMsg, aParam.GetLineScrollAmount());
     WriteParam(aMsg, aParam.GetPageScrollAmount());
     WriteParam(aMsg, aParam.AllowVerticalScrollWithWheel());
+    WriteParam(aMsg, aParam.mClipRect);
     WriteParam(aMsg, aParam.GetContentDescription());
   }
 
   static bool ReadContentDescription(const Message* aMsg, void** aIter, paramType* aResult)
   {
     nsCString str;
     if (!ReadParam(aMsg, aIter, &str)) {
       return false;
@@ -769,16 +770,17 @@ struct ParamTraits<mozilla::layers::Fram
             ReadParam(aMsg, aIter, &aResult->mScrollGeneration) &&
             ReadParam(aMsg, aIter, &aResult->mExtraResolution) &&
             ReadParam(aMsg, aIter, &aResult->mBackgroundColor) &&
             ReadParam(aMsg, aIter, &aResult->mDoSmoothScroll) &&
             ReadParam(aMsg, aIter, &aResult->mSmoothScrollOffset) &&
             ReadParam(aMsg, aIter, &aResult->mLineScrollAmount) &&
             ReadParam(aMsg, aIter, &aResult->mPageScrollAmount) &&
             ReadParam(aMsg, aIter, &aResult->mAllowVerticalScrollWithWheel) &&
+            ReadParam(aMsg, aIter, &aResult->mClipRect) &&
             ReadContentDescription(aMsg, aIter, aResult));
   }
 };
 
 template<>
 struct ParamTraits<mozilla::layers::TextureFactoryIdentifier>
 {
   typedef mozilla::layers::TextureFactoryIdentifier paramType;
--- a/gfx/layers/Effects.h
+++ b/gfx/layers/Effects.h
@@ -281,17 +281,18 @@ inline TemporaryRef<TexturedEffect>
 CreateTexturedEffect(TextureSource* aSource,
                      TextureSource* aSourceOnWhite,
                      const gfx::Filter& aFilter,
                      bool isAlphaPremultiplied)
 {
   MOZ_ASSERT(aSource);
   if (aSourceOnWhite) {
     MOZ_ASSERT(aSource->GetFormat() == gfx::SurfaceFormat::R8G8B8X8 ||
-               aSourceOnWhite->GetFormat() == gfx::SurfaceFormat::B8G8R8X8);
+               aSource->GetFormat() == gfx::SurfaceFormat::B8G8R8X8);
+    MOZ_ASSERT(aSource->GetFormat() == aSourceOnWhite->GetFormat());
     return MakeAndAddRef<EffectComponentAlpha>(aSource, aSourceOnWhite, aFilter);
   }
 
   return CreateTexturedEffect(aSource->GetFormat(),
                               aSource,
                               aFilter,
                               isAlphaPremultiplied);
 }
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -3,16 +3,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 GFX_FRAMEMETRICS_H
 #define GFX_FRAMEMETRICS_H
 
 #include <stdint.h>                     // for uint32_t, uint64_t
 #include "Units.h"                      // for CSSRect, CSSPixel, etc
+#include "mozilla/Maybe.h"
 #include "mozilla/gfx/BasePoint.h"      // for BasePoint
 #include "mozilla/gfx/Rect.h"           // for RoundedIn
 #include "mozilla/gfx/ScaleFactor.h"    // for ScaleFactor
 #include "mozilla/gfx/Logging.h"        // for Log
 #include "gfxColor.h"
 #include "nsString.h"
 
 namespace IPC {
@@ -93,17 +94,18 @@ public:
            mHasScrollgrab == aOther.mHasScrollgrab &&
            mUpdateScrollOffset == aOther.mUpdateScrollOffset &&
            mScrollGeneration == aOther.mScrollGeneration &&
            mExtraResolution == aOther.mExtraResolution &&
            mBackgroundColor == aOther.mBackgroundColor &&
            mDoSmoothScroll == aOther.mDoSmoothScroll &&
            mLineScrollAmount == aOther.mLineScrollAmount &&
            mPageScrollAmount == aOther.mPageScrollAmount &&
-           mAllowVerticalScrollWithWheel == aOther.mAllowVerticalScrollWithWheel;
+           mAllowVerticalScrollWithWheel == aOther.mAllowVerticalScrollWithWheel &&
+           mClipRect == aOther.mClipRect;
   }
   bool operator!=(const FrameMetrics& aOther) const
   {
     return !operator==(aOther);
   }
 
   bool IsDefault() const
   {
@@ -512,16 +514,31 @@ public:
     return mAllowVerticalScrollWithWheel;
   }
 
   void SetAllowVerticalScrollWithWheel()
   {
     mAllowVerticalScrollWithWheel = true;
   }
 
+  void SetClipRect(const Maybe<ParentLayerIntRect>& aClipRect)
+  {
+    mClipRect = aClipRect;
+  }
+  const Maybe<ParentLayerIntRect>& GetClipRect() const
+  {
+    return mClipRect;
+  }
+  bool HasClipRect() const {
+    return mClipRect.isSome();
+  }
+  const ParentLayerIntRect& ClipRect() const {
+    return mClipRect.ref();
+  }
+
 private:
 
   // The pres-shell resolution that has been induced on the document containing
   // this scroll frame as a result of zooming this scroll frame (whether via
   // user action, or choosing an initial zoom level on page load). This can
   // only be different from 1.0 for frames that are zoomable, which currently
   // is just the root content document's root scroll frame (mIsRoot = true).
   // This is a plain float rather than a ScaleFactor because in and of itself
@@ -683,16 +700,19 @@ private:
   LayoutDeviceIntSize mLineScrollAmount;
 
   // The value of GetPageScrollAmount(), for scroll frames.
   LayoutDeviceIntSize mPageScrollAmount;
 
   // Whether or not the frame can be vertically scrolled with a mouse wheel.
   bool mAllowVerticalScrollWithWheel;
 
+  // The clip rect to use when compositing a layer with this FrameMetrics.
+  Maybe<ParentLayerIntRect> mClipRect;
+
   // WARNING!!!!
   //
   // When adding new fields to FrameMetrics, the following places should be
   // updated to include them (as needed):
   //    FrameMetrics::operator ==
   //    AsyncPanZoomController::NotifyLayersUpdated
   //    The ParamTraits specialization in GfxMessageUtils.h
   //
--- a/gfx/layers/LayersLogging.cpp
+++ b/gfx/layers/LayersLogging.cpp
@@ -178,16 +178,19 @@ AppendToString(std::stringstream& aStrea
   AppendToString(aStream, m.GetDisplayPort(), "] [dp=");
   AppendToString(aStream, m.GetCriticalDisplayPort(), "] [cdp=");
   AppendToString(aStream, m.GetBackgroundColor(), "] [color=");
   if (!detailed) {
     AppendToString(aStream, m.GetScrollId(), "] [scrollId=");
     if (m.GetScrollParentId() != FrameMetrics::NULL_SCROLL_ID) {
       AppendToString(aStream, m.GetScrollParentId(), "] [scrollParent=");
     }
+    if (m.HasClipRect()) {
+      AppendToString(aStream, m.ClipRect(), "] [clip=");
+    }
     AppendToString(aStream, m.GetZoom(), "] [z=", "] }");
   } else {
     AppendToString(aStream, m.GetDisplayPortMargins(), " [dpm=");
     aStream << nsPrintfCString("] um=%d", m.GetUseDisplayPortMargins()).get();
     AppendToString(aStream, m.GetRootCompositionSize(), "] [rcs=");
     AppendToString(aStream, m.GetViewport(), "] [v=");
     aStream << nsPrintfCString("] [z=(ld=%.3f r=%.3f",
             m.GetDevPixelsPerCSSPixel().scale,
--- a/gfx/layers/TiledLayerBuffer.h
+++ b/gfx/layers/TiledLayerBuffer.h
@@ -89,37 +89,47 @@ namespace layers {
 // should always be a factor of the tile length, to avoid tiles covering
 // non-integer amounts of pixels.
 
 template<typename Derived, typename Tile>
 class TiledLayerBuffer
 {
 public:
   TiledLayerBuffer()
-    : mRetainedWidth(0)
+    : mFirstTileX(0)
+    , mFirstTileY(0)
+    , mRetainedWidth(0)
     , mRetainedHeight(0)
     , mResolution(1)
     , mTileSize(gfxPlatform::GetPlatform()->GetTileWidth(), gfxPlatform::GetPlatform()->GetTileHeight())
   {}
 
   ~TiledLayerBuffer() {}
 
   // Given a tile origin aligned to a multiple of GetScaledTileSize,
   // return the tile that describes that region.
   // NOTE: To get the valid area of that tile you must intersect
   //       (aTileOrigin.x, aTileOrigin.y,
   //        GetScaledTileSize().width, GetScaledTileSize().height)
   //       and GetValidRegion() to get the area of the tile that is valid.
-  Tile GetTile(const nsIntPoint& aTileOrigin) const;
-
+  Tile& GetTile(const gfx::IntPoint& aTileOrigin);
   // Given a tile x, y relative to the top left of the layer, this function
   // will return the tile for
   // (x*GetScaledTileSize().width, y*GetScaledTileSize().height,
   //  GetScaledTileSize().width, GetScaledTileSize().height)
-  Tile GetTile(int x, int y) const;
+  Tile& GetTile(int x, int y);
+
+  int TileIndex(const gfx::IntPoint& aTileOrigin) const;
+  int TileIndex(int x, int y) const { return x * mRetainedHeight + y; }
+
+  bool HasTile(int index) const { return index >= 0 && index < (int)mRetainedTiles.Length(); }
+  bool HasTile(const gfx::IntPoint& aTileOrigin) const;
+  bool HasTile(int x, int y) const {
+    return x >= 0 && x < mRetainedWidth && y >= 0 && y < mRetainedHeight;
+  }
 
   const gfx::IntSize& GetTileSize() const { return mTileSize; }
 
   gfx::IntSize GetScaledTileSize() const { return RoundedToInt(gfx::Size(mTileSize) / mResolution); }
 
   unsigned int GetTileCount() const { return mRetainedTiles.Length(); }
 
   const nsIntRegion& GetValidRegion() const { return mValidRegion; }
@@ -150,51 +160,49 @@ public:
   int RoundDownToTileEdge(int aX, int aTileLength) const { return aX - GetTileStart(aX, aTileLength); }
 
   // Get and set draw scaling. mResolution affects the resolution at which the
   // contents of the buffer are drawn. mResolution has no effect on the
   // coordinate space of the valid region, but does affect the size of an
   // individual tile's rect in relation to the valid region.
   // Setting the resolution will invalidate the buffer.
   float GetResolution() const { return mResolution; }
-  void SetResolution(float aResolution) {
-    if (mResolution == aResolution) {
-      return;
-    }
-
-    Update(nsIntRegion(), nsIntRegion());
-    mResolution = aResolution;
-  }
   bool IsLowPrecision() const { return mResolution < 1; }
 
   typedef Tile* Iterator;
   Iterator TilesBegin() { return mRetainedTiles.Elements(); }
   Iterator TilesEnd() { return mRetainedTiles.Elements() + mRetainedTiles.Length(); }
 
   void Dump(std::stringstream& aStream, const char* aPrefix, bool aDumpHtml);
 
 protected:
   // The implementor should call Update() to change
   // the new valid region. This implementation will call
   // validateTile on each tile that is dirty, which is left
   // to the implementor.
   void Update(const nsIntRegion& aNewValidRegion, const nsIntRegion& aPaintRegion);
 
+  // Return a reference to this tile in GetTile when the requested tile offset
+  // does not exist.
+  Tile mPlaceHolderTile;
+
   nsIntRegion     mValidRegion;
   nsIntRegion     mPaintedRegion;
 
   /**
    * mRetainedTiles is a rectangular buffer of mRetainedWidth x mRetainedHeight
    * stored as column major with the same origin as mValidRegion.GetBounds().
    * Any tile that does not intersect mValidRegion is a PlaceholderTile.
    * Only the region intersecting with mValidRegion should be read from a tile,
    * another other region is assumed to be uninitialized. The contents of the
    * tiles is scaled by mResolution.
    */
   nsTArray<Tile>  mRetainedTiles;
+  int             mFirstTileX;
+  int             mFirstTileY;
   int             mRetainedWidth;  // in tiles
   int             mRetainedHeight; // in tiles
   float           mResolution;
   gfx::IntSize    mTileSize;
 
 private:
   const Derived& AsDerived() const { return *static_cast<const Derived*>(this); }
   Derived& AsDerived() { return *static_cast<Derived*>(this); }
@@ -244,58 +252,73 @@ static inline int floor_div(int a, int b
     int sub;
     sub = a ^ b;
     // The results of this shift is either 0 or -1.
     sub >>= 8*sizeof(int)-1;
     return div+sub;
   }
 }
 
-template<typename Derived, typename Tile> Tile
-TiledLayerBuffer<Derived, Tile>::GetTile(const nsIntPoint& aTileOrigin) const
+template<typename Derived, typename Tile> bool
+TiledLayerBuffer<Derived, Tile>::HasTile(const gfx::IntPoint& aTileOrigin) const {
+  gfx::IntSize scaledTileSize = GetScaledTileSize();
+  return HasTile(floor_div(aTileOrigin.x, scaledTileSize.width) - mFirstTileX,
+                 floor_div(aTileOrigin.y, scaledTileSize.height) - mFirstTileY);
+}
+
+template<typename Derived, typename Tile> Tile&
+TiledLayerBuffer<Derived, Tile>::GetTile(const nsIntPoint& aTileOrigin)
 {
-  // TODO Cache firstTileOriginX/firstTileOriginY
+  if (HasTile(aTileOrigin)) {
+    return mRetainedTiles[TileIndex(aTileOrigin)];
+  }
+  return mPlaceHolderTile;
+}
+
+template<typename Derived, typename Tile> int
+TiledLayerBuffer<Derived, Tile>::TileIndex(const gfx::IntPoint& aTileOrigin) const
+{
   // Find the tile x/y of the first tile and the target tile relative to the (0, 0)
   // origin, the difference is the tile x/y relative to the start of the tile buffer.
   gfx::IntSize scaledTileSize = GetScaledTileSize();
-  int firstTileX = floor_div(mValidRegion.GetBounds().x, scaledTileSize.width);
-  int firstTileY = floor_div(mValidRegion.GetBounds().y, scaledTileSize.height);
-  return GetTile(floor_div(aTileOrigin.x, scaledTileSize.width) - firstTileX,
-                 floor_div(aTileOrigin.y, scaledTileSize.height) - firstTileY);
+  return TileIndex(floor_div(aTileOrigin.x, scaledTileSize.width) - mFirstTileX,
+                   floor_div(aTileOrigin.y, scaledTileSize.height) - mFirstTileY);
 }
 
-template<typename Derived, typename Tile> Tile
-TiledLayerBuffer<Derived, Tile>::GetTile(int x, int y) const
+template<typename Derived, typename Tile> Tile&
+TiledLayerBuffer<Derived, Tile>::GetTile(int x, int y)
 {
-  int index = x * mRetainedHeight + y;
-  return mRetainedTiles.SafeElementAt(index, AsDerived().GetPlaceholderTile());
+  if (HasTile(x, y)) {
+    return mRetainedTiles[TileIndex(x, y)];
+  }
+  return mPlaceHolderTile;
 }
 
 template<typename Derived, typename Tile> void
 TiledLayerBuffer<Derived, Tile>::Dump(std::stringstream& aStream,
                                       const char* aPrefix,
                                       bool aDumpHtml)
 {
   gfx::IntRect visibleRect = GetValidRegion().GetBounds();
   gfx::IntSize scaledTileSize = GetScaledTileSize();
   for (int32_t x = visibleRect.x; x < visibleRect.x + visibleRect.width;) {
     int32_t tileStartX = GetTileStart(x, scaledTileSize.width);
     int32_t w = scaledTileSize.width - tileStartX;
 
     for (int32_t y = visibleRect.y; y < visibleRect.y + visibleRect.height;) {
       int32_t tileStartY = GetTileStart(y, scaledTileSize.height);
-      Tile tileTexture =
-        GetTile(nsIntPoint(RoundDownToTileEdge(x, scaledTileSize.width),
-                           RoundDownToTileEdge(y, scaledTileSize.height)));
+      nsIntPoint tileOrigin = nsIntPoint(RoundDownToTileEdge(x, scaledTileSize.width),
+                                         RoundDownToTileEdge(y, scaledTileSize.height));
+      Tile& tileTexture = GetTile(tileOrigin);
       int32_t h = scaledTileSize.height - tileStartY;
 
       aStream << "\n" << aPrefix << "Tile (x=" <<
         RoundDownToTileEdge(x, scaledTileSize.width) << ", y=" <<
         RoundDownToTileEdge(y, scaledTileSize.height) << "): ";
-      if (tileTexture != AsDerived().GetPlaceholderTile()) {
+      if (!tileTexture.IsPlaceholderTile()) {
         tileTexture.DumpTexture(aStream);
       } else {
         aStream << "empty tile";
       }
       y += h;
     }
     x += w;
   }
@@ -592,15 +615,19 @@ TiledLayerBuffer<Derived, Tile>::Update(
   }
 #endif
 
   // At this point, oldTileCount should be zero
   MOZ_ASSERT(oldTileCount == 0, "Failed to release old tiles");
 
   mRetainedTiles = newRetainedTiles;
   mValidRegion = newValidRegion;
+
+  mFirstTileX = floor_div(mValidRegion.GetBounds().x, scaledTileSize.width);
+  mFirstTileY = floor_div(mValidRegion.GetBounds().y, scaledTileSize.height);
+
   mPaintedRegion.Or(mPaintedRegion, aPaintRegion);
 }
 
 } // layers
 } // mozilla
 
 #endif // GFX_TILEDLAYERBUFFER_H
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -2893,16 +2893,17 @@ void AsyncPanZoomController::NotifyLayer
     }
     mFrameMetrics.SetCompositionBounds(aLayerMetrics.GetCompositionBounds());
     mFrameMetrics.SetRootCompositionSize(aLayerMetrics.GetRootCompositionSize());
     mFrameMetrics.SetPresShellResolution(aLayerMetrics.GetPresShellResolution());
     mFrameMetrics.SetCumulativeResolution(aLayerMetrics.GetCumulativeResolution());
     mFrameMetrics.SetHasScrollgrab(aLayerMetrics.GetHasScrollgrab());
     mFrameMetrics.SetLineScrollAmount(aLayerMetrics.GetLineScrollAmount());
     mFrameMetrics.SetPageScrollAmount(aLayerMetrics.GetPageScrollAmount());
+    mFrameMetrics.SetClipRect(aLayerMetrics.GetClipRect());
 
     if (scrollOffsetUpdated) {
       APZC_LOG("%p updating scroll offset from %s to %s\n", this,
         ToString(mFrameMetrics.GetScrollOffset()).c_str(),
         ToString(aLayerMetrics.GetScrollOffset()).c_str());
 
       mFrameMetrics.CopyScrollInfoFrom(aLayerMetrics);
 
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -369,17 +369,17 @@ gfxMemorySharedReadLock::ReadLock()
 
   return PR_ATOMIC_INCREMENT(&mReadCount);
 }
 
 int32_t
 gfxMemorySharedReadLock::ReadUnlock()
 {
   int32_t readCount = PR_ATOMIC_DECREMENT(&mReadCount);
-  NS_ASSERTION(readCount >= 0, "ReadUnlock called without ReadLock.");
+  MOZ_ASSERT(readCount >= 0);
 
   return readCount;
 }
 
 int32_t
 gfxMemorySharedReadLock::GetReadCount()
 {
   NS_ASSERT_OWNINGTHREAD(gfxMemorySharedReadLock);
@@ -419,17 +419,17 @@ gfxShmSharedReadLock::ReadLock() {
 
 int32_t
 gfxShmSharedReadLock::ReadUnlock() {
   if (!mAllocSuccess) {
     return 0;
   }
   ShmReadLockInfo* info = GetShmReadLockInfoPtr();
   int32_t readCount = PR_ATOMIC_DECREMENT(&info->readCount);
-  NS_ASSERTION(readCount >= 0, "ReadUnlock called without a ReadLock.");
+  MOZ_ASSERT(readCount >= 0);
   if (readCount <= 0) {
     mAllocator->FreeShmemSection(mShmemSection);
   }
   return readCount;
 }
 
 int32_t
 gfxShmSharedReadLock::GetReadCount() {
@@ -515,16 +515,17 @@ TileClient::TileClient(const TileClient&
 {
   mBackBuffer.Set(this, o.mBackBuffer);
   mBackBufferOnWhite = o.mBackBufferOnWhite;
   mFrontBuffer = o.mFrontBuffer;
   mFrontBufferOnWhite = o.mFrontBufferOnWhite;
   mBackLock = o.mBackLock;
   mFrontLock = o.mFrontLock;
   mCompositableClient = o.mCompositableClient;
+  mUpdateRect = o.mUpdateRect;
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   mLastUpdate = o.mLastUpdate;
 #endif
   mManager = o.mManager;
   mInvalidFront = o.mInvalidFront;
   mInvalidBack = o.mInvalidBack;
 }
 
@@ -534,16 +535,17 @@ TileClient::operator=(const TileClient& 
   if (this == &o) return *this;
   mBackBuffer.Set(this, o.mBackBuffer);
   mBackBufferOnWhite = o.mBackBufferOnWhite;
   mFrontBuffer = o.mFrontBuffer;
   mFrontBufferOnWhite = o.mFrontBufferOnWhite;
   mBackLock = o.mBackLock;
   mFrontLock = o.mFrontLock;
   mCompositableClient = o.mCompositableClient;
+  mUpdateRect = o.mUpdateRect;
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   mLastUpdate = o.mLastUpdate;
 #endif
   mManager = o.mManager;
   mInvalidFront = o.mInvalidFront;
   mInvalidBack = o.mInvalidBack;
   return *this;
 }
@@ -604,16 +606,18 @@ CopyFrontToBack(TextureClient* aFront,
 
   if (!aBack->Lock(OpenMode::OPEN_READ_WRITE)) {
     gfxCriticalError() << "[Tiling:Client] Failed to lock the tile's back buffer";
     return false;
   }
 
   gfx::IntPoint rectToCopyTopLeft = aRectToCopy.TopLeft();
   aFront->CopyToTextureClient(aBack, &aRectToCopy, &rectToCopyTopLeft);
+
+  aFront->Unlock();
   return true;
 }
 
 void
 TileClient::ValidateBackBufferFromFront(const nsIntRegion& aDirtyRegion,
                                         nsIntRegion& aAddPaintedRegion)
 {
   if (mBackBuffer && mFrontBuffer) {
@@ -700,19 +704,19 @@ TileClient::DiscardBackBuffer()
       // Our current back-buffer is still locked by the compositor. This can occur
       // when the client is producing faster than the compositor can consume. In
       // this case we just want to drop it and not return it to the pool.
      mManager->ReportClientLost(*mBackBuffer);
      if (mBackBufferOnWhite) {
        mManager->ReportClientLost(*mBackBufferOnWhite);
      }
     } else {
-      mManager->ReturnTextureClient(*mBackBuffer);
+      mManager->ReturnTextureClientDeferred(*mBackBuffer);
       if (mBackBufferOnWhite) {
-        mManager->ReturnTextureClient(*mBackBufferOnWhite);
+        mManager->ReturnTextureClientDeferred(*mBackBufferOnWhite);
       }
     }
     mBackLock->ReadUnlock();
     if (mBackBuffer->IsLocked()) {
       mBackBuffer->Unlock();
     }
     if (mBackBufferOnWhite && mBackBufferOnWhite->IsLocked()) {
       mBackBufferOnWhite->Unlock();
@@ -811,34 +815,28 @@ TileClient::GetTileDescriptor()
     // reference count doesn't go to zero before the host receives the message.
     // see TiledLayerBufferComposite::TiledLayerBufferComposite
     mFrontLock.get()->AddRef();
   }
 
   if (mFrontLock->GetType() == gfxSharedReadLock::TYPE_MEMORY) {
     return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(),
                                   mFrontBufferOnWhite ? MaybeTexture(mFrontBufferOnWhite->GetIPDLActor()) : MaybeTexture(null_t()),
+                                  mUpdateRect,
                                   TileLock(uintptr_t(mFrontLock.get())));
   } else {
     gfxShmSharedReadLock *lock = static_cast<gfxShmSharedReadLock*>(mFrontLock.get());
     return TexturedTileDescriptor(nullptr, mFrontBuffer->GetIPDLActor(),
                                   mFrontBufferOnWhite ? MaybeTexture(mFrontBufferOnWhite->GetIPDLActor()) : MaybeTexture(null_t()),
+                                  mUpdateRect,
                                   TileLock(lock->GetShmemSection()));
   }
 }
 
 void
-ClientTiledLayerBuffer::ReadUnlock() {
-  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
-    if (mRetainedTiles[i].IsPlaceholderTile()) continue;
-    mRetainedTiles[i].ReadUnlock();
-  }
-}
-
-void
 ClientTiledLayerBuffer::ReadLock() {
   for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
     if (mRetainedTiles[i].IsPlaceholderTile()) continue;
     mRetainedTiles[i].ReadLock();
   }
 }
 
 void
@@ -868,19 +866,22 @@ ClientTiledLayerBuffer::GetSurfaceDescri
   for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
     TileDescriptor tileDesc;
     if (mRetainedTiles.SafeElementAt(i, GetPlaceholderTile()) == GetPlaceholderTile()) {
       tileDesc = PlaceholderTileDescriptor();
     } else {
       tileDesc = mRetainedTiles[i].GetTileDescriptor();
     }
     tiles.AppendElement(tileDesc);
+    mRetainedTiles[i].mUpdateRect = IntRect();
   }
   return SurfaceDescriptorTiles(mValidRegion, mPaintedRegion,
-                                tiles, mRetainedWidth, mRetainedHeight,
+                                tiles,
+                                mFirstTileX, mFirstTileY,
+                                mRetainedWidth, mRetainedHeight,
                                 mResolution, mFrameResolution.xScale,
                                 mFrameResolution.yScale);
 }
 
 void
 ClientTiledLayerBuffer::PaintThebes(const nsIntRegion& aNewValidRegion,
                                    const nsIntRegion& aPaintRegion,
                                    LayerManager::DrawPaintedLayerCallback aCallback,
@@ -1131,16 +1132,18 @@ ClientTiledLayerBuffer::ValidateTile(Til
   nsIntRegion extraPainted;
   RefPtr<TextureClient> backBufferOnWhite;
   RefPtr<TextureClient> backBuffer =
     aTile.GetBackBuffer(offsetScaledDirtyRegion,
                         content, mode,
                         &createdTextureClient, extraPainted,
                         &backBufferOnWhite);
 
+  aTile.mUpdateRect = offsetScaledDirtyRegion.GetBounds().Union(extraPainted.GetBounds());
+
   extraPainted.MoveBy(aTileOrigin);
   extraPainted.And(extraPainted, mNewValidRegion);
   mPaintedRegion.Or(mPaintedRegion, extraPainted);
 
   if (!backBuffer) {
     gfxCriticalError() << "[Tiling:Client] Failed to allocate a TextureClient";
     aTile.DiscardBackBuffer();
     aTile.DiscardFrontBuffer();
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -265,16 +265,17 @@ struct TileClient
       RefPtr<TextureClient> mBuffer;
   } mBackBuffer;
   RefPtr<TextureClient> mBackBufferOnWhite;
   RefPtr<TextureClient> mFrontBuffer;
   RefPtr<TextureClient> mFrontBufferOnWhite;
   RefPtr<gfxSharedReadLock> mBackLock;
   RefPtr<gfxSharedReadLock> mFrontLock;
   RefPtr<ClientLayerManager> mManager;
+  gfx::IntRect mUpdateRect;
   CompositableClient* mCompositableClient;
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   TimeStamp        mLastUpdate;
 #endif
   nsIntRegion mInvalidFront;
   nsIntRegion mInvalidBack;
   nsExpirationState mExpirationState;
 
@@ -413,18 +414,16 @@ public:
                     std::numeric_limits<int32_t>::max())
   {}
 
   void PaintThebes(const nsIntRegion& aNewValidRegion,
                    const nsIntRegion& aPaintRegion,
                    LayerManager::DrawPaintedLayerCallback aCallback,
                    void* aCallbackData);
 
-  void ReadUnlock();
-
   void ReadLock();
 
   void Release();
 
   void DiscardBuffers();
 
   const CSSToParentLayerScale2D& GetFrameResolution() { return mFrameResolution; }
 
@@ -440,16 +439,25 @@ public:
                          nsIntRegion& aInvalidRegion,
                          const nsIntRegion& aOldValidRegion,
                          BasicTiledLayerPaintData* aPaintData,
                          LayerManager::DrawPaintedLayerCallback aCallback,
                          void* aCallbackData);
 
   SurfaceDescriptorTiles GetSurfaceDescriptorTiles();
 
+  void SetResolution(float aResolution) {
+    if (mResolution == aResolution) {
+      return;
+    }
+
+    Update(nsIntRegion(), nsIntRegion());
+    mResolution = aResolution;
+  }
+
 protected:
   TileClient ValidateTile(TileClient aTile,
                           const nsIntPoint& aTileRect,
                           const nsIntRegion& dirtyRect);
 
   void PostValidate(const nsIntRegion& aPaintRegion);
 
   void UnlockTile(TileClient aTile);
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -618,16 +618,27 @@ AsyncCompositionManager::ApplyAsyncConte
 #endif
 
     mIsFirstPaint = false;
     mLayersUpdated = false;
 
     // Apply the render offset
     mLayerManager->GetCompositor()->SetScreenRenderOffset(offset);
 
+    // See the comment below - the first FrameMetrics has the clip computed
+    // by layout (currently, effectively the composition bounds), which we
+    // intersect here to include the layer clip.
+    if (i == 0 && metrics.HasClipRect()) {
+      if (clipRect) {
+        clipRect = Some(clipRect.value().Intersect(metrics.ClipRect()));
+      } else {
+        clipRect = Some(metrics.ClipRect());
+      }
+    }
+
     combinedAsyncTransformWithoutOverscroll *= asyncTransformWithoutOverscroll;
     combinedAsyncTransform *= (Matrix4x4(asyncTransformWithoutOverscroll) * overscrollTransform);
     if (i > 0 && clipRect) {
       // The clip rect Layout calculates is the intersection of the composition
       // bounds of all the scroll frames at the time of the paint (when there
       // are no async transforms).
       // An async transform on a scroll frame does not affect the composition
       // bounds of *that* scroll frame, but it does affect the composition
--- a/gfx/layers/composite/TiledContentHost.cpp
+++ b/gfx/layers/composite/TiledContentHost.cpp
@@ -24,382 +24,364 @@ class gfxReusableSurfaceWrapper;
 namespace mozilla {
 using namespace gfx;
 namespace layers {
 
 class Layer;
 
 TiledLayerBufferComposite::TiledLayerBufferComposite()
   : mFrameResolution()
-  , mHasDoubleBufferedTiles(false)
-  , mIsValid(false)
 {}
 
+TiledLayerBufferComposite::~TiledLayerBufferComposite()
+{
+  Clear();
+}
+
 /* static */ void
 TiledLayerBufferComposite::RecycleCallback(TextureHost* textureHost, void* aClosure)
 {
   textureHost->CompositorRecycle();
 }
 
-TiledLayerBufferComposite::TiledLayerBufferComposite(ISurfaceAllocator* aAllocator,
-                                                     const SurfaceDescriptorTiles& aDescriptor,
-                                                     const nsIntRegion& aOldPaintedRegion,
-                                                     Compositor* aCompositor)
-{
-  mIsValid = true;
-  mHasDoubleBufferedTiles = false;
-  mValidRegion = aDescriptor.validRegion();
-  mPaintedRegion = aDescriptor.paintedRegion();
-  mRetainedWidth = aDescriptor.retainedWidth();
-  mRetainedHeight = aDescriptor.retainedHeight();
-  mResolution = aDescriptor.resolution();
-  mFrameResolution = CSSToParentLayerScale2D(aDescriptor.frameXResolution(),
-                                             aDescriptor.frameYResolution());
-  if (mResolution == 0 || IsNaN(mResolution)) {
-    // There are divisions by mResolution so this protects the compositor process
-    // against malicious content processes and fuzzing.
-    mIsValid = false;
-    return;
-  }
-
-  // Combine any valid content that wasn't already uploaded
-  nsIntRegion oldPaintedRegion(aOldPaintedRegion);
-  oldPaintedRegion.And(oldPaintedRegion, mValidRegion);
-  mPaintedRegion.Or(mPaintedRegion, oldPaintedRegion);
-
-  bool isSameProcess = aAllocator->IsSameProcess();
-
-  const InfallibleTArray<TileDescriptor>& tiles = aDescriptor.tiles();
-  for(size_t i = 0; i < tiles.Length(); i++) {
-    CompositableTextureHostRef texture;
-    CompositableTextureHostRef textureOnWhite;
-    const TileDescriptor& tileDesc = tiles[i];
-    switch (tileDesc.type()) {
-      case TileDescriptor::TTexturedTileDescriptor : {
-        texture = TextureHost::AsTextureHost(tileDesc.get_TexturedTileDescriptor().textureParent());
-        MaybeTexture onWhite = tileDesc.get_TexturedTileDescriptor().textureOnWhite();
-        if (onWhite.type() == MaybeTexture::TPTextureParent) {
-          textureOnWhite = TextureHost::AsTextureHost(onWhite.get_PTextureParent());
-        }
-        const TileLock& ipcLock = tileDesc.get_TexturedTileDescriptor().sharedLock();
-        nsRefPtr<gfxSharedReadLock> sharedLock;
-        if (ipcLock.type() == TileLock::TShmemSection) {
-          sharedLock = gfxShmSharedReadLock::Open(aAllocator, ipcLock.get_ShmemSection());
-        } else {
-          if (!isSameProcess) {
-            // Trying to use a memory based lock instead of a shmem based one in
-            // the cross-process case is a bad security violation.
-            NS_ERROR("A client process may be trying to peek at the host's address space!");
-            // This tells the TiledContentHost that deserialization failed so that
-            // it can propagate the error.
-            mIsValid = false;
-
-            mRetainedTiles.Clear();
-            return;
-          }
-          sharedLock = reinterpret_cast<gfxMemorySharedReadLock*>(ipcLock.get_uintptr_t());
-          if (sharedLock) {
-            // The corresponding AddRef is in TiledClient::GetTileDescriptor
-            sharedLock.get()->Release();
-          }
-        }
-
-        CompositableTextureSourceRef textureSource;
-        CompositableTextureSourceRef textureSourceOnWhite;
-        if (texture) {
-          texture->SetCompositor(aCompositor);
-          texture->PrepareTextureSource(textureSource);
-        }
-        if (textureOnWhite) {
-          textureOnWhite->SetCompositor(aCompositor);
-          textureOnWhite->PrepareTextureSource(textureSourceOnWhite);
-        }
-        mRetainedTiles.AppendElement(TileHost(sharedLock,
-                                              texture.get(),
-                                              textureOnWhite.get(),
-                                              textureSource.get(),
-                                              textureSourceOnWhite.get()));
-        break;
-      }
-      default:
-        NS_WARNING("Unrecognised tile descriptor type");
-        // Fall through
-      case TileDescriptor::TPlaceholderTileDescriptor :
-        mRetainedTiles.AppendElement(GetPlaceholderTile());
-        break;
-    }
-    if (texture && !texture->HasInternalBuffer()) {
-      mHasDoubleBufferedTiles = true;
-    }
-  }
-}
-
-void
-TiledLayerBufferComposite::ReadUnlock()
-{
-  if (!IsValid()) {
-    return;
-  }
-  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
-    mRetainedTiles[i].ReadUnlock();
-  }
-}
-
-void
-TiledLayerBufferComposite::ReleaseTextureHosts()
-{
-  if (!IsValid()) {
-    return;
-  }
-  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
-    mRetainedTiles[i].mTextureHost = nullptr;
-    mRetainedTiles[i].mTextureHostOnWhite = nullptr;
-    mRetainedTiles[i].mTextureSource = nullptr;
-    mRetainedTiles[i].mTextureSourceOnWhite = nullptr;
-  }
-}
-
-void
-TiledLayerBufferComposite::Upload()
-{
-  if(!IsValid()) {
-    return;
-  }
-  // The TextureClients were created with the TextureFlags::IMMEDIATE_UPLOAD flag,
-  // so calling Update on all the texture hosts will perform the texture upload.
-  Update(mValidRegion, mPaintedRegion);
-  ClearPaintedRegion();
-}
-
-TileHost
-TiledLayerBufferComposite::ValidateTile(TileHost aTile,
-                                        const IntPoint& aTileOrigin,
-                                        const nsIntRegion& aDirtyRect)
-{
-  if (aTile.IsPlaceholderTile()) {
-    NS_WARNING("Placeholder tile encountered in painted region");
-    return aTile;
-  }
-
-#ifdef GFX_TILEDLAYER_PREF_WARNINGS
-  printf_stderr("Upload tile %i, %i\n", aTileOrigin.x, aTileOrigin.y);
-  long start = PR_IntervalNow();
-#endif
-
-  MOZ_ASSERT(aTile.mTextureHost->GetFlags() & TextureFlags::IMMEDIATE_UPLOAD);
-
-#ifdef MOZ_GFX_OPTIMIZE_MOBILE
-  MOZ_ASSERT(!aTile.mTextureHostOnWhite);
-  // We possibly upload the entire texture contents here. This is a purposeful
-  // decision, as sub-image upload can often be slow and/or unreliable, but
-  // we may want to reevaluate this in the future.
-  // For !HasInternalBuffer() textures, this is likely a no-op.
-  aTile.mTextureHost->Updated(nullptr);
-#else
-  nsIntRegion tileUpdated = aDirtyRect.MovedBy(-aTileOrigin);
-  aTile.mTextureHost->Updated(&tileUpdated);
-  if (aTile.mTextureHostOnWhite) {
-    aTile.mTextureHostOnWhite->Updated(&tileUpdated);
-  }
-#endif
-
-#ifdef GFX_TILEDLAYER_PREF_WARNINGS
-  if (PR_IntervalNow() - start > 1) {
-    printf_stderr("Tile Time to upload %i\n", PR_IntervalNow() - start);
-  }
-#endif
-  return aTile;
-}
-
 void
 TiledLayerBufferComposite::SetCompositor(Compositor* aCompositor)
 {
   MOZ_ASSERT(aCompositor);
-  if (!IsValid()) {
-    return;
-  }
-  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
-    if (mRetainedTiles[i].IsPlaceholderTile()) continue;
-    mRetainedTiles[i].mTextureHost->SetCompositor(aCompositor);
-    if (mRetainedTiles[i].mTextureHostOnWhite) {
-      mRetainedTiles[i].mTextureHostOnWhite->SetCompositor(aCompositor);
+  for (TileHost& tile : mRetainedTiles) {
+    if (tile.IsPlaceholderTile()) continue;
+    tile.mTextureHost->SetCompositor(aCompositor);
+    if (tile.mTextureHostOnWhite) {
+      tile.mTextureHostOnWhite->SetCompositor(aCompositor);
     }
   }
 }
 
 TiledContentHost::TiledContentHost(const TextureInfo& aTextureInfo)
   : ContentHost(aTextureInfo)
   , mTiledBuffer(TiledLayerBufferComposite())
   , mLowPrecisionTiledBuffer(TiledLayerBufferComposite())
-  , mOldTiledBuffer(TiledLayerBufferComposite())
-  , mOldLowPrecisionTiledBuffer(TiledLayerBufferComposite())
-  , mPendingUpload(false)
-  , mPendingLowPrecisionUpload(false)
 {
   MOZ_COUNT_CTOR(TiledContentHost);
 }
 
 TiledContentHost::~TiledContentHost()
 {
   MOZ_COUNT_DTOR(TiledContentHost);
-
-  // Unlock any buffers that may still be locked. If we have a pending upload,
-  // we will need to unlock the buffer that was about to be uploaded.
-  // If a buffer that was being composited had double-buffered tiles, we will
-  // need to unlock that buffer too.
-  if (mPendingUpload) {
-    mTiledBuffer.ReadUnlock();
-    if (mOldTiledBuffer.HasDoubleBufferedTiles()) {
-      mOldTiledBuffer.ReadUnlock();
-    }
-  } else if (mTiledBuffer.HasDoubleBufferedTiles()) {
-    mTiledBuffer.ReadUnlock();
-  }
-
-  if (mPendingLowPrecisionUpload) {
-    mLowPrecisionTiledBuffer.ReadUnlock();
-    if (mOldLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
-      mOldLowPrecisionTiledBuffer.ReadUnlock();
-    }
-  } else if (mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
-    mLowPrecisionTiledBuffer.ReadUnlock();
-  }
 }
 
 void
 TiledContentHost::Attach(Layer* aLayer,
                          Compositor* aCompositor,
                          AttachFlags aFlags /* = NO_FLAGS */)
 {
   CompositableHost::Attach(aLayer, aCompositor, aFlags);
 }
 
 void
 TiledContentHost::Detach(Layer* aLayer,
                          AttachFlags aFlags /* = NO_FLAGS */)
 {
   if (!mKeepAttached || aLayer == mLayer || aFlags & FORCE_DETACH) {
-
-    // Unlock any buffers that may still be locked. If we have a pending upload,
-    // we will need to unlock the buffer that was about to be uploaded.
-    // If a buffer that was being composited had double-buffered tiles, we will
-    // need to unlock that buffer too.
-    if (mPendingUpload) {
-      mTiledBuffer.ReadUnlock();
-      if (mOldTiledBuffer.HasDoubleBufferedTiles()) {
-        mOldTiledBuffer.ReadUnlock();
-      }
-    } else if (mTiledBuffer.HasDoubleBufferedTiles()) {
-      mTiledBuffer.ReadUnlock();
-    }
-
-    if (mPendingLowPrecisionUpload) {
-      mLowPrecisionTiledBuffer.ReadUnlock();
-      if (mOldLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
-        mOldLowPrecisionTiledBuffer.ReadUnlock();
-      }
-    } else if (mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
-      mLowPrecisionTiledBuffer.ReadUnlock();
-    }
-
-    mTiledBuffer = TiledLayerBufferComposite();
-    mLowPrecisionTiledBuffer = TiledLayerBufferComposite();
-    mOldTiledBuffer = TiledLayerBufferComposite();
-    mOldLowPrecisionTiledBuffer = TiledLayerBufferComposite();
+    // Clear the TiledLayerBuffers, which will take care of releasing the
+    // copy-on-write locks.
+    mTiledBuffer.Clear();
+    mLowPrecisionTiledBuffer.Clear();
   }
   CompositableHost::Detach(aLayer,aFlags);
 }
 
 bool
 TiledContentHost::UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
                                       const SurfaceDescriptorTiles& aTiledDescriptor)
 {
   if (aTiledDescriptor.resolution() < 1) {
-    if (mPendingLowPrecisionUpload) {
-      mLowPrecisionTiledBuffer.ReadUnlock();
-    } else {
-      mPendingLowPrecisionUpload = true;
-      // If the old buffer has double-buffered tiles, hang onto it so we can
-      // unlock it after we've composited the new buffer.
-      // We only need to hang onto the locks, but not the textures.
-      // Releasing the textures here can help prevent a memory spike in the
-      // situation that the client starts rendering new content before we get
-      // to composite the new buffer.
-      if (mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
-        mOldLowPrecisionTiledBuffer = mLowPrecisionTiledBuffer;
-        mOldLowPrecisionTiledBuffer.ReleaseTextureHosts();
-      }
-    }
-    mLowPrecisionTiledBuffer =
-      TiledLayerBufferComposite(aAllocator,
-                                aTiledDescriptor,
-                                mLowPrecisionTiledBuffer.GetPaintedRegion(),
-                                mCompositor);
-    if (!mLowPrecisionTiledBuffer.IsValid()) {
-      // Something bad happened. Stop here, return false (kills the child process),
-      // and do as little work as possible on the received data as it appears
-      // to be corrupted.
-      mPendingLowPrecisionUpload = false;
-      mPendingUpload = false;
+    if (!mLowPrecisionTiledBuffer.UseTiles(aTiledDescriptor, mCompositor, aAllocator)) {
       return false;
     }
   } else {
-    if (mPendingUpload) {
-      mTiledBuffer.ReadUnlock();
-    } else {
-      mPendingUpload = true;
-      if (mTiledBuffer.HasDoubleBufferedTiles()) {
-        mOldTiledBuffer = mTiledBuffer;
-        mOldTiledBuffer.ReleaseTextureHosts();
-      }
-    }
-    mTiledBuffer = TiledLayerBufferComposite(aAllocator,
-                                             aTiledDescriptor,
-                                             mTiledBuffer.GetPaintedRegion(),
-                                             mCompositor);
-    if (!mTiledBuffer.IsValid()) {
-      // Something bad happened. Stop here, return false (kills the child process),
-      // and do as little work as possible on the received data as it appears
-      // to be corrupted.
-      mPendingLowPrecisionUpload = false;
-      mPendingUpload = false;
+    if (!mTiledBuffer.UseTiles(aTiledDescriptor, mCompositor, aAllocator)) {
       return false;
     }
   }
   return true;
 }
 
 void
+UseTileTexture(CompositableTextureHostRef& aTexture,
+               CompositableTextureSourceRef& aTextureSource,
+               const IntRect& aUpdateRect,
+               TextureHost* aNewTexture,
+               Compositor* aCompositor)
+{
+  if (aTexture && aTexture->GetFormat() != aNewTexture->GetFormat()) {
+    // Only reuse textures if their format match the new texture's.
+    aTextureSource = nullptr;
+    aTexture = nullptr;
+  }
+  aTexture = aNewTexture;
+  if (aTexture) {
+    if (aCompositor) {
+      aTexture->SetCompositor(aCompositor);
+    }
+
+    if (!aUpdateRect.IsEmpty()) {
+#ifdef MOZ_GFX_OPTIMIZE_MOBILE
+      aTexture->Updated(nullptr);
+#else
+      // We possibly upload the entire texture contents here. This is a purposeful
+      // decision, as sub-image upload can often be slow and/or unreliable, but
+      // we may want to reevaluate this in the future.
+      // For !HasInternalBuffer() textures, this is likely a no-op.
+      nsIntRegion region = aUpdateRect;
+      aTexture->Updated(&region);
+#endif
+    }
+    aTexture->PrepareTextureSource(aTextureSource);
+  }
+}
+
+bool
+GetCopyOnWriteLock(const TileLock& ipcLock, TileHost& aTile, ISurfaceAllocator* aAllocator) {
+  MOZ_ASSERT(aAllocator);
+
+  nsRefPtr<gfxSharedReadLock> sharedLock;
+  if (ipcLock.type() == TileLock::TShmemSection) {
+    sharedLock = gfxShmSharedReadLock::Open(aAllocator, ipcLock.get_ShmemSection());
+  } else {
+    if (!aAllocator->IsSameProcess()) {
+      // Trying to use a memory based lock instead of a shmem based one in
+      // the cross-process case is a bad security violation.
+      NS_ERROR("A client process may be trying to peek at the host's address space!");
+      return false;
+    }
+    sharedLock = reinterpret_cast<gfxMemorySharedReadLock*>(ipcLock.get_uintptr_t());
+    if (sharedLock) {
+      // The corresponding AddRef is in TiledClient::GetTileDescriptor
+      sharedLock.get()->Release();
+    }
+  }
+  aTile.mSharedLock = sharedLock;
+  return true;
+}
+
+bool
+TiledLayerBufferComposite::UseTiles(const SurfaceDescriptorTiles& aTiles,
+                                    Compositor* aCompositor,
+                                    ISurfaceAllocator* aAllocator)
+{
+  if (mResolution != aTiles.resolution()) {
+    Clear();
+  }
+  MOZ_ASSERT(aAllocator);
+  MOZ_ASSERT(aCompositor);
+  if (!aAllocator || !aCompositor) {
+    return false;
+  }
+
+  if (aTiles.resolution() == 0 || IsNaN(aTiles.resolution())) {
+    // There are divisions by mResolution so this protects the compositor process
+    // against malicious content processes and fuzzing.
+    return false;
+  }
+
+  int newFirstTileX = aTiles.firstTileX();
+  int newFirstTileY = aTiles.firstTileY();
+  int oldFirstTileX = mFirstTileX;
+  int oldFirstTileY = mFirstTileY;
+  int newRetainedWidth = aTiles.retainedWidth();
+  int newRetainedHeight = aTiles.retainedHeight();
+  int oldRetainedWidth = mRetainedWidth;
+  int oldRetainedHeight = mRetainedHeight;
+
+  const InfallibleTArray<TileDescriptor>& tileDescriptors = aTiles.tiles();
+
+  nsTArray<TileHost> oldTiles;
+  mRetainedTiles.SwapElements(oldTiles);
+  mRetainedTiles.SetLength(tileDescriptors.Length());
+
+  // Step 1, we need to unlock tiles that don't have an internal buffer after the
+  // next frame where they are replaced.
+  // Since we are about to replace the tiles' textures, we need to keep their locks
+  // somewhere (in mPreviousSharedLock) until we composite the layer.
+  for (size_t i = 0; i < oldTiles.Length(); ++i) {
+    TileHost& tile = oldTiles[i];
+    // It can happen that we still have a previous lock at this point,
+    // if we changed a tile's front buffer (causing mSharedLock to
+    // go into mPreviousSharedLock, and then did not composite that tile until
+    // the next transaction, either because the tile is offscreen or because the
+    // two transactions happened with no composition in between (over-production).
+    tile.ReadUnlockPrevious();
+
+    if (tile.mTextureHost && !tile.mTextureHost->HasInternalBuffer()) {
+      MOZ_ASSERT(tile.mSharedLock);
+      int tileX = i % oldRetainedWidth + oldFirstTileX;
+      int tileY = i / oldRetainedWidth + oldFirstTileY;
+
+      if (tileX >= newFirstTileX && tileY >= newFirstTileY &&
+          tileX < (newFirstTileX + newRetainedWidth) &&
+          tileY < (newFirstTileY + newRetainedHeight)) {
+        // This tile still exist in the new buffer
+        tile.mPreviousSharedLock = tile.mSharedLock;
+        tile.mSharedLock = nullptr;
+      } else {
+        // This tile does not exist anymore in the new buffer because the size
+        // changed.
+        tile.ReadUnlock();
+      }
+    }
+
+    // By now we should not have anything in mSharedLock.
+    MOZ_ASSERT(!tile.mSharedLock);
+  }
+
+  // Step 2, move the tiles in mRetainedTiles at places that correspond to where
+  // they should be with the new retained with and height rather than the
+  // old one.
+  for (size_t i = 0; i < tileDescriptors.Length(); i++) {
+    int tileX = i % newRetainedWidth + newFirstTileX;
+    int tileY = i / newRetainedWidth + newFirstTileY;
+
+    // First, get the already existing tiles to the right place in the array,
+    // and use placeholders where there was no tiles.
+    if (tileX < oldFirstTileX || tileY < oldFirstTileY ||
+        tileX >= (oldFirstTileX + oldRetainedWidth) ||
+        tileY >= (oldFirstTileY + oldRetainedHeight)) {
+      mRetainedTiles[i] = GetPlaceholderTile();
+    } else {
+      mRetainedTiles[i] = oldTiles[(tileY - oldFirstTileY) * oldRetainedWidth +
+                                   (tileX - oldFirstTileX)];
+      // If we hit this assertion it means we probably mixed something up in the
+      // logic that tries to reuse tiles on the compositor side. It is most likely
+      // benign, but we are missing some fast paths so let's try to make it not happen.
+      MOZ_ASSERT(tileX == mRetainedTiles[i].x && tileY == mRetainedTiles[i].y);
+    }
+  }
+
+  // It is important to remove the duplicated reference to tiles before calling
+  // TextureHost::PrepareTextureSource, etc. because depending on the textures
+  // ref counts we may or may not get some of the fast paths.
+  oldTiles.Clear();
+
+  // Step 3, handle the texture updates and release the copy-on-write locks.
+  for (size_t i = 0; i < mRetainedTiles.Length(); i++) {
+    const TileDescriptor& tileDesc = tileDescriptors[i];
+
+    TileHost& tile = mRetainedTiles[i];
+
+    switch (tileDesc.type()) {
+      case TileDescriptor::TTexturedTileDescriptor: {
+        const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor();
+
+        const TileLock& ipcLock = texturedDesc.sharedLock();
+        if (!GetCopyOnWriteLock(ipcLock, tile, aAllocator)) {
+          return false;
+        }
+
+        RefPtr<TextureHost> textureHost = TextureHost::AsTextureHost(
+          texturedDesc.textureParent()
+        );
+
+        RefPtr<TextureHost> textureOnWhite = nullptr;
+        if (texturedDesc.textureOnWhite().type() == MaybeTexture::TPTextureParent) {
+          textureOnWhite = TextureHost::AsTextureHost(
+            texturedDesc.textureOnWhite().get_PTextureParent()
+          );
+        }
+
+        UseTileTexture(tile.mTextureHost,
+                       tile.mTextureSource,
+                       texturedDesc.updateRect(),
+                       textureHost,
+                       aCompositor);
+
+        if (textureOnWhite) {
+          UseTileTexture(tile.mTextureHostOnWhite,
+                         tile.mTextureSourceOnWhite,
+                         texturedDesc.updateRect(),
+                         textureOnWhite,
+                         aCompositor);
+        } else {
+          // We could still have component alpha textures from a previous frame.
+          tile.mTextureSourceOnWhite = nullptr;
+          tile.mTextureHostOnWhite = nullptr;
+        }
+
+        if (textureHost->HasInternalBuffer()) {
+          // Now that we did the texture upload (in UseTileTexture), we can release
+          // the lock.
+          tile.ReadUnlock();
+        }
+
+        break;
+      }
+      default:
+        NS_WARNING("Unrecognised tile descriptor type");
+      case TileDescriptor::TPlaceholderTileDescriptor: {
+
+        if (tile.mTextureHost) {
+          tile.mTextureHost->UnbindTextureSource();
+          tile.mTextureSource = nullptr;
+        }
+        if (tile.mTextureHostOnWhite) {
+          tile.mTextureHostOnWhite->UnbindTextureSource();
+          tile.mTextureSourceOnWhite = nullptr;
+        }
+        // we may have a previous lock, and are about to loose our reference to it.
+        // It is okay to unlock it because we just destroyed the texture source.
+        tile.ReadUnlockPrevious();
+        tile = GetPlaceholderTile();
+
+        break;
+      }
+    }
+
+    tile.x = i % newRetainedWidth + newFirstTileX;
+    tile.y = i / newRetainedWidth + newFirstTileY;
+  }
+
+  mFirstTileX = newFirstTileX;
+  mFirstTileY = newFirstTileY;
+  mRetainedWidth = newRetainedWidth;
+  mRetainedHeight = newRetainedHeight;
+  mValidRegion = aTiles.validRegion();
+
+  mResolution = aTiles.resolution();
+  mFrameResolution = CSSToParentLayerScale2D(aTiles.frameXResolution(),
+                                             aTiles.frameYResolution());
+
+  return true;
+}
+
+void
+TiledLayerBufferComposite::Clear()
+{
+  for (TileHost& tile : mRetainedTiles) {
+    tile.ReadUnlock();
+    tile.ReadUnlockPrevious();
+  }
+  mRetainedTiles.Clear();
+  mFirstTileX = 0;
+  mFirstTileY = 0;
+  mRetainedWidth = 0;
+  mRetainedHeight = 0;
+  mValidRegion = nsIntRegion();
+  mPaintedRegion = nsIntRegion();
+  mResolution = 1.0;
+}
+
+void
 TiledContentHost::Composite(EffectChain& aEffectChain,
                             float aOpacity,
                             const gfx::Matrix4x4& aTransform,
                             const gfx::Filter& aFilter,
                             const gfx::Rect& aClipRect,
                             const nsIntRegion* aVisibleRegion /* = nullptr */)
 {
   MOZ_ASSERT(mCompositor);
-  if (mPendingUpload) {
-    mTiledBuffer.SetCompositor(mCompositor);
-    mTiledBuffer.Upload();
-
-    // For a single-buffered tiled buffer, Upload will upload the shared memory
-    // surface to texture memory and we no longer need to read from them.
-    if (!mTiledBuffer.HasDoubleBufferedTiles()) {
-      mTiledBuffer.ReadUnlock();
-    }
-  }
-  if (mPendingLowPrecisionUpload) {
-    mLowPrecisionTiledBuffer.SetCompositor(mCompositor);
-    mLowPrecisionTiledBuffer.Upload();
-
-    if (!mLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
-      mLowPrecisionTiledBuffer.ReadUnlock();
-    }
-  }
-
   // Reduce the opacity of the low-precision buffer to make it a
   // little more subtle and less jarring. In particular, text
   // rendered at low-resolution and scaled tends to look pretty
   // heavy and this helps mitigate that. When we reduce the opacity
   // we also make sure to draw the background color behind the
   // reduced-opacity tile so that content underneath doesn't show
   // through.
   // However, in cases where the background is transparent, or the layer
@@ -435,34 +417,21 @@ TiledContentHost::Composite(EffectChain&
 
   // Render the low and high precision buffers.
   RenderLayerBuffer(mLowPrecisionTiledBuffer,
                     lowPrecisionOpacityReduction < 1.0f ? &backgroundColor : nullptr,
                     aEffectChain, lowPrecisionOpacityReduction * aOpacity,
                     aFilter, aClipRect, *renderRegion, aTransform);
   RenderLayerBuffer(mTiledBuffer, nullptr, aEffectChain, aOpacity, aFilter,
                     aClipRect, *renderRegion, aTransform);
-
-  // Now release the old buffer if it had double-buffered tiles, as we can
-  // guarantee that they're no longer on the screen (and so any locks that may
-  // have been held have been released).
-  if (mPendingUpload && mOldTiledBuffer.HasDoubleBufferedTiles()) {
-    mOldTiledBuffer.ReadUnlock();
-    mOldTiledBuffer = TiledLayerBufferComposite();
-  }
-  if (mPendingLowPrecisionUpload && mOldLowPrecisionTiledBuffer.HasDoubleBufferedTiles()) {
-    mOldLowPrecisionTiledBuffer.ReadUnlock();
-    mOldLowPrecisionTiledBuffer = TiledLayerBufferComposite();
-  }
-  mPendingUpload = mPendingLowPrecisionUpload = false;
 }
 
 
 void
-TiledContentHost::RenderTile(const TileHost& aTile,
+TiledContentHost::RenderTile(TileHost& aTile,
                              const gfxRGBA* aBackgroundColor,
                              EffectChain& aEffectChain,
                              float aOpacity,
                              const gfx::Matrix4x4& aTransform,
                              const gfx::Filter& aFilter,
                              const gfx::Rect& aClipRect,
                              const nsIntRegion& aScreenRegion,
                              const IntPoint& aTextureOffset,
@@ -519,16 +488,17 @@ TiledContentHost::RenderTile(const TileH
     mCompositor->DrawQuad(graphicsRect, aClipRect, aEffectChain, aOpacity, aTransform);
   }
   DiagnosticFlags flags = DiagnosticFlags::CONTENT | DiagnosticFlags::TILE;
   if (aTile.mTextureHostOnWhite) {
     flags |= DiagnosticFlags::COMPONENT_ALPHA;
   }
   mCompositor->DrawDiagnostics(flags,
                                aScreenRegion, aClipRect, aTransform, mFlashCounter);
+  aTile.ReadUnlockPrevious();
 }
 
 void
 TiledContentHost::RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer,
                                     const gfxRGBA* aBackgroundColor,
                                     EffectChain& aEffectChain,
                                     float aOpacity,
                                     const gfx::Filter& aFilter,
@@ -586,20 +556,20 @@ TiledContentHost::RenderLayerBuffer(Tile
     int tileY = 0;
     for (int32_t y = visibleRect.y; y < visibleRect.y + visibleRect.height;) {
       int32_t tileStartY = aLayerBuffer.GetTileStart(y, scaledTileSize.height);
       int32_t h = scaledTileSize.height - tileStartY;
       if (y + h > visibleRect.y + visibleRect.height) {
         h = visibleRect.y + visibleRect.height - y;
       }
 
-      TileHost tileTexture = aLayerBuffer.
-        GetTile(IntPoint(aLayerBuffer.RoundDownToTileEdge(x, scaledTileSize.width),
-                         aLayerBuffer.RoundDownToTileEdge(y, scaledTileSize.height)));
-      if (tileTexture != aLayerBuffer.GetPlaceholderTile()) {
+      nsIntPoint tileOrigin = nsIntPoint(aLayerBuffer.RoundDownToTileEdge(x, scaledTileSize.width),
+                                         aLayerBuffer.RoundDownToTileEdge(y, scaledTileSize.height));
+      TileHost& tileTexture = aLayerBuffer.GetTile(tileOrigin);
+      if (!tileTexture.IsPlaceholderTile()) {
         nsIntRegion tileDrawRegion;
         tileDrawRegion.And(IntRect(x, y, w, h), aLayerBuffer.GetValidRegion());
         tileDrawRegion.And(tileDrawRegion, aVisibleRegion);
         tileDrawRegion.Sub(tileDrawRegion, maskRegion);
 
         if (!tileDrawRegion.IsEmpty()) {
           tileDrawRegion.ScaleRoundOut(resolution, resolution);
           IntPoint tileOffset((x - tileStartX) * resolution,
--- a/gfx/layers/composite/TiledContentHost.h
+++ b/gfx/layers/composite/TiledContentHost.h
@@ -47,138 +47,134 @@ struct EffectChain;
 
 class TileHost {
 public:
   // Constructs a placeholder TileHost. See the comments above
   // TiledLayerBuffer for more information on what this is used for;
   // essentially, this is a sentinel used to represent an invalid or blank
   // tile.
   TileHost()
+  : x(-1)
+  , y(-1)
   {}
 
   // Constructs a TileHost from a gfxSharedReadLock and TextureHost.
   TileHost(gfxSharedReadLock* aSharedLock,
                TextureHost* aTextureHost,
                TextureHost* aTextureHostOnWhite,
                TextureSource* aSource,
                TextureSource* aSourceOnWhite)
     : mSharedLock(aSharedLock)
     , mTextureHost(aTextureHost)
     , mTextureHostOnWhite(aTextureHostOnWhite)
     , mTextureSource(aSource)
     , mTextureSourceOnWhite(aSourceOnWhite)
+    , x(-1)
+    , y(-1)
   {}
 
   TileHost(const TileHost& o) {
     mTextureHost = o.mTextureHost;
     mTextureHostOnWhite = o.mTextureHostOnWhite;
     mTextureSource = o.mTextureSource;
     mTextureSourceOnWhite = o.mTextureSourceOnWhite;
     mSharedLock = o.mSharedLock;
+    mPreviousSharedLock = o.mPreviousSharedLock;
+    x = o.x;
+    y = o.y;
   }
   TileHost& operator=(const TileHost& o) {
     if (this == &o) {
       return *this;
     }
     mTextureHost = o.mTextureHost;
     mTextureHostOnWhite = o.mTextureHostOnWhite;
     mTextureSource = o.mTextureSource;
     mTextureSourceOnWhite = o.mTextureSourceOnWhite;
     mSharedLock = o.mSharedLock;
+    mPreviousSharedLock = o.mPreviousSharedLock;
+    x = o.x;
+    y = o.y;
     return *this;
   }
 
   bool operator== (const TileHost& o) const {
     return mTextureHost == o.mTextureHost;
   }
   bool operator!= (const TileHost& o) const {
     return mTextureHost != o.mTextureHost;
   }
 
   bool IsPlaceholderTile() const { return mTextureHost == nullptr; }
 
   void ReadUnlock() {
     if (mSharedLock) {
       mSharedLock->ReadUnlock();
+      mSharedLock = nullptr;
+    }
+  }
+
+  void ReadUnlockPrevious() {
+    if (mPreviousSharedLock) {
+      mPreviousSharedLock->ReadUnlock();
+      mPreviousSharedLock = nullptr;
     }
   }
 
   void Dump(std::stringstream& aStream) {
     aStream << "TileHost(...)"; // fill in as needed
   }
 
   void DumpTexture(std::stringstream& aStream) {
     // TODO We should combine the OnWhite/OnBlack here an just output a single image.
     CompositableHost::DumpTextureHost(aStream, mTextureHost);
   }
 
   RefPtr<gfxSharedReadLock> mSharedLock;
+  RefPtr<gfxSharedReadLock> mPreviousSharedLock;
   CompositableTextureHostRef mTextureHost;
   CompositableTextureHostRef mTextureHostOnWhite;
   mutable CompositableTextureSourceRef mTextureSource;
   mutable CompositableTextureSourceRef mTextureSourceOnWhite;
+  // This is not strictly necessary but makes debugging whole lot easier.
+  int x;
+  int y;
 };
 
 class TiledLayerBufferComposite
   : public TiledLayerBuffer<TiledLayerBufferComposite, TileHost>
 {
   friend class TiledLayerBuffer<TiledLayerBufferComposite, TileHost>;
 
 public:
-  typedef TiledLayerBuffer<TiledLayerBufferComposite, TileHost>::Iterator Iterator;
+  TiledLayerBufferComposite();
+  ~TiledLayerBufferComposite();
 
-  TiledLayerBufferComposite();
-  TiledLayerBufferComposite(ISurfaceAllocator* aAllocator,
-                            const SurfaceDescriptorTiles& aDescriptor,
-                            const nsIntRegion& aOldPaintedRegion,
-                            Compositor* aCompositor);
+  bool UseTiles(const SurfaceDescriptorTiles& aTileDescriptors,
+                Compositor* aCompositor,
+                ISurfaceAllocator* aAllocator);
+
+  void Clear();
 
   TileHost GetPlaceholderTile() const { return TileHost(); }
 
   // Stores the absolute resolution of the containing frame, calculated
   // by the sum of the resolutions of all parent layers' FrameMetrics.
   const CSSToParentLayerScale2D& GetFrameResolution() { return mFrameResolution; }
 
-  void ReadUnlock();
-
-  void ReleaseTextureHosts();
-
-  /**
-   * This will synchronously upload any necessary texture contents, making the
-   * sources immediately available for compositing. For texture hosts that
-   * don't have an internal buffer, this is unlikely to actually do anything.
-   */
-  void Upload();
-
   void SetCompositor(Compositor* aCompositor);
 
-  bool HasDoubleBufferedTiles() { return mHasDoubleBufferedTiles; }
-
-  bool IsValid() const { return mIsValid; }
-
   // Recycle callback for TextureHost.
   // Used when TiledContentClient is present in client side.
   static void RecycleCallback(TextureHost* textureHost, void* aClosure);
 
 protected:
-  TileHost ValidateTile(TileHost aTile,
-                        const gfx::IntPoint& aTileRect,
-                        const nsIntRegion& dirtyRect);
-
-  // do nothing, the desctructor in the texture host takes care of releasing resources
-  void ReleaseTile(TileHost aTile) {}
-
   void SwapTiles(TileHost& aTileA, TileHost& aTileB) { std::swap(aTileA, aTileB); }
 
-  void UnlockTile(TileHost aTile) {}
-  void PostValidate(const nsIntRegion& aPaintRegion) {}
-private:
   CSSToParentLayerScale2D mFrameResolution;
-  bool mHasDoubleBufferedTiles;
-  bool mIsValid;
 };
 
 /**
  * ContentHost for tiled PaintedLayers. Since tiled layers are special snow
  * flakes, we have a unique update process. All the textures that back the
  * tiles are added in the usual way, but Updated is called on the host side
  * in response to a message that describes the transaction for every tile.
  * Composition happens in the normal way.
@@ -228,21 +224,20 @@ public:
 
   const nsIntRegion& GetValidRegion() const override
   {
     return mTiledBuffer.GetValidRegion();
   }
 
   virtual void SetCompositor(Compositor* aCompositor) override
   {
+    MOZ_ASSERT(aCompositor);
     CompositableHost::SetCompositor(aCompositor);
     mTiledBuffer.SetCompositor(aCompositor);
     mLowPrecisionTiledBuffer.SetCompositor(aCompositor);
-    mOldTiledBuffer.SetCompositor(aCompositor);
-    mOldLowPrecisionTiledBuffer.SetCompositor(aCompositor);
   }
 
   virtual bool UseTiledLayerBuffer(ISurfaceAllocator* aAllocator,
                                    const SurfaceDescriptorTiles& aTiledDescriptor) override;
 
   void Composite(EffectChain& aEffectChain,
                  float aOpacity,
                  const gfx::Matrix4x4& aTransform,
@@ -274,33 +269,29 @@ private:
                          EffectChain& aEffectChain,
                          float aOpacity,
                          const gfx::Filter& aFilter,
                          const gfx::Rect& aClipRect,
                          nsIntRegion aMaskRegion,
                          gfx::Matrix4x4 aTransform);
 
   // Renders a single given tile.
-  void RenderTile(const TileHost& aTile,
+  void RenderTile(TileHost& aTile,
                   const gfxRGBA* aBackgroundColor,
                   EffectChain& aEffectChain,
                   float aOpacity,
                   const gfx::Matrix4x4& aTransform,
                   const gfx::Filter& aFilter,
                   const gfx::Rect& aClipRect,
                   const nsIntRegion& aScreenRegion,
                   const gfx::IntPoint& aTextureOffset,
                   const gfx::IntSize& aTextureBounds);
 
   void EnsureTileStore() {}
 
   TiledLayerBufferComposite    mTiledBuffer;
   TiledLayerBufferComposite    mLowPrecisionTiledBuffer;
-  TiledLayerBufferComposite    mOldTiledBuffer;
-  TiledLayerBufferComposite    mOldLowPrecisionTiledBuffer;
-  bool                         mPendingUpload;
-  bool                         mPendingLowPrecisionUpload;
 };
 
 }
 }
 
 #endif
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -310,31 +310,34 @@ union TileLock {
 union MaybeTexture {
   PTexture;
   null_t;
 };
 
 struct TexturedTileDescriptor {
   PTexture texture;
   MaybeTexture textureOnWhite;
+  IntRect updateRect;
   TileLock sharedLock;
 };
 
 struct PlaceholderTileDescriptor {
 };
 
 union TileDescriptor {
   TexturedTileDescriptor;
   PlaceholderTileDescriptor;
 };
 
 struct SurfaceDescriptorTiles {
   nsIntRegion validRegion;
   nsIntRegion paintedRegion;
   TileDescriptor[] tiles;
+  int         firstTileX;
+  int         firstTileY;
   int         retainedWidth;
   int         retainedHeight;
   float       resolution;
   float       frameXResolution;
   float       frameYResolution;
 };
 
 struct OpUseTiledLayerBuffer {
--- a/gfx/qcms/transform.c
+++ b/gfx/qcms/transform.c
@@ -1215,17 +1215,17 @@ qcms_transform* qcms_transform_create(
 
         qcms_transform *transform = transform_alloc();
         if (!transform) {
 		return NULL;
 	}
 	if (out_type != QCMS_DATA_RGB_8 &&
                 out_type != QCMS_DATA_RGBA_8) {
             assert(0 && "output type");
-	    transform_free(transform);
+	    qcms_transform_release(transform);
             return NULL;
         }
 
 	if (out->output_table_r &&
 			out->output_table_g &&
 			out->output_table_b) {
 		precache = true;
 	}
@@ -1238,17 +1238,17 @@ qcms_transform* qcms_transform_create(
 		// Precache the transformation to a CLUT 33x33x33 in size.
 		// 33 is used by many profiles and works well in pratice. 
 		// This evenly divides 256 into blocks of 8x8x8.
 		// TODO For transforming small data sets of about 200x200 or less
 		// precaching should be avoided.
 		qcms_transform *result = qcms_transform_precacheLUT_float(transform, in, out, 33, in_type);
 		if (!result) {
             		assert(0 && "precacheLUT failed");
-			transform_free(transform);
+			qcms_transform_release(transform);
 			return NULL;
 		}
 		return result;
 	}
 
 	if (precache) {
 		transform->output_table_r = precache_reference(out->output_table_r);
 		transform->output_table_g = precache_reference(out->output_table_g);
@@ -1268,17 +1268,17 @@ qcms_transform* qcms_transform_create(
 	}
 
         if (in->color_space == RGB_SIGNATURE) {
 		struct matrix in_matrix, out_matrix, result;
 
 		if (in_type != QCMS_DATA_RGB_8 &&
                     in_type != QCMS_DATA_RGBA_8){
                 	assert(0 && "input type");
-			transform_free(transform);
+			qcms_transform_release(transform);
                 	return NULL;
             	}
 		if (precache) {
 #ifdef X86
 		    if (sse_version_available() >= 2) {
 			    if (in_type == QCMS_DATA_RGB_8)
 				    transform->transform_fn = qcms_transform_data_rgb_out_lut_sse2;
 			    else
@@ -1332,33 +1332,43 @@ qcms_transform* qcms_transform_create(
 		out_matrix = build_colorant_matrix(out);
 		out_matrix = matrix_invert(out_matrix);
 		if (out_matrix.invalid) {
 			qcms_transform_release(transform);
 			return NULL;
 		}
 		result = matrix_multiply(out_matrix, in_matrix);
 
+		/* check for NaN values in the matrix and bail if we find any */
+		for (unsigned i = 0 ; i < 3 ; ++i) {
+			for (unsigned j = 0 ; j < 3 ; ++j) {
+				if (result.m[i][j] != result.m[i][j]) {
+					qcms_transform_release(transform);
+					return NULL;
+				}
+			}
+		}
+
 		/* store the results in column major mode
 		 * this makes doing the multiplication with sse easier */
 		transform->matrix[0][0] = result.m[0][0];
 		transform->matrix[1][0] = result.m[0][1];
 		transform->matrix[2][0] = result.m[0][2];
 		transform->matrix[0][1] = result.m[1][0];
 		transform->matrix[1][1] = result.m[1][1];
 		transform->matrix[2][1] = result.m[1][2];
 		transform->matrix[0][2] = result.m[2][0];
 		transform->matrix[1][2] = result.m[2][1];
 		transform->matrix[2][2] = result.m[2][2];
 
 	} else if (in->color_space == GRAY_SIGNATURE) {
 		if (in_type != QCMS_DATA_GRAY_8 &&
 				in_type != QCMS_DATA_GRAYA_8){
 			assert(0 && "input type");
-			transform_free(transform);
+			qcms_transform_release(transform);
 			return NULL;
 		}
 
 		transform->input_gamma_table_gray = build_input_gamma_table(in->grayTRC);
 		if (!transform->input_gamma_table_gray) {
 			qcms_transform_release(transform);
 			return NO_MEM_TRANSFORM;
 		}
@@ -1373,17 +1383,17 @@ qcms_transform* qcms_transform_create(
 			if (in_type == QCMS_DATA_GRAY_8) {
 				transform->transform_fn = qcms_transform_data_gray_out_lut;
 			} else {
 				transform->transform_fn = qcms_transform_data_graya_out_lut;
 			}
 		}
 	} else {
 		assert(0 && "unexpected colorspace");
-		transform_free(transform);
+		qcms_transform_release(transform);
 		return NULL;
 	}
 	return transform;
 }
 
 #if defined(__GNUC__) && !defined(__x86_64__) && !defined(__amd64__)
 /* we need this to avoid crashes when gcc assumes the stack is 128bit aligned */
 __attribute__((__force_align_arg_pointer__))
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -817,40 +817,30 @@ gfxFontconfigFontEntry::CopyFontTable(ui
 void
 gfxFontconfigFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
 {
     if (mHasStyles) {
         return;
     }
 
     // add font entries for each of the faces
-    gfxPlatformFontList *fp = gfxPlatformFontList::PlatformFontList();
     uint32_t numFonts = mFontPatterns.Length();
     NS_ASSERTION(numFonts, "font family containing no faces!!");
     for (uint32_t i = 0; i < numFonts; i++) {
         FcPattern* face = mFontPatterns[i];
 
         // figure out the psname/fullname and choose which to use as the facename
         nsAutoString psname, fullname;
         GetFaceNames(face, mName, psname, fullname);
         const nsAutoString& faceName = !psname.IsEmpty() ? psname : fullname;
 
         gfxFontconfigFontEntry *fontEntry =
             new gfxFontconfigFontEntry(faceName, face);
         AddFontEntry(fontEntry);
 
-        // add entry to local name lists
-        if (!psname.IsEmpty()) {
-            fp->AddPostscriptName(fontEntry, psname);
-        }
-        NS_ASSERTION(!fullname.IsEmpty(), "empty font fullname");
-        if (!fullname.IsEmpty()) {
-            fp->AddFullname(fontEntry, fullname);
-        }
-
         if (LOG_FONTLIST_ENABLED()) {
             LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
                  " with style: %s weight: %d stretch: %d"
                  " psname: %s fullname: %s",
                  NS_ConvertUTF16toUTF8(fontEntry->Name()).get(),
                  NS_ConvertUTF16toUTF8(Name()).get(),
                  fontEntry->IsItalic() ? "italic" : "normal",
                  fontEntry->Weight(), fontEntry->Stretch(),
@@ -1020,21 +1010,21 @@ gfxFcPlatformFontList::AddFontSetFamilie
             static_cast<gfxFontconfigFontFamily*>(fontFamily);
         fcFamily->AddFontPattern(font);
 
         // map the psname, fullname ==> font family for local font lookups
         nsAutoString psname, fullname;
         GetFaceNames(font, familyName, psname, fullname);
         if (!psname.IsEmpty()) {
             ToLowerCase(psname);
-            mLocalNames.Put(psname, fontFamily);
+            mLocalNames.Put(psname, font);
         }
         if (!fullname.IsEmpty()) {
             ToLowerCase(fullname);
-            mLocalNames.Put(fullname, fontFamily);
+            mLocalNames.Put(fullname, font);
         }
     }
 }
 
 nsresult
 gfxFcPlatformFontList::InitFontList()
 {
     mLastConfig = FcConfigGetCurrent();
@@ -1159,47 +1149,27 @@ gfxFcPlatformFontList::GetDefaultFont(co
 }
 
 gfxFontEntry*
 gfxFcPlatformFontList::LookupLocalFont(const nsAString& aFontName,
                                        uint16_t aWeight,
                                        int16_t aStretch,
                                        bool aItalic)
 {
-    gfxFontEntry* lookup;
-
-    // first, lookup in face name lists
-    lookup = FindFaceName(aFontName);
-    if (!lookup) {
-        // if not found, check in global facename ==> family list
-        nsAutoString keyName(aFontName);
-        ToLowerCase(keyName);
-        gfxFontFamily* fontFamily = mLocalNames.GetWeak(keyName);
+    nsAutoString keyName(aFontName);
+    ToLowerCase(keyName);
 
-        // name is not in the global list, done
-        if (!fontFamily) {
-            return nullptr;
-        }
-
-        // name is in global list but family needs enumeration
-        fontFamily->FindStyleVariations();
-
-        // facename ==> font entry should now be in the list
-        lookup = FindFaceName(aFontName);
-        NS_ASSERTION(lookup, "facename to family mapping failure");
-        if (!lookup) {
-            return nullptr;
-        }
+    // if name is not in the global list, done
+    FcPattern* fontPattern = mLocalNames.Get(keyName);
+    if (!fontPattern) {
+        return nullptr;
     }
 
-    gfxFontconfigFontEntry* fcFontEntry =
-        static_cast<gfxFontconfigFontEntry*>(lookup);
-
-    return new gfxFontconfigFontEntry(fcFontEntry->Name(),
-                                      fcFontEntry->GetPattern(),
+    return new gfxFontconfigFontEntry(aFontName,
+                                      fontPattern,
                                       aWeight, aStretch, aItalic);
 }
 
 gfxFontEntry*
 gfxFcPlatformFontList::MakePlatformFont(const nsAString& aFontName,
                                         uint16_t aWeight,
                                         int16_t aStretch,
                                         bool aItalic,
@@ -1379,16 +1349,19 @@ gfxFcPlatformFontList::FindGenericFamily
         return genericFamily;
     }
 
     // if not found, ask fontconfig to pick the appropriate font
     nsAutoRef<FcPattern> genericPattern(FcPatternCreate());
     FcPatternAddString(genericPattern, FC_FAMILY,
                        ToFcChar8Ptr(generic.get()));
 
+    // -- prefer scalable fonts
+    FcPatternAddBool(genericPattern, FC_SCALABLE, FcTrue);
+
     // -- add the lang to the pattern
     if (!fcLang.IsEmpty()) {
         FcPatternAddString(genericPattern, FC_LANG,
                            ToFcChar8Ptr(fcLang.get()));
     }
 
     // -- perform substitutions
     FcConfigSubstitute(nullptr, genericPattern, FcMatchPattern);
--- a/gfx/thebes/gfxFcPlatformFontList.h
+++ b/gfx/thebes/gfxFcPlatformFontList.h
@@ -244,17 +244,19 @@ protected:
 #ifdef MOZ_BUNDLED_FONTS
     void ActivateBundledFonts();
     nsCString mBundledFontsPath;
     bool mBundledFontsInitialized;
 #endif
 
     // to avoid enumerating all fonts, maintain a mapping of local font
     // names to family
-    nsRefPtrHashtable<nsStringHashKey, gfxFontFamily> mLocalNames;
+    nsBaseHashtable<nsStringHashKey,
+                    nsCountedRef<FcPattern>,
+                    FcPattern*> mLocalNames;
 
     // caching generic/lang ==> font family
     nsRefPtrHashtable<nsCStringHashKey, gfxFontFamily> mGenericMappings;
 
     nsCOMPtr<nsITimer> mCheckFontUpdatesTimer;
     nsCountedRef<FcConfig> mLastConfig;
 
     static FT_Library sCairoFTLibrary;
--- a/gfx/thebes/gfxFontUtils.cpp
+++ b/gfx/thebes/gfxFontUtils.cpp
@@ -10,16 +10,17 @@
 #include "gfxColor.h"
 
 #include "nsServiceManagerUtils.h"
 
 #include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/BinarySearch.h"
+#include "mozilla/Snprintf.h"
 
 #include "nsCOMPtr.h"
 #include "nsIUUIDGenerator.h"
 #include "nsIUnicodeDecoder.h"
 
 #include "harfbuzz/hb.h"
 
 #include "plbase64.h"
@@ -63,31 +64,32 @@ void
 gfxSparseBitSet::Dump(const char* aPrefix, eGfxLog aWhichLog) const
 {
     NS_ASSERTION(mBlocks.DebugGetHeader(), "mHdr is null, this is bad");
     uint32_t b, numBlocks = mBlocks.Length();
 
     for (b = 0; b < numBlocks; b++) {
         Block *block = mBlocks[b];
         if (!block) continue;
-        char outStr[256];
+        const int BUFSIZE = 256;
+        char outStr[BUFSIZE];
         int index = 0;
-        index += sprintf(&outStr[index], "%s u+%6.6x [", aPrefix, (b << BLOCK_INDEX_SHIFT));
+        index += snprintf(&outStr[index], BUFSIZE - index, "%s u+%6.6x [", aPrefix, (b << BLOCK_INDEX_SHIFT));
         for (int i = 0; i < 32; i += 4) {
             for (int j = i; j < i + 4; j++) {
                 uint8_t bits = block->mBits[j];
                 uint8_t flip1 = ((bits & 0xaa) >> 1) | ((bits & 0x55) << 1);
                 uint8_t flip2 = ((flip1 & 0xcc) >> 2) | ((flip1 & 0x33) << 2);
                 uint8_t flipped = ((flip2 & 0xf0) >> 4) | ((flip2 & 0x0f) << 4);
 
-                index += sprintf(&outStr[index], "%2.2x", flipped);
+                index += snprintf(&outStr[index], BUFSIZE - index, "%2.2x", flipped);
             }
-            if (i + 4 != 32) index += sprintf(&outStr[index], " ");
+            if (i + 4 != 32) index += snprintf(&outStr[index], BUFSIZE - index, " ");
         }
-        index += sprintf(&outStr[index], "]");
+        index += snprintf(&outStr[index], BUFSIZE - index, "]");
         LOG(aWhichLog, ("%s", outStr));
     }
 }
 
 nsresult
 gfxFontUtils::ReadCMAPTableFormat10(const uint8_t *aBuf, uint32_t aLength,
                                     gfxSparseBitSet& aCharacterMap)
 {
@@ -1405,18 +1407,18 @@ gfxFontUtils::DecodeFontName(const char 
     const char *csName = GetCharsetForFontName(aPlatformCode, aScriptCode, aLangCode);
 
     if (!csName) {
         // nullptr -> unknown charset
 #ifdef DEBUG
         char warnBuf[128];
         if (aByteLen > 64)
             aByteLen = 64;
-        sprintf(warnBuf, "skipping font name, unknown charset %d:%d:%d for <%.*s>",
-                aPlatformCode, aScriptCode, aLangCode, aByteLen, aNameData);
+        snprintf_literal(warnBuf, "skipping font name, unknown charset %d:%d:%d for <%.*s>",
+                         aPlatformCode, aScriptCode, aLangCode, aByteLen, aNameData);
         NS_WARNING(warnBuf);
 #endif
         return false;
     }
 
     if (csName[0] == 0) {
         // empty charset name: data is utf16be, no need to instantiate a converter
         uint32_t strLen = aByteLen / 2;
--- a/gfx/thebes/gfxHarfBuzzShaper.cpp
+++ b/gfx/thebes/gfxHarfBuzzShaper.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsString.h"
 #include "gfxContext.h"
 #include "gfxFontConstants.h"
 #include "gfxHarfBuzzShaper.h"
 #include "gfxFontUtils.h"
 #include "gfxTextRun.h"
+#include "mozilla/Snprintf.h"
 #include "nsUnicodeProperties.h"
 #include "nsUnicodeScriptCodes.h"
 #include "nsUnicodeNormalizer.h"
 
 #include "harfbuzz/hb.h"
 #include "harfbuzz/hb-ot.h"
 
 #if ENABLE_INTL_API // ICU is available: we'll use it for Unicode composition
@@ -896,20 +897,20 @@ gfxHarfBuzzShaper::GetHKerning(uint16_t 
                                  (coverage & KERN0_COVERAGE_MINIMUM) != 0);
                 break;
             default:
                 // TODO: implement support for other formats,
                 // if they're ever used in practice
 #if DEBUG
                 {
                     char buf[1024];
-                    sprintf(buf, "unknown kern subtable in %s: "
-                                 "ver 0 format %d\n",
-                            NS_ConvertUTF16toUTF8(mFont->GetName()).get(),
-                            format);
+                    snprintf_literal(buf, "unknown kern subtable in %s: "
+                                          "ver 0 format %d\n",
+                                     NS_ConvertUTF16toUTF8(mFont->GetName()).get(),
+                                     format);
                     NS_WARNING(buf);
                 }
 #endif
                 break;
             }
         }
     } else {
         // It wasn't a "version 0" table; check if it is Apple version 1.0
@@ -958,20 +959,20 @@ gfxHarfBuzzShaper::GetHKerning(uint16_t 
                 default:
                     // TODO: implement support for other formats.
                     // Note that format 1 cannot be supported here,
                     // as it requires the full glyph array to run the FSM,
                     // not just the current glyph pair.
 #if DEBUG
                     {
                         char buf[1024];
-                        sprintf(buf, "unknown kern subtable in %s: "
-                                     "ver 0 format %d\n",
-                                NS_ConvertUTF16toUTF8(mFont->GetName()).get(),
-                                format);
+                        snprintf_literal(buf, "unknown kern subtable in %s: "
+                                              "ver 0 format %d\n",
+                                         NS_ConvertUTF16toUTF8(mFont->GetName()).get(),
+                                         format);
                         NS_WARNING(buf);
                     }
 #endif
                     break;
                 }
             }
         }
     }
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2243,16 +2243,17 @@ gfxPlatform::OptimalFormatForContent(gfx
  * As such, we will evaluate them all as soon as one of them is evaluated
  * and remember the values.  Changing these preferences during the run will
  * not have any effect until we restart.
  */
 static bool sLayersSupportsD3D9 = false;
 static bool sLayersSupportsD3D11 = false;
 static bool sANGLESupportsD3D11 = false;
 static bool sLayersSupportsHardwareVideoDecoding = false;
+static bool sLayersHardwareVideoDecodingFailed = false;
 static bool sBufferRotationCheckPref = true;
 static bool sPrefBrowserTabsRemoteAutostart = false;
 
 static bool sLayersAccelerationPrefsInitialized = false;
 
 void
 InitLayersAccelerationPrefs()
 {
@@ -2302,16 +2303,20 @@ InitLayersAccelerationPrefs()
 #endif
         NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING,
                                                &status))) {
       if (status == nsIGfxInfo::FEATURE_STATUS_OK) {
         sLayersSupportsHardwareVideoDecoding = true;
       }
     }
 
+    Preferences::AddBoolVarCache(&sLayersHardwareVideoDecodingFailed,
+                                 "media.hardware-video-decoding.failed",
+                                 false);
+
     sLayersAccelerationPrefsInitialized = true;
   }
 }
 
 bool
 gfxPlatform::CanUseDirect3D9()
 {
   // this function is called from the compositor thread, so it is not
@@ -2330,17 +2335,17 @@ gfxPlatform::CanUseDirect3D11()
 }
 
 bool
 gfxPlatform::CanUseHardwareVideoDecoding()
 {
   // this function is called from the compositor thread, so it is not
   // safe to init the prefs etc. from here.
   MOZ_ASSERT(sLayersAccelerationPrefsInitialized);
-  return sLayersSupportsHardwareVideoDecoding;
+  return sLayersSupportsHardwareVideoDecoding && !sLayersHardwareVideoDecodingFailed;
 }
 
 bool
 gfxPlatform::CanUseDirect3D11ANGLE()
 {
   MOZ_ASSERT(sLayersAccelerationPrefsInitialized);
   return sANGLESupportsD3D11;
 }
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gfxTextRun.h"
 #include "gfxGlyphExtents.h"
 #include "gfxPlatformFontList.h"
 #include "gfxUserFontSet.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/PathHelpers.h"
+#include "mozilla/Snprintf.h"
 #include "nsGkAtoms.h"
 #include "nsILanguageAtomService.h"
 #include "nsServiceManagerUtils.h"
 
 #include "gfxContext.h"
 #include "gfxFontConstants.h"
 #include "gfxFontMissingGlyphs.h"
 #include "gfxScriptItemizer.h"
@@ -1897,18 +1898,18 @@ gfxFontGroup::GetDefaultFont()
     }
 
     if (!mDefaultFont) {
         // an empty font list at this point is fatal; we're not going to
         // be able to do even the most basic layout operations
         char msg[256]; // CHECK buffer length if revising message below
         nsAutoString families;
         mFamilyList.ToString(families);
-        sprintf(msg, "unable to find a usable font (%.220s)",
-                NS_ConvertUTF16toUTF8(families).get());
+        snprintf_literal(msg, "unable to find a usable font (%.220s)",
+                         NS_ConvertUTF16toUTF8(families).get());
         NS_RUNTIMEABORT(msg);
     }
 
     return mDefaultFont.get();
 }
 
 
 gfxFont*
--- a/intl/locale/nsScriptableDateFormat.cpp
+++ b/intl/locale/nsScriptableDateFormat.cpp
@@ -1,13 +1,14 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "mozilla/Snprintf.h"
 #include "nsILocaleService.h"
 #include "nsDateTimeFormatCID.h"
 #include "nsIDateTimeFormat.h"
 #include "nsIScriptableDateFormat.h"
 #include "nsCOMPtr.h"
 #include "nsServiceManagerUtils.h"
 
 static NS_DEFINE_CID(kLocaleServiceCID, NS_LOCALESERVICE_CID);
@@ -108,17 +109,17 @@ NS_IMETHODIMP nsScriptableDateFormat::Fo
   if ((time_t)-1 != timetTime) {
     rv = dateTimeFormat->FormatTime(locale, dateFormatSelector, timeFormatSelector, 
                                      timetTime, mStringOut);
   }
   else {
     // if mktime fails (e.g. year <= 1970), then try NSPR.
     PRTime prtime;
     char string[32];
-    sprintf(string, "%.2d/%.2d/%d %.2d:%.2d:%.2d", month, day, year, hour, minute, second);
+    snprintf_literal(string, "%.2d/%.2d/%d %.2d:%.2d:%.2d", month, day, year, hour, minute, second);
     if (PR_SUCCESS != PR_ParseTimeString(string, false, &prtime))
       return NS_ERROR_INVALID_ARG;
 
     rv = dateTimeFormat->FormatPRTime(locale, dateFormatSelector, timeFormatSelector, 
                                       prtime, mStringOut);
   }
   if (NS_SUCCEEDED(rv))
     *dateTimeString = ToNewUnicode(mStringOut);
--- a/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -7,16 +7,17 @@
 #ifndef __IPC_GLUE_IPCMESSAGEUTILS_H__
 #define __IPC_GLUE_IPCMESSAGEUTILS_H__
 
 #include "base/process_util.h"
 #include "chrome/common/ipc_message_utils.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/DebugOnly.h"
+#include "mozilla/Maybe.h"
 #include "mozilla/TimeStamp.h"
 #ifdef XP_WIN
 #include "mozilla/TimeStamp_windows.h"
 #endif
 #include "mozilla/TypeTraits.h"
 #include "mozilla/IntegerTypeTraits.h"
 
 #include <stdint.h>
@@ -800,11 +801,45 @@ struct ParamTraits<mozilla::OwningSerial
 
 template <>
 struct ParamTraits<nsIWidget::TouchPointerState>
   : public BitFlagsEnumSerializer<nsIWidget::TouchPointerState,
                                   nsIWidget::TouchPointerState::ALL_BITS>
 {
 };
 
+template<class T>
+struct ParamTraits< mozilla::Maybe<T> >
+{
+  typedef mozilla::Maybe<T> paramType;
+
+  static void Write(Message* msg, const paramType& param)
+  {
+    if (param.isSome()) {
+      WriteParam(msg, true);
+      WriteParam(msg, param.value());
+    } else {
+      WriteParam(msg, false);
+    }
+  }
+
+  static bool Read(const Message* msg, void** iter, paramType* result)
+  {
+    bool isSome;
+    if (!ReadParam(msg, iter, &isSome)) {
+      return false;
+    }
+    if (isSome) {
+      T tmp;
+      if (!ReadParam(msg, iter, &tmp)) {
+        return false;
+      }
+      *result = mozilla::Some(mozilla::Move(tmp));
+    } else {
+      *result = mozilla::Nothing();
+    }
+    return true;
+  }
+};
+
 } /* namespace IPC */
 
 #endif /* __IPC_GLUE_IPCMESSAGEUTILS_H__ */
--- a/ipc/netd/Netd.cpp
+++ b/ipc/netd/Netd.cpp
@@ -12,16 +12,17 @@
 #include "android/log.h"
 
 #include "nsWhitespaceTokenizer.h"
 #include "nsXULAppAPI.h"
 #include "nsAutoPtr.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "mozilla/RefPtr.h"
+#include "mozilla/Snprintf.h"
 
 #define NETD_LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Gonk", args)
 #define ICS_SYS_USB_RNDIS_MAC "/sys/class/android_usb/android0/f_rndis/ethaddr"
 #define INVALID_SOCKET -1
 #define MAX_RECONNECT_TIMES 10
 
 namespace {
 
@@ -62,19 +63,19 @@ InitRndisAddress()
   memset(address, 0, sizeof(address));
   // First byte is 0x02 to signify a locally administered address.
   address[0] = 0x02;
   length = strlen(serialno);
   for (i = 0; i < length; i++) {
     address[i % (kEthernetAddressLength - 1) + 1] ^= serialno[i];
   }
 
-  sprintf(mac, "%02x:%02x:%02x:%02x:%02x:%02x",
-          address[0], address[1], address[2],
-          address[3], address[4], address[5]);
+  snprintf_literal(mac, "%02x:%02x:%02x:%02x:%02x:%02x",
+                   address[0], address[1], address[2],
+                   address[3], address[4], address[5]);
   length = strlen(mac);
   ret = write(fd.get(), mac, length);
   if (ret != length) {
     NETD_LOG("Fail to write file %s.", ICS_SYS_USB_RNDIS_MAC);
     return false;
   }
   return true;
 }
--- a/js/public/StructuredClone.h
+++ b/js/public/StructuredClone.h
@@ -157,27 +157,35 @@ JS_PUBLIC_API(bool)
 JS_StructuredClone(JSContext* cx, JS::HandleValue v, JS::MutableHandleValue vp,
                    const JSStructuredCloneCallbacks* optionalCallbacks, void* closure);
 
 // RAII sugar for JS_WriteStructuredClone.
 class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) {
     uint64_t* data_;
     size_t nbytes_;
     uint32_t version_;
+    enum {
+        OwnsTransferablesIfAny,
+        IgnoreTransferablesIfAny,
+        NoTransferables
+    } ownTransferables_;
+
     const JSStructuredCloneCallbacks* callbacks_;
     void* closure_;
 
   public:
     JSAutoStructuredCloneBuffer()
         : data_(nullptr), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION),
+          ownTransferables_(NoTransferables),
           callbacks_(nullptr), closure_(nullptr)
     {}
 
     JSAutoStructuredCloneBuffer(const JSStructuredCloneCallbacks* callbacks, void* closure)
         : data_(nullptr), nbytes_(0), version_(JS_STRUCTURED_CLONE_VERSION),
+          ownTransferables_(NoTransferables),
           callbacks_(callbacks), closure_(closure)
     {}
 
     JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other);
     JSAutoStructuredCloneBuffer& operator=(JSAutoStructuredCloneBuffer&& other);
 
     ~JSAutoStructuredCloneBuffer() { clear(); }
 
@@ -189,21 +197,27 @@ class JS_PUBLIC_API(JSAutoStructuredClon
     // Copy some memory. It will be automatically freed by the destructor.
     bool copy(const uint64_t* data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION);
 
     // Adopt some memory. It will be automatically freed by the destructor.
     // data must have been allocated by the JS engine (e.g., extracted via
     // JSAutoStructuredCloneBuffer::steal).
     void adopt(uint64_t* data, size_t nbytes, uint32_t version=JS_STRUCTURED_CLONE_VERSION);
 
-    // Remove the buffer so that it will not be automatically freed.
-    // After this, the caller is responsible for feeding the memory back to
-    // JSAutoStructuredCloneBuffer::adopt.
+    // Release the buffer and transfer ownership to the caller. The caller is
+    // responsible for calling JS_ClearStructuredClone or feeding the memory
+    // back to JSAutoStructuredCloneBuffer::adopt.
     void steal(uint64_t** datap, size_t* nbytesp, uint32_t* versionp=nullptr);
 
+    // Abandon ownership of any transferable objects stored in the buffer,
+    // without freeing the buffer itself. Useful when copying the data out into
+    // an external container, though note that you will need to use adopt() or
+    // JS_ClearStructuredClone to properly release that data eventually.
+    void abandon() { ownTransferables_ = IgnoreTransferablesIfAny; }
+
     bool read(JSContext* cx, JS::MutableHandleValue vp,
               const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr);
 
     bool write(JSContext* cx, JS::HandleValue v,
                const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr);
 
     bool write(JSContext* cx, JS::HandleValue v, JS::HandleValue transferable,
                const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr);
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -2980,16 +2980,17 @@ fi
 
 case "${OS_TARGET}" in
 Android|WINNT|Darwin)
   MOZ_GLUE_IN_PROGRAM=
   ;;
 *)
   dnl On !Android !Windows !OSX, we only want to link executables against mozglue
   MOZ_GLUE_IN_PROGRAM=1
+  AC_DEFINE(MOZ_GLUE_IN_PROGRAM)
   ;;
 esac
 
 if test "$MOZ_MEMORY"; then
   AC_DEFINE(MOZ_MEMORY)
   if test "x$MOZ_DEBUG" = "x1"; then
     AC_DEFINE(MOZ_MEMORY_DEBUG)
   fi
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -330,16 +330,28 @@ namespace FunctionType {
 
 namespace CClosure {
   static void Trace(JSTracer* trc, JSObject* obj);
   static void Finalize(JSFreeOp* fop, JSObject* obj);
 
   // libffi callback
   static void ClosureStub(ffi_cif* cif, void* result, void** args,
     void* userData);
+
+  struct ArgClosure : public ScriptEnvironmentPreparer::Closure {
+      ArgClosure(ffi_cif* cifArg, void* resultArg, void** argsArg, ClosureInfo* cinfoArg)
+        : cif(cifArg), result(resultArg), args(argsArg), cinfo(cinfoArg) {}
+
+      bool operator()(JSContext *cx) override;
+
+      ffi_cif* cif;
+      void* result;
+      void** args;
+      ClosureInfo* cinfo;
+  };
 }
 
 namespace CData {
   static void Finalize(JSFreeOp* fop, JSObject* obj);
 
   bool ValueGetter(JSContext* cx, JS::CallArgs args);
   bool ValueSetter(JSContext* cx, JS::CallArgs args);
 
@@ -6775,19 +6787,16 @@ CClosure::Create(JSContext* cx,
   // Get the prototype of the FunctionType object, of class CTypeProto,
   // which stores our JSContext for use with the closure.
   RootedObject proto(cx);
   if (!JS_GetPrototype(cx, typeObj, &proto))
     return nullptr;
   MOZ_ASSERT(proto);
   MOZ_ASSERT(CType::IsCTypeProto(proto));
 
-  // Get a JSContext for use with the closure.
-  JSContext* closeureCx = js::DefaultJSContext(JS_GetRuntime(cx));
-
   // Prepare the error sentinel value. It's important to do this now, because
   // we might be unable to convert the value to the proper type. If so, we want
   // the caller to know about it _now_, rather than some uncertain time in the
   // future when the error sentinel is actually needed.
   mozilla::UniquePtr<uint8_t[], JS::FreePolicy> errResult;
   if (!errVal.isUndefined()) {
 
     // Make sure the callback returns something.
@@ -6814,17 +6823,16 @@ CClosure::Create(JSContext* cx,
 
   ClosureInfo* cinfo = cx->new_<ClosureInfo>(JS_GetRuntime(cx));
   if (!cinfo) {
     JS_ReportOutOfMemory(cx);
     return nullptr;
   }
 
   // Copy the important bits of context into cinfo.
-  cinfo->cx = closeureCx;
   cinfo->errResult = errResult.release();
   cinfo->closureObj = result;
   cinfo->typeObj = typeObj;
   cinfo->thisObj = thisObj;
   cinfo->jsfnObj = fnObj;
 
   // Stash the ClosureInfo struct on our new object.
   JS_SetReservedSlot(result, SLOT_CLOSUREINFO, PRIVATE_TO_JSVAL(cinfo));
@@ -6885,33 +6893,37 @@ void
 CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData)
 {
   MOZ_ASSERT(cif);
   MOZ_ASSERT(result);
   MOZ_ASSERT(args);
   MOZ_ASSERT(userData);
 
   // Retrieve the essentials from our closure object.
-  ClosureInfo* cinfo = static_cast<ClosureInfo*>(userData);
-  JSContext* cx = cinfo->cx;
-
+  ArgClosure argClosure(cif, result, args, static_cast<ClosureInfo*>(userData));
+  JSRuntime* rt = argClosure.cinfo->rt;
+  RootedObject fun(rt, argClosure.cinfo->jsfnObj);
+  (void) js::PrepareScriptEnvironmentAndInvoke(rt, fun, argClosure);
+}
+
+bool CClosure::ArgClosure::operator()(JSContext* cx)
+{
   // Let the runtime callback know that we are about to call into JS again. The end callback will
   // fire automatically when we exit this function.
   js::AutoCTypesActivityCallback autoCallback(cx, js::CTYPES_CALLBACK_BEGIN,
                                               js::CTYPES_CALLBACK_END);
 
   RootedObject typeObj(cx, cinfo->typeObj);
   RootedObject thisObj(cx, cinfo->thisObj);
   RootedValue jsfnVal(cx, ObjectValue(*cinfo->jsfnObj));
+  AssertSameCompartment(cx, cinfo->jsfnObj);
+
 
   JS_AbortIfWrongThread(JS_GetRuntime(cx));
 
-  JSAutoRequest ar(cx);
-  JSAutoCompartment ac(cx, cinfo->jsfnObj);
-
   // Assert that our CIFs agree.
   FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
   MOZ_ASSERT(cif == &fninfo->mCIF);
 
   TypeCode typeCode = CType::GetTypeCode(fninfo->mReturnType);
 
   // Initialize the result to zero, in case something fails. Small integer types
   // are promoted to a word-sized ffi_arg, so we must be careful to zero the
@@ -6934,25 +6946,25 @@ CClosure::ClosureStub(ffi_cif* cif, void
     }
     memset(result, 0, rvSize);
   }
 
   // Set up an array for converted arguments.
   JS::AutoValueVector argv(cx);
   if (!argv.resize(cif->nargs)) {
     JS_ReportOutOfMemory(cx);
-    return;
+    return false;
   }
 
   for (uint32_t i = 0; i < cif->nargs; ++i) {
     // Convert each argument, and have any CData objects created depend on
     // the existing buffers.
     RootedObject argType(cx, fninfo->mArgTypes[i]);
     if (!ConvertToJS(cx, argType, nullptr, args[i], false, false, argv[i]))
-      return;
+      return false;
   }
 
   // Call the JS function. 'thisObj' may be nullptr, in which case the JS
   // engine will find an appropriate object to use.
   RootedValue rval(cx);
   bool success = JS_CallFunctionValue(cx, thisObj, jsfnVal, argv, &rval);
 
   // Convert the result. Note that we pass 'ConversionType::Return', such that
@@ -6963,43 +6975,34 @@ CClosure::ClosureStub(ffi_cif* cif, void
   if (success && cif->rtype != &ffi_type_void)
     success = ImplicitConvert(cx, rval, fninfo->mReturnType, result,
                               ConversionType::Return, nullptr, typeObj);
 
   if (!success) {
     // Something failed. The callee may have thrown, or it may not have
     // returned a value that ImplicitConvert() was happy with. Depending on how
     // prudent the consumer has been, we may or may not have a recovery plan.
-
-    // In any case, a JS exception cannot be passed to C code, so report the
-    // exception if any and clear it from the cx.
-    if (JS_IsExceptionPending(cx))
-      JS_ReportPendingException(cx);
+    //
+    // Note that PrepareScriptEnvironmentAndInvoke should take care of reporting
+    // the exception.
 
     if (cinfo->errResult) {
       // Good case: we have a sentinel that we can return. Copy it in place of
       // the actual return value, and then proceed.
 
       // The buffer we're returning might be larger than the size of the return
       // type, due to libffi alignment issues (see above). But it should never
       // be smaller.
       size_t copySize = CType::GetSize(fninfo->mReturnType);
       MOZ_ASSERT(copySize <= rvSize);
       memcpy(result, cinfo->errResult, copySize);
     } else {
       // Bad case: not much we can do here. The rv is already zeroed out, so we
-      // just report (another) error and hope for the best. JS_ReportError will
-      // actually throw an exception here, so then we have to report it. Again.
-      // Ugh.
-      JS_ReportError(cx, "JavaScript callback failed, and an error sentinel "
-                         "was not specified.");
-      if (JS_IsExceptionPending(cx))
-        JS_ReportPendingException(cx);
-
-      return;
+      // just return and hope for the best.
+      return false;
     }
   }
 
   // Small integer types must be returned as a word-sized ffi_arg. Coerce it
   // back into the size libffi expects.
   switch (typeCode) {
 #define INTEGRAL_CASE(name, type, ffiType)                                     \
   case TYPE_##name:                                                            \
@@ -7012,16 +7015,18 @@ CClosure::ClosureStub(ffi_cif* cif, void
     CTYPES_FOR_EACH_WRAPPED_INT_TYPE(INTEGRAL_CASE)
     CTYPES_FOR_EACH_BOOL_TYPE(INTEGRAL_CASE)
     CTYPES_FOR_EACH_CHAR_TYPE(INTEGRAL_CASE)
     CTYPES_FOR_EACH_CHAR16_TYPE(INTEGRAL_CASE)
 #undef INTEGRAL_CASE
   default:
     break;
   }
+
+  return true;
 }
 
 /*******************************************************************************
 ** CData implementation
 *******************************************************************************/
 
 // Create a new CData object of type 'typeObj' containing binary data supplied
 // in 'source', optionally with a referent object 'refObj'.
--- a/js/src/ctypes/CTypes.h
+++ b/js/src/ctypes/CTypes.h
@@ -312,19 +312,17 @@ struct FunctionInfo
   // Flag indicating whether the function behaves like a C function with
   // ... as the final formal parameter.
   bool mIsVariadic;
 };
 
 // Parameters necessary for invoking a JS function from a C closure.
 struct ClosureInfo
 {
-  JSContext* cx;                   // JSContext to use
-  JSRuntime* rt;                   // Used in the destructor, where cx might have already
-                                   // been GCed.
+  JSRuntime* rt;
   JS::Heap<JSObject*> closureObj;  // CClosure object
   JS::Heap<JSObject*> typeObj;     // FunctionType describing the C function
   JS::Heap<JSObject*> thisObj;     // 'this' object to use for the JS function call
   JS::Heap<JSObject*> jsfnObj;     // JS function
   void* errResult;                 // Result that will be returned if the closure throws
   ffi_closure* closure;            // The C closure itself
 
   // Anything conditionally freed in the destructor should be initialized to
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -25,16 +25,17 @@
 #include "vm/TypedArrayObject.h"
 #include "vm/UnboxedObject.h"
 
 #include "jscompartmentinlines.h"
 #include "jsobjinlines.h"
 
 #include "gc/Nursery-inl.h"
 #include "vm/String-inl.h"
+#include "vm/UnboxedObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
 
 using mozilla::ArrayLength;
 using mozilla::DebugOnly;
 using mozilla::IsBaseOf;
 using mozilla::IsSame;
--- a/js/src/gc/Nursery-inl.h
+++ b/js/src/gc/Nursery-inl.h
@@ -36,28 +36,30 @@ namespace js {
 // instead.
 
 template <typename T>
 static inline T*
 AllocateObjectBuffer(ExclusiveContext* cx, uint32_t count)
 {
     if (cx->isJSContext()) {
         Nursery& nursery = cx->asJSContext()->runtime()->gc.nursery;
-        return static_cast<T*>(nursery.allocateBuffer(cx->zone(), count * sizeof(T)));
+        size_t nbytes = JS_ROUNDUP(count * sizeof(T), sizeof(Value));
+        return static_cast<T*>(nursery.allocateBuffer(cx->zone(), nbytes));
     }
     return cx->zone()->pod_malloc<T>(count);
 }
 
 template <typename T>
 static inline T*
 AllocateObjectBuffer(ExclusiveContext* cx, JSObject* obj, uint32_t count)
 {
     if (cx->isJSContext()) {
         Nursery& nursery = cx->asJSContext()->runtime()->gc.nursery;
-        return static_cast<T*>(nursery.allocateBuffer(obj, count * sizeof(T)));
+        size_t nbytes = JS_ROUNDUP(count * sizeof(T), sizeof(Value));
+        return static_cast<T*>(nursery.allocateBuffer(obj, nbytes));
     }
     return obj->zone()->pod_malloc<T>(count);
 }
 
 // If this returns null then the old buffer will be left alone.
 template <typename T>
 static inline T*
 ReallocateObjectBuffer(ExclusiveContext* cx, JSObject* obj, T* oldBuffer,
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -81,17 +81,17 @@ class gcstats::StatisticsSerializer
         append(name, vfmt, va, units);
         va_end(va);
     }
 
     void appendDecimal(const char* name, const char* units, double d) {
         if (d < 0)
             d = 0;
         if (asJSON_)
-            appendNumber(name, "%d.%d", units, (int)d, (int)(d * 10.) % 10);
+            appendNumber(name, "%d.%03d", units, (int)d, (int)(d * 1000.) % 1000);
         else
             appendNumber(name, "%.1f", units, d);
     }
 
     void appendIfNonzeroMS(const char* name, double v) {
         if (asJSON_ || v >= 0.1)
             appendDecimal(name, "ms", v);
     }
@@ -562,29 +562,38 @@ Statistics::formatData(StatisticsSeriali
     ss.endObject();
 
     return !ss.isOOM();
 }
 
 typedef Vector<UniqueChars, 8, SystemAllocPolicy> FragmentVector;
 
 static UniqueChars
-Join(const FragmentVector& fragments) {
+Join(const FragmentVector& fragments, const char* separator = "") {
+    const size_t separatorLength = strlen(separator);
     size_t length = 0;
-    for (size_t i = 0; i < fragments.length(); ++i)
+    for (size_t i = 0; i < fragments.length(); ++i) {
         length += fragments[i] ? strlen(fragments[i].get()) : 0;
+        if (i < (fragments.length() - 1))
+            length += separatorLength;
+    }
 
     char* joined = js_pod_malloc<char>(length + 1);
     joined[length] = '\0';
 
     char* cursor = joined;
     for (size_t i = 0; i < fragments.length(); ++i) {
         if (fragments[i])
             strcpy(cursor, fragments[i].get());
         cursor += fragments[i] ? strlen(fragments[i].get()) : 0;
+        if (i < (fragments.length() - 1)) {
+            if (separatorLength)
+                strcpy(cursor, separator);
+            cursor += separatorLength;
+        }
     }
 
     return UniqueChars(joined);
 }
 
 static int64_t
 SumChildTimes(size_t phaseSlot, Phase phase, Statistics::PhaseTimeTable phaseTimes)
 {
@@ -750,32 +759,179 @@ Statistics::formatDetailedTotals()
     Max Pause: %.3fms\n\
 ";
     char buffer[1024];
     memset(buffer, 0, sizeof(buffer));
     JS_snprintf(buffer, sizeof(buffer), format, t(total), t(longest));
     return make_string_copy(buffer);
 }
 
+UniqueChars
+Statistics::formatJsonMessage(uint64_t timestamp)
+{
+    MOZ_ASSERT(!aborted);
+
+    FragmentVector fragments;
+
+    if (!fragments.append(make_string_copy("{")) ||
+        !fragments.append(formatJsonDescription(timestamp)) ||
+        !fragments.append(make_string_copy("\"slices\":[")))
+    {
+        return UniqueChars(nullptr);
+    }
+
+    for (unsigned i = 0; i < slices.length(); i++) {
+        if (!fragments.append(make_string_copy("{")) ||
+            !fragments.append(formatJsonSliceDescription(i, slices[i])) ||
+            !fragments.append(make_string_copy("\"times\":{")) ||
+            !fragments.append(formatJsonPhaseTimes(slices[i].phaseTimes)) ||
+            !fragments.append(make_string_copy("}}")) ||
+            (i < (slices.length() - 1) && !fragments.append(make_string_copy(","))))
+        {
+            return UniqueChars(nullptr);
+        }
+    }
+
+    if (!fragments.append(make_string_copy("],\"totals\":{")) ||
+        !fragments.append(formatJsonPhaseTimes(phaseTimes)) ||
+        !fragments.append(make_string_copy("}}")))
+    {
+        return UniqueChars(nullptr);
+    }
+
+    return Join(fragments);
+}
+
+UniqueChars
+Statistics::formatJsonDescription(uint64_t timestamp)
+{
+    int64_t total, longest;
+    gcDuration(&total, &longest);
+
+    int64_t sccTotal, sccLongest;
+    sccDurations(&sccTotal, &sccLongest);
+
+    double mmu20 = computeMMU(20 * PRMJ_USEC_PER_MSEC);
+    double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC);
+
+    const char *format =
+        "\"timestamp\":%llu,"
+        "\"max_pause\":%lu.%03lu,"
+        "\"total_time\":%lu.%03lu,"
+        "\"zones_collected\":%d,"
+        "\"total_zones\":%d,"
+        "\"total_compartments\":%d,"
+        "\"minor_gcs\":%d,"
+        "\"store_buffer_overflows\":%d,"
+        "\"mmu_20ms\":%d,"
+        "\"mmu_50ms\":%d,"
+        "\"scc_sweep_total\":%lu.%03lu,"
+        "\"scc_sweep_max_pause\":%lu.%03lu,"
+        "\"nonincremental_reason\":\"%s\","
+        "\"allocated\":%u,"
+        "\"added_chunks\":%d,"
+        "\"removed_chunks\":%d,";
+    char buffer[1024];
+    memset(buffer, 0, sizeof(buffer));
+    JS_snprintf(buffer, sizeof(buffer), format,
+                (unsigned long long)timestamp,
+                longest / 1000, longest % 1000,
+                total / 1000, total % 1000,
+                zoneStats.collectedZoneCount,
+                zoneStats.zoneCount,
+                zoneStats.compartmentCount,
+                counts[STAT_MINOR_GC],
+                counts[STAT_STOREBUFFER_OVERFLOW],
+                int(mmu20 * 100),
+                int(mmu50 * 100),
+                sccTotal / 1000, sccTotal % 1000,
+                sccLongest / 1000, sccLongest % 1000,
+                nonincrementalReason_ ? nonincrementalReason_ : "none",
+                unsigned(preBytes / 1024 / 1024),
+                counts[STAT_NEW_CHUNK],
+                counts[STAT_DESTROY_CHUNK]);
+    return make_string_copy(buffer);
+}
+
+UniqueChars
+Statistics::formatJsonSliceDescription(unsigned i, const SliceData& slice)
+{
+    int64_t duration = slices[i].duration();
+    int64_t when = slices[i].start - slices[0].start;
+    char budgetDescription[200];
+    slice.budget.describe(budgetDescription, sizeof(budgetDescription) - 1);
+    int64_t pageFaults = slices[i].endFaults - slices[i].startFaults;
+
+    const char* format =
+        "\"slice\":%d,"
+        "\"pause\":%lu.%03lu,"
+        "\"when\":%lu.%03lu,"
+        "\"reason\":\"%s\","
+        "\"budget\":\"%s\","
+        "\"page_faults\":%llu,"
+        "\"start_timestamp\":%llu,"
+        "\"end_timestamp\":%llu,";
+    char buffer[1024];
+    memset(buffer, 0, sizeof(buffer));
+    JS_snprintf(buffer, sizeof(buffer), format,
+                i,
+                duration / 1000, duration % 1000,
+                when / 1000, when % 1000,
+                ExplainReason(slices[i].reason),
+                budgetDescription,
+                pageFaults,
+                slices[i].start,
+                slices[i].end);
+    return make_string_copy(buffer);
+}
+
+UniqueChars
+FilterJsonKey(const char*const buffer)
+{
+    char* mut = strdup(buffer);
+    char* c = mut;
+    while (*c) {
+        if (!isalpha(*c))
+            *c = '_';
+        else if (isupper(*c))
+            *c = tolower(*c);
+        ++c;
+    }
+    return UniqueChars(mut);
+}
+
+UniqueChars
+Statistics::formatJsonPhaseTimes(PhaseTimeTable phaseTimes)
+{
+    FragmentVector fragments;
+    char buffer[128];
+    for (AllPhaseIterator iter(phaseTimes); !iter.done(); iter.advance()) {
+        Phase phase;
+        size_t dagSlot;
+        iter.get(&phase, &dagSlot);
+
+        UniqueChars name = FilterJsonKey(phases[phase].name);
+        int64_t ownTime = phaseTimes[dagSlot][phase];
+        JS_snprintf(buffer, sizeof(buffer), "\"%s\":%lu.%03lu",
+                    name.get(), ownTime / 1000, ownTime % 1000);
+
+        if (!fragments.append(make_string_copy(buffer)))
+            return UniqueChars(nullptr);
+    }
+    return Join(fragments, ",");
+}
+
 char16_t*
 Statistics::formatMessage()
 {
     StatisticsSerializer ss(StatisticsSerializer::AsText);
     formatData(ss, 0);
     return ss.finishJSString();
 }
 
-char16_t*
-Statistics::formatJSON(uint64_t timestamp)
-{
-    StatisticsSerializer ss(StatisticsSerializer::AsJSON);
-    formatData(ss, timestamp);
-    return ss.finishJSString();
-}
-
 Statistics::Statistics(JSRuntime* rt)
   : runtime(rt),
     startupTime(PRMJ_Now()),
     fp(nullptr),
     gcDepth(0),
     nonincrementalReason_(nullptr),
     timedGCStart(0),
     preBytes(0),
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -182,17 +182,17 @@ struct Statistics
         MOZ_ASSERT(s < STAT_LIMIT);
         counts[s]++;
     }
 
     int64_t beginSCC();
     void endSCC(unsigned scc, int64_t start);
 
     char16_t* formatMessage();
-    char16_t* formatJSON(uint64_t timestamp);
+    UniqueChars formatJsonMessage(uint64_t timestamp);
     UniqueChars formatDetailedMessage();
 
     JS::GCSliceCallback setSliceCallback(JS::GCSliceCallback callback);
 
     int64_t clearMaxGCPauseAccumulator();
     int64_t getMaxGCPauseSinceClear();
 
     // Return the current phase, suppressing the synthetic PHASE_MUTATOR phase.
@@ -314,16 +314,20 @@ struct Statistics
     void printStats();
     bool formatData(StatisticsSerializer& ss, uint64_t timestamp);
 
     UniqueChars formatDetailedDescription();
     UniqueChars formatDetailedSliceDescription(unsigned i, const SliceData& slice);
     UniqueChars formatDetailedPhaseTimes(PhaseTimeTable phaseTimes);
     UniqueChars formatDetailedTotals();
 
+    UniqueChars formatJsonDescription(uint64_t timestamp);
+    UniqueChars formatJsonSliceDescription(unsigned i, const SliceData& slice);
+    UniqueChars formatJsonPhaseTimes(PhaseTimeTable phaseTimes);
+
     double computeMMU(int64_t resolution);
 };
 
 struct AutoGCSlice
 {
     AutoGCSlice(Statistics& stats, const ZoneGCStats& zoneStats, JSGCInvocationKind gckind,
                 SliceBudget budget, JS::gcreason::Reason reason
                 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
--- a/js/src/gc/Tracer.cpp
+++ b/js/src/gc/Tracer.cpp
@@ -21,16 +21,18 @@
 #include "gc/Marking.h"
 #include "gc/Zone.h"
 
 #include "vm/Shape.h"
 #include "vm/Symbol.h"
 
 #include "jsgcinlines.h"
 
+#include "vm/ObjectGroup-inl.h"
+
 using namespace js;
 using namespace js::gc;
 using mozilla::DebugOnly;
 
 namespace js {
 template<typename T>
 void
 CheckTracedThing(JSTracer* trc, T thing);
--- a/js/src/jit/BacktrackingAllocator.cpp
+++ b/js/src/jit/BacktrackingAllocator.cpp
@@ -1,15 +1,18 @@
 /* -*- 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/. */
 
 #include "jit/BacktrackingAllocator.h"
+
+#include "jsprf.h"
+
 #include "jit/BitSet.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::DebugOnly;
 
 /////////////////////////////////////////////////////////////////////
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -32,16 +32,17 @@
 
 #include "jsboolinlines.h"
 #include "jsscriptinlines.h"
 
 #include "jit/JitFrames-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/ScopeObject-inl.h"
 #include "vm/StringObject-inl.h"
+#include "vm/UnboxedObject-inl.h"
 
 using mozilla::BitwiseCast;
 using mozilla::DebugOnly;
 
 namespace js {
 namespace jit {
 
 #ifdef DEBUG
@@ -5149,39 +5150,23 @@ RemoveExistingTypedArraySetElemStub(JSCo
         // being replaced with one that expects out of bounds index.
         MOZ_ASSERT(!iter->toSetElem_TypedArray()->expectOutOfBounds());
         iter.unlink(cx);
         return true;
     }
     return false;
 }
 
-static size_t
-SetElemObjectInitializedLength(JSObject *obj)
-{
-    if (obj->isNative())
-        return obj->as<NativeObject>().getDenseInitializedLength();
-    return obj->as<UnboxedArrayObject>().initializedLength();
-}
-
-static size_t
-SetElemObjectCapacity(JSObject *obj)
-{
-    if (obj->isNative())
-        return obj->as<NativeObject>().getDenseCapacity();
-    return obj->as<UnboxedArrayObject>().capacity();
-}
-
 static bool
 CanOptimizeDenseOrUnboxedArraySetElem(JSObject* obj, uint32_t index,
                                       Shape* oldShape, uint32_t oldCapacity, uint32_t oldInitLength,
                                       bool* isAddingCaseOut, size_t* protoDepthOut)
 {
-    uint32_t initLength = SetElemObjectInitializedLength(obj);
-    uint32_t capacity = SetElemObjectCapacity(obj);
+    uint32_t initLength = GetAnyBoxedOrUnboxedInitializedLength(obj);
+    uint32_t capacity = GetAnyBoxedOrUnboxedCapacity(obj);
 
     *isAddingCaseOut = false;
     *protoDepthOut = 0;
 
     // Some initial sanity checks.
     if (initLength < oldInitLength || capacity < oldCapacity)
         return false;
 
@@ -5258,19 +5243,19 @@ DoSetElemFallback(JSContext* cx, Baselin
     if (!obj)
         return false;
 
     RootedShape oldShape(cx, obj->maybeShape());
 
     // Check the old capacity
     uint32_t oldCapacity = 0;
     uint32_t oldInitLength = 0;
-    if (obj->isNative() && index.isInt32() && index.toInt32() >= 0) {
-        oldCapacity = obj->as<NativeObject>().getDenseCapacity();
-        oldInitLength = obj->as<NativeObject>().getDenseInitializedLength();
+    if (index.isInt32() && index.toInt32() >= 0) {
+        oldCapacity = GetAnyBoxedOrUnboxedCapacity(obj);
+        oldInitLength = GetAnyBoxedOrUnboxedInitializedLength(obj);
     }
 
     if (op == JSOP_INITELEM) {
         if (!InitElemOperation(cx, obj, index, rhs))
             return false;
     } else if (op == JSOP_INITELEM_ARRAY) {
         MOZ_ASSERT(uint32_t(index.toInt32()) == GET_UINT24(pc));
         if (!InitArrayElemOperation(cx, pc, obj, index.toInt32(), rhs))
@@ -5528,18 +5513,16 @@ ICSetElem_DenseOrUnboxedArray::Compiler:
         emitPostWriteBarrierSlot(masm, obj, R1, scratchReg, saveRegs);
 
         masm.Pop(R1);
     }
 
     // Unbox key.
     Register key = masm.extractInt32(R1, ExtractTemp1);
 
-    Address valueAddr(BaselineStackReg, ICStackValueOffset);
-
     if (unboxedType_ == JSVAL_TYPE_MAGIC) {
         // Set element on a native object.
 
         // Load obj->elements in scratchReg.
         masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratchReg);
 
         // Bounds check.
         Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength());
@@ -5564,16 +5547,18 @@ ICSetElem_DenseOrUnboxedArray::Compiler:
                           &failure);
 
         // Failure is not possible now.  Free up registers.
         regs.add(R0);
         regs.add(R1);
         regs.takeUnchecked(obj);
         regs.takeUnchecked(key);
 
+        Address valueAddr(BaselineStackReg, ICStackValueOffset);
+
         // We need to convert int32 values being stored into doubles. In this case
         // the heap typeset is guaranteed to contain both int32 and double, so it's
         // okay to store a double. Note that double arrays are only created by
         // IonMonkey, so if we have no floating-point support Ion is disabled and
         // there should be no double arrays.
         if (cx->runtime()->jitSupportsFloatingPoint)
             masm.convertInt32ValueToDouble(valueAddr, regs.getAny(), &noSpecialHandling);
         else
@@ -5593,20 +5578,21 @@ ICSetElem_DenseOrUnboxedArray::Compiler:
         masm.load32(initLength, scratchReg);
         masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg);
         masm.branch32(Assembler::BelowOrEqual, scratchReg, key, &failure);
 
         // Load obj->elements.
         masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg);
 
         // Compute the address being written to.
-        BaseIndex address(obj, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_)));
+        BaseIndex address(scratchReg, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_)));
 
         EmitUnboxedPreBarrierForBaseline(masm, address, unboxedType_);
 
+        Address valueAddr(BaselineStackReg, ICStackValueOffset + sizeof(Value));
         masm.Push(R0);
         masm.loadValue(valueAddr, R0);
         masm.storeUnboxedProperty(address, unboxedType_,
                                   ConstantOrRegister(TypedOrValueRegister(R0)), &failurePopR0);
         masm.Pop(R0);
     }
 
     EmitReturnFromIC(masm);
@@ -5743,18 +5729,16 @@ ICSetElemDenseOrUnboxedArrayAddCompiler:
 
     // Reset register set.
     regs = availableGeneralRegs(2);
     scratchReg = regs.takeAny();
 
     // Unbox key.
     Register key = masm.extractInt32(R1, ExtractTemp1);
 
-    Address valueAddr(BaselineStackReg, ICStackValueOffset);
-
     if (unboxedType_ == JSVAL_TYPE_MAGIC) {
         // Adding element to a native object.
 
         // Load obj->elements in scratchReg.
         masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratchReg);
 
         // Bounds check (key == initLength)
         Address initLength(scratchReg, ObjectElements::offsetOfInitializedLength());
@@ -5788,16 +5772,19 @@ ICSetElemDenseOrUnboxedArrayAddCompiler:
 
         // Convert int32 values to double if convertDoubleElements is set. In this
         // case the heap typeset is guaranteed to contain both int32 and double, so
         // it's okay to store a double.
         Label dontConvertDoubles;
         masm.branchTest32(Assembler::Zero, elementsFlags,
                           Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS),
                           &dontConvertDoubles);
+
+        Address valueAddr(BaselineStackReg, ICStackValueOffset);
+
         // Note that double arrays are only created by IonMonkey, so if we have no
         // floating-point support Ion is disabled and there should be no double arrays.
         if (cx->runtime()->jitSupportsFloatingPoint)
             masm.convertInt32ValueToDouble(valueAddr, regs.getAny(), &dontConvertDoubles);
         else
             masm.assumeUnreachable("There shouldn't be double arrays when there is no FP support.");
         masm.bind(&dontConvertDoubles);
 
@@ -5810,41 +5797,41 @@ ICSetElemDenseOrUnboxedArrayAddCompiler:
         // Adding element to an unboxed array.
 
         // Bounds check (key == initLength)
         Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength());
         masm.load32(initLengthAddr, scratchReg);
         masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), scratchReg);
         masm.branch32(Assembler::NotEqual, scratchReg, key, &failure);
 
-        Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength());
-
         // Capacity check.
         masm.checkUnboxedArrayCapacity(obj, Int32Key(key), scratchReg, &failure);
 
-        // Increment initLength before write.
+        // Load obj->elements.
+        masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg);
+
+        // Write the value first, since this can fail. No need for pre-barrier
+        // since we're not overwriting an old value.
+        masm.Push(R0);
+        Address valueAddr(BaselineStackReg, ICStackValueOffset + sizeof(Value));
+        masm.loadValue(valueAddr, R0);
+        BaseIndex address(scratchReg, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_)));
+        masm.storeUnboxedProperty(address, unboxedType_,
+                                  ConstantOrRegister(TypedOrValueRegister(R0)), &failurePopR0);
+        masm.Pop(R0);
+
+        // Increment initialized length.
         masm.add32(Imm32(1), initLengthAddr);
 
-        // If length is now <= key, increment length before write.
+        // If length is now <= key, increment length.
+        Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength());
         Label skipIncrementLength;
         masm.branch32(Assembler::Above, lengthAddr, key, &skipIncrementLength);
         masm.add32(Imm32(1), lengthAddr);
         masm.bind(&skipIncrementLength);
-
-        // Load obj->elements.
-        masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), scratchReg);
-
-        masm.Push(R0);
-        masm.loadValue(valueAddr, R0);
-
-        // Write the value. No need for pre-barrier since we're not overwriting an old value.
-        BaseIndex address(obj, key, ScaleFromElemWidth(UnboxedTypeSize(unboxedType_)));
-        masm.storeUnboxedProperty(address, unboxedType_,
-                                  ConstantOrRegister(TypedOrValueRegister(R0)), &failurePopR0);
-        masm.Pop(R0);
     }
 
     EmitReturnFromIC(masm);
 
     if (failurePopR0.used()) {
         masm.bind(&failurePopR0);
         masm.Pop(R0);
         masm.jump(&failure);
@@ -9951,28 +9938,21 @@ GetTemplateObjectForNative(JSContext* cx
         ObjectGroup* group = ObjectGroup::allocationSiteGroup(cx, script, pc, JSProto_Array);
         if (!group)
             return false;
         res->setGroup(group);
         return true;
     }
 
     if (native == js::array_concat) {
-        if (args.thisv().isObject() &&
-            args.thisv().toObject().is<ArrayObject>() &&
-            !args.thisv().toObject().isSingleton() &&
-            !args.thisv().toObject().group()->hasUnanalyzedPreliminaryObjects())
-        {
-            RootedObject proto(cx, args.thisv().toObject().getProto());
-            res.set(NewDenseEmptyArray(cx, proto, TenuredObject));
+        if (args.thisv().isObject() && !args.thisv().toObject().isSingleton()) {
+            res.set(NewFullyAllocatedArrayTryReuseGroup(cx, &args.thisv().toObject(), 0,
+                                                        TenuredObject, /* forceAnalyze = */ true));
             if (!res)
                 return false;
-
-            res->setGroup(args.thisv().toObject().group());
-            return true;
         }
     }
 
     if (native == js::str_split && args.length() == 1 && args[0].isString()) {
         res.set(NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject));
         if (!res)
             return false;
 
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -3086,17 +3086,17 @@ class ICGetElem_UnboxedArray : public IC
                    (static_cast<int32_t>(elementType_) << 16);
         }
 
       public:
         Compiler(JSContext* cx, ICStub* firstMonitorStub, ObjectGroup* group)
           : ICStubCompiler(cx, ICStub::GetElem_UnboxedArray),
             firstMonitorStub_(firstMonitorStub),
             group_(cx, group),
-            elementType_(group->unboxedLayout().elementType())
+            elementType_(group->unboxedLayoutDontCheckGeneration().elementType())
         {}
 
         ICStub* getStub(ICStubSpace* space) {
             return newStub<ICGetElem_UnboxedArray>(space, getStubCode(), firstMonitorStub_, group_);
         }
     };
 };
 
@@ -3293,17 +3293,19 @@ class ICSetElem_DenseOrUnboxedArray : pu
             return static_cast<int32_t>(kind) |
                    (static_cast<int32_t>(unboxedType_) << 16);
         }
 
         Compiler(JSContext* cx, Shape* shape, HandleObjectGroup group)
           : ICStubCompiler(cx, ICStub::SetElem_DenseOrUnboxedArray),
             shape_(cx, shape),
             group_(cx, group),
-            unboxedType_(shape ? JSVAL_TYPE_MAGIC : group->unboxedLayout().elementType())
+            unboxedType_(shape
+                         ? JSVAL_TYPE_MAGIC
+                         : group->unboxedLayoutDontCheckGeneration().elementType())
         {}
 
         ICUpdatedStub* getStub(ICStubSpace* space) {
             ICSetElem_DenseOrUnboxedArray* stub =
                 newStub<ICSetElem_DenseOrUnboxedArray>(space, getStubCode(), shape_, group_);
             if (!stub || !stub->initUpdatingChain(cx, space))
                 return nullptr;
             return stub;
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -5,16 +5,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/BaselineInspector.h"
 
 #include "mozilla/DebugOnly.h"
 
 #include "jit/BaselineIC.h"
 
+#include "vm/ObjectGroup-inl.h"
+
 using namespace js;
 using namespace js::jit;
 
 using mozilla::DebugOnly;
 
 bool
 SetElemICInspector::sawOOBDenseWrite() const
 {
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -2487,16 +2487,22 @@ void
 CodeGenerator::visitMaybeCopyElementsForWrite(LMaybeCopyElementsForWrite* lir)
 {
     Register object = ToRegister(lir->object());
     Register temp = ToRegister(lir->temp());
 
     OutOfLineCode* ool = oolCallVM(CopyElementsForWriteInfo, lir,
                                    (ArgList(), object), StoreNothing());
 
+    if (lir->mir()->checkNative()) {
+        masm.loadObjClass(object, temp);
+        masm.branchTest32(Assembler::NonZero, Address(temp, Class::offsetOfFlags()),
+                          Imm32(Class::NON_NATIVE), ool->rejoin());
+    }
+
     masm.loadPtr(Address(object, NativeObject::offsetOfElements()), temp);
     masm.branchTest32(Assembler::NonZero,
                       Address(temp, ObjectElements::offsetOfFlags()),
                       Imm32(ObjectElements::COPY_ON_WRITE),
                       ool->entry());
     masm.bind(ool->rejoin());
 }
 
@@ -6803,19 +6809,21 @@ CodeGenerator::visitOutOfLineStoreElemen
     callVM(SetDenseOrUnboxedArrayElementInfo, ins);
 
     restoreLive(ins);
     masm.jump(ool->rejoin());
 }
 
 template <typename T>
 static void
-StoreUnboxedPointer(MacroAssembler& masm, T address, MIRType type, const LAllocation* value)
-{
-    masm.patchableCallPreBarrier(address, type);
+StoreUnboxedPointer(MacroAssembler& masm, T address, MIRType type, const LAllocation* value,
+                    bool preBarrier)
+{
+    if (preBarrier)
+        masm.patchableCallPreBarrier(address, type);
     if (value->isConstant()) {
         Value v = *value->toConstant();
         if (v.isMarkable()) {
             masm.storePtr(ImmGCPtr(v.toGCThing()), address);
         } else {
             MOZ_ASSERT(v.isNull());
             masm.storePtr(ImmWord(0), address);
         }
@@ -6824,36 +6832,39 @@ StoreUnboxedPointer(MacroAssembler& masm
     }
 }
 
 void
 CodeGenerator::visitStoreUnboxedPointer(LStoreUnboxedPointer* lir)
 {
     MIRType type;
     int32_t offsetAdjustment;
+    bool preBarrier;
     if (lir->mir()->isStoreUnboxedObjectOrNull()) {
         type = MIRType_Object;
         offsetAdjustment = lir->mir()->toStoreUnboxedObjectOrNull()->offsetAdjustment();
+        preBarrier = lir->mir()->toStoreUnboxedObjectOrNull()->preBarrier();
     } else if (lir->mir()->isStoreUnboxedString()) {
         type = MIRType_String;
         offsetAdjustment = lir->mir()->toStoreUnboxedString()->offsetAdjustment();
+        preBarrier = lir->mir()->toStoreUnboxedString()->preBarrier();
     } else {
         MOZ_CRASH();
     }
 
     Register elements = ToRegister(lir->elements());
     const LAllocation* index = lir->index();
     const LAllocation* value = lir->value();
 
     if (index->isConstant()) {
         Address address(elements, ToInt32(index) * sizeof(uintptr_t) + offsetAdjustment);
-        StoreUnboxedPointer(masm, address, type, value);
+        StoreUnboxedPointer(masm, address, type, value, preBarrier);
     } else {
         BaseIndex address(elements, ToRegister(index), ScalePointer, offsetAdjustment);
-        StoreUnboxedPointer(masm, address, type, value);
+        StoreUnboxedPointer(masm, address, type, value, preBarrier);
     }
 }
 
 typedef bool (*ConvertUnboxedObjectToNativeFn)(JSContext*, JSObject*);
 static const VMFunction ConvertUnboxedObjectToNativeInfo =
     FunctionInfo<ConvertUnboxedObjectToNativeFn>(UnboxedPlainObject::convertToNative);
 
 void
@@ -6884,24 +6895,32 @@ CodeGenerator::emitArrayPopShift(LInstru
     } else {
         MOZ_ASSERT(mir->mode() == MArrayPopShift::Shift);
         ool = oolCallVM(ArrayShiftDenseInfo, lir, (ArgList(), obj), StoreValueTo(out));
     }
 
     // VM call if a write barrier is necessary.
     masm.branchTestNeedsIncrementalBarrier(Assembler::NonZero, ool->entry());
 
-    // Load elements and length.
-    masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
-    masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), lengthTemp);
-
-    // VM call if length != initializedLength.
+    // Load elements and length, and VM call if length != initializedLength.
     Int32Key key = Int32Key(lengthTemp);
-    Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
-    masm.branchKey(Assembler::NotEqual, initLength, key, ool->entry());
+    if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
+        masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
+        masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), lengthTemp);
+
+        Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
+        masm.branchKey(Assembler::NotEqual, initLength, key, ool->entry());
+    } else {
+        masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), elementsTemp);
+        masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), lengthTemp);
+        masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), lengthTemp);
+
+        Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength());
+        masm.branchKey(Assembler::NotEqual, lengthAddr, key, ool->entry());
+    }
 
     // Test for length != 0. On zero length either take a VM call or generate
     // an undefined value, depending on whether the call is known to produce
     // undefined.
     Label done;
     if (mir->maybeUndefined()) {
         Label notEmpty;
         masm.branchTest32(Assembler::NonZero, lengthTemp, lengthTemp, &notEmpty);
@@ -6910,35 +6929,51 @@ CodeGenerator::emitArrayPopShift(LInstru
         masm.bind(&notEmpty);
     } else {
         masm.branchTest32(Assembler::Zero, lengthTemp, lengthTemp, ool->entry());
     }
 
     masm.bumpKey(&key, -1);
 
     if (mir->mode() == MArrayPopShift::Pop) {
-        masm.loadElementTypedOrValue(BaseIndex(elementsTemp, lengthTemp, TimesEight), out,
-                                     mir->needsHoleCheck(), ool->entry());
+        if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
+            BaseIndex addr(elementsTemp, lengthTemp, TimesEight);
+            masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry());
+        } else {
+            size_t elemSize = UnboxedTypeSize(mir->unboxedType());
+            BaseIndex addr(elementsTemp, lengthTemp, ScaleFromElemWidth(elemSize));
+            masm.loadUnboxedProperty(addr, mir->unboxedType(), out);
+        }
     } else {
         MOZ_ASSERT(mir->mode() == MArrayPopShift::Shift);
-        masm.loadElementTypedOrValue(Address(elementsTemp, 0), out, mir->needsHoleCheck(),
-                                     ool->entry());
-    }
-
-    // Handle the failure case when the array length is non-writable in the
-    // OOL path.  (Unlike in the adding-an-element cases, we can't rely on the
-    // capacity <= length invariant for such arrays to avoid an explicit
-    // check.)
-    Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags());
-    Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH);
-    masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry());
-
-    // Now adjust length and initializedLength.
-    masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfLength()));
-    masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
+        Address addr(elementsTemp, 0);
+        if (mir->unboxedType() == JSVAL_TYPE_MAGIC)
+            masm.loadElementTypedOrValue(addr, out, mir->needsHoleCheck(), ool->entry());
+        else
+            masm.loadUnboxedProperty(addr, mir->unboxedType(), out);
+    }
+
+    if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
+        // Handle the failure case when the array length is non-writable in the
+        // OOL path.  (Unlike in the adding-an-element cases, we can't rely on the
+        // capacity <= length invariant for such arrays to avoid an explicit
+        // check.)
+        Address elementFlags(elementsTemp, ObjectElements::offsetOfFlags());
+        Imm32 bit(ObjectElements::NONWRITABLE_ARRAY_LENGTH);
+        masm.branchTest32(Assembler::NonZero, elementFlags, bit, ool->entry());
+
+        // Now adjust length and initializedLength.
+        masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfLength()));
+        masm.store32(lengthTemp, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
+    } else {
+        // Unboxed arrays always have writable lengths. Adjust length and
+        // initializedLength.
+        masm.store32(lengthTemp, Address(obj, UnboxedArrayObject::offsetOfLength()));
+        masm.add32(Imm32(-1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()));
+    }
 
     if (mir->mode() == MArrayPopShift::Shift) {
         // Don't save the temp registers.
         LiveRegisterSet temps;
         temps.add(elementsTemp);
         temps.add(lengthTemp);
 
         saveVolatile(temps);
@@ -6967,45 +7002,71 @@ CodeGenerator::visitArrayPopShiftT(LArra
 {
     Register obj = ToRegister(lir->object());
     Register elements = ToRegister(lir->temp0());
     Register length = ToRegister(lir->temp1());
     TypedOrValueRegister out(lir->mir()->type(), ToAnyRegister(lir->output()));
     emitArrayPopShift(lir, lir->mir(), obj, elements, length, out);
 }
 
-typedef bool (*ArrayPushDenseFn)(JSContext*, HandleArrayObject, HandleValue, uint32_t*);
+typedef bool (*ArrayPushDenseFn)(JSContext*, HandleObject, HandleValue, uint32_t*);
 static const VMFunction ArrayPushDenseInfo =
     FunctionInfo<ArrayPushDenseFn>(jit::ArrayPushDense);
 
 void
 CodeGenerator::emitArrayPush(LInstruction* lir, const MArrayPush* mir, Register obj,
                              ConstantOrRegister value, Register elementsTemp, Register length)
 {
     OutOfLineCode* ool = oolCallVM(ArrayPushDenseInfo, lir, (ArgList(), obj, value), StoreRegisterTo(length));
 
-    // Load elements and length.
-    masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
-    masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length);
-
     Int32Key key = Int32Key(length);
-    Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
-    Address capacity(elementsTemp, ObjectElements::offsetOfCapacity());
-
-    // Guard length == initializedLength.
-    masm.branchKey(Assembler::NotEqual, initLength, key, ool->entry());
-
-    // Guard length < capacity.
-    masm.branchKey(Assembler::BelowOrEqual, capacity, key, ool->entry());
-
-    masm.storeConstantOrRegister(value, BaseIndex(elementsTemp, length, TimesEight));
+    if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
+        // Load elements and length.
+        masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), elementsTemp);
+        masm.load32(Address(elementsTemp, ObjectElements::offsetOfLength()), length);
+
+        // Guard length == initializedLength.
+        Address initLength(elementsTemp, ObjectElements::offsetOfInitializedLength());
+        masm.branchKey(Assembler::NotEqual, initLength, key, ool->entry());
+
+        // Guard length < capacity.
+        Address capacity(elementsTemp, ObjectElements::offsetOfCapacity());
+        masm.branchKey(Assembler::BelowOrEqual, capacity, key, ool->entry());
+
+        // Do the store.
+        masm.storeConstantOrRegister(value, BaseIndex(elementsTemp, length, TimesEight));
+    } else {
+        // Load initialized length.
+        masm.load32(Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), length);
+        masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), length);
+
+        // Guard length == initializedLength.
+        Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength());
+        masm.branchKey(Assembler::NotEqual, lengthAddr, key, ool->entry());
+
+        // Guard length < capacity.
+        masm.checkUnboxedArrayCapacity(obj, key, elementsTemp, ool->entry());
+
+        // Load elements and do the store.
+        masm.loadPtr(Address(obj, UnboxedArrayObject::offsetOfElements()), elementsTemp);
+        size_t elemSize = UnboxedTypeSize(mir->unboxedType());
+        BaseIndex addr(elementsTemp, length, ScaleFromElemWidth(elemSize));
+        masm.storeUnboxedProperty(addr, mir->unboxedType(), value, nullptr);
+    }
 
     masm.bumpKey(&key, 1);
-    masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength()));
-    masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
+
+    // Update length and initialized length.
+    if (mir->unboxedType() == JSVAL_TYPE_MAGIC) {
+        masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfLength()));
+        masm.store32(length, Address(elementsTemp, ObjectElements::offsetOfInitializedLength()));
+    } else {
+        masm.store32(length, Address(obj, UnboxedArrayObject::offsetOfLength()));
+        masm.add32(Imm32(1), Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()));
+    }
 
     masm.bind(ool->rejoin());
 }
 
 void
 CodeGenerator::visitArrayPushV(LArrayPushV* lir)
 {
     Register obj = ToRegister(lir->object());
@@ -7039,23 +7100,33 @@ CodeGenerator::visitArrayConcat(LArrayCo
     Register rhs = ToRegister(lir->rhs());
     Register temp1 = ToRegister(lir->temp1());
     Register temp2 = ToRegister(lir->temp2());
 
     // If 'length == initializedLength' for both arrays we try to allocate an object
     // inline and pass it to the stub. Else, we just pass nullptr and the stub falls
     // back to a slow path.
     Label fail, call;
-    masm.loadPtr(Address(lhs, NativeObject::offsetOfElements()), temp1);
-    masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
-    masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
-
-    masm.loadPtr(Address(rhs, NativeObject::offsetOfElements()), temp1);
-    masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
-    masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
+    if (lir->mir()->unboxedType() == JSVAL_TYPE_MAGIC) {
+        masm.loadPtr(Address(lhs, NativeObject::offsetOfElements()), temp1);
+        masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
+        masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
+
+        masm.loadPtr(Address(rhs, NativeObject::offsetOfElements()), temp1);
+        masm.load32(Address(temp1, ObjectElements::offsetOfInitializedLength()), temp2);
+        masm.branch32(Assembler::NotEqual, Address(temp1, ObjectElements::offsetOfLength()), temp2, &fail);
+    } else {
+        masm.load32(Address(lhs, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), temp1);
+        masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp1);
+        masm.branch32(Assembler::NotEqual, Address(lhs, UnboxedArrayObject::offsetOfLength()), temp1, &fail);
+
+        masm.load32(Address(rhs, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), temp1);
+        masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), temp1);
+        masm.branch32(Assembler::NotEqual, Address(rhs, UnboxedArrayObject::offsetOfLength()), temp1, &fail);
+    }
 
     // Try to allocate an object.
     masm.createGCObject(temp1, temp2, lir->mir()->templateObj(), lir->mir()->initialHeap(), &fail);
     masm.jump(&call);
     {
         masm.bind(&fail);
         masm.movePtr(ImmPtr(nullptr), temp1);
     }
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -24,17 +24,19 @@
 #include "vm/RegExpStatics.h"
 #include "vm/TraceLogging.h"
 
 #include "jsopcodeinlines.h"
 #include "jsscriptinlines.h"
 
 #include "jit/CompileInfo-inl.h"
 #include "vm/NativeObject-inl.h"
+#include "vm/ObjectGroup-inl.h"
 #include "vm/ScopeObject-inl.h"
+#include "vm/UnboxedObject-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::AssertedCast;
 using mozilla::DebugOnly;
 using mozilla::Maybe;
 
@@ -6612,17 +6614,17 @@ IonBuilder::jsop_initelem_array()
     current->add(id);
 
     // Get the elements vector.
     MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC);
     current->add(elements);
 
     if (unboxedType != JSVAL_TYPE_MAGIC) {
         // Note: storeUnboxedValue takes care of any post barriers on the value.
-        storeUnboxedValue(obj, elements, 0, id, unboxedType, value);
+        storeUnboxedValue(obj, elements, 0, id, unboxedType, value, /* preBarrier = */ false);
 
         MInstruction* increment = MIncrementUnboxedArrayInitializedLength::New(alloc(), obj);
         current->add(increment);
 
         if (!resumeAfter(increment))
             return false;
     } else {
         if (NeedsPostBarrier(info(), value))
@@ -7094,16 +7096,17 @@ IonBuilder::maybeInsertResume()
     return resumeAfter(ins);
 }
 
 // Return whether property lookups can be performed effectlessly on clasp.
 static bool
 ClassHasEffectlessLookup(const Class* clasp)
 {
     return (clasp == &UnboxedPlainObject::class_) ||
+           (clasp == &UnboxedArrayObject::class_) ||
            IsTypedObjectClass(clasp) ||
            (clasp->isNative() && !clasp->ops.lookupProperty);
 }
 
 // Return whether an object might have a property for name which is not
 // accounted for by type information.
 static bool
 ObjectHasExtraOwnProperty(CompileCompartment* comp, TypeSet::ObjectKey* object, PropertyName* name)
@@ -9102,17 +9105,19 @@ IonBuilder::setElemTryCache(bool* emitte
 
     // We can avoid worrying about holes in the IC if we know a priori we are safe
     // from them. If TI can guard that there are no indexed properties on the prototype
     // chain, we know that we anen't missing any setters by overwriting the hole with
     // another value.
     bool guardHoles = ElementAccessHasExtraIndexedProperty(constraints(), object);
 
     // Make sure the object being written to doesn't have copy on write elements.
-    object = addMaybeCopyElementsForWrite(object);
+    const Class* clasp = object->resultTypeSet() ? object->resultTypeSet()->getKnownClass(constraints()) : nullptr;
+    bool checkNative = !clasp || !clasp->isNative();
+    object = addMaybeCopyElementsForWrite(object, checkNative);
 
     if (NeedsPostBarrier(info(), value))
         current->add(MPostWriteBarrier::New(alloc(), object, value));
 
     // Emit SetElementCache.
     bool strict = JSOp(*pc) == JSOP_STRICTSETELEM;
     MInstruction* ins = MSetElementCache::New(alloc(), object, index, value, strict, guardHoles);
     current->add(ins);
@@ -9145,17 +9150,17 @@ IonBuilder::jsop_setelem_dense(Temporary
         current->add(MPostWriteBarrier::New(alloc(), obj, value));
 
     // Ensure id is an integer.
     MInstruction* idInt32 = MToInt32::New(alloc(), id);
     current->add(idInt32);
     id = idInt32;
 
     // Copy the elements vector if necessary.
-    obj = addMaybeCopyElementsForWrite(obj);
+    obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
 
     // Get the elements vector.
     MElements* elements = MElements::New(alloc(), obj, unboxedType != JSVAL_TYPE_MAGIC);
     current->add(elements);
 
     // Ensure the value is a double, if double conversion might be needed.
     MDefinition* newValue = value;
     switch (conversion) {
@@ -11496,17 +11501,17 @@ IonBuilder::storeUnboxedProperty(MDefini
 
     return storeUnboxedValue(obj, obj, UnboxedPlainObject::offsetOfData(),
                              scaledOffset, unboxedType, value);
 }
 
 MInstruction*
 IonBuilder::storeUnboxedValue(MDefinition* obj, MDefinition* elements, int32_t elementsOffset,
                               MDefinition* scaledOffset, JSValueType unboxedType,
-                              MDefinition* value)
+                              MDefinition* value, bool preBarrier /* = true */)
 {
     MInstruction* store;
     switch (unboxedType) {
       case JSVAL_TYPE_BOOLEAN:
         store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Uint8,
                                          DoesNotRequireMemoryBarrier, elementsOffset);
         break;
 
@@ -11516,22 +11521,23 @@ IonBuilder::storeUnboxedValue(MDefinitio
         break;
 
       case JSVAL_TYPE_DOUBLE:
         store = MStoreUnboxedScalar::New(alloc(), elements, scaledOffset, value, Scalar::Float64,
                                          DoesNotRequireMemoryBarrier, elementsOffset);
         break;
 
       case JSVAL_TYPE_STRING:
-        store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value, elementsOffset);
+        store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value,
+                                         elementsOffset, preBarrier);
         break;
 
       case JSVAL_TYPE_OBJECT:
         store = MStoreUnboxedObjectOrNull::New(alloc(), elements, scaledOffset, value, obj,
-                                               elementsOffset);
+                                               elementsOffset, preBarrier);
         break;
 
       default:
         MOZ_CRASH();
     }
 
     current->add(store);
     return store;
@@ -12526,21 +12532,21 @@ MInstruction*
 IonBuilder::addConvertElementsToDoubles(MDefinition* elements)
 {
     MInstruction* convert = MConvertElementsToDoubles::New(alloc(), elements);
     current->add(convert);
     return convert;
 }
 
 MDefinition*
-IonBuilder::addMaybeCopyElementsForWrite(MDefinition* object)
+IonBuilder::addMaybeCopyElementsForWrite(MDefinition* object, bool checkNative)
 {
     if (!ElementAccessMightBeCopyOnWrite(constraints(), object))
         return object;
-    MInstruction* copy = MMaybeCopyElementsForWrite::New(alloc(), object);
+    MInstruction* copy = MMaybeCopyElementsForWrite::New(alloc(), object, checkNative);
     current->add(copy);
     return copy;
 }
 
 MInstruction*
 IonBuilder::addBoundsCheck(MDefinition* index, MDefinition* length)
 {
     MInstruction* check = MBoundsCheck::New(alloc(), index, length);
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -390,17 +390,17 @@ class IonBuilder
     MDefinition* createThisScriptedBaseline(MDefinition* callee);
     MDefinition* createThis(JSFunction* target, MDefinition* callee);
     MInstruction* createDeclEnvObject(MDefinition* callee, MDefinition* scopeObj);
     MInstruction* createCallObject(MDefinition* callee, MDefinition* scopeObj);
 
     MDefinition* walkScopeChain(unsigned hops);
 
     MInstruction* addConvertElementsToDoubles(MDefinition* elements);
-    MDefinition* addMaybeCopyElementsForWrite(MDefinition* object);
+    MDefinition* addMaybeCopyElementsForWrite(MDefinition* object, bool checkNative);
     MInstruction* addBoundsCheck(MDefinition* index, MDefinition* length);
     MInstruction* addShapeGuard(MDefinition* obj, Shape* const shape, BailoutKind bailoutKind);
     MInstruction* addGroupGuard(MDefinition* obj, ObjectGroup* group, BailoutKind bailoutKind);
     MInstruction* addUnboxedExpandoGuard(MDefinition* obj, bool hasExpando, BailoutKind bailoutKind);
 
     MInstruction*
     addGuardReceiverPolymorphic(MDefinition* obj, const BaselineInspector::ReceiverVector& receivers);
 
@@ -954,17 +954,17 @@ class IonBuilder
     MInstruction* loadUnboxedValue(MDefinition* elements, size_t elementsOffset,
                                    MDefinition* scaledOffset, JSValueType unboxedType,
                                    BarrierKind barrier, TemporaryTypeSet* types);
     MInstruction* storeUnboxedProperty(MDefinition* obj, size_t offset, JSValueType unboxedType,
                                        MDefinition* value);
     MInstruction* storeUnboxedValue(MDefinition* obj,
                                     MDefinition* elements, int32_t elementsOffset,
                                     MDefinition* scaledOffset, JSValueType unboxedType,
-                                    MDefinition* value);
+                                    MDefinition* value, bool preBarrier = true);
     bool checkPreliminaryGroups(MDefinition *obj);
     bool freezePropTypeSets(TemporaryTypeSet* types,
                             JSObject* foundProto, PropertyName* name);
     bool canInlinePropertyOpShapes(const BaselineInspector::ReceiverVector& receivers);
 
     TemporaryTypeSet* bytecodeTypes(jsbytecode* pc);
 
     // Use one of the below methods for updating the current block, rather than
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -623,16 +623,19 @@ TestMatchingReceiver(MacroAssembler& mas
                                     &success);
             masm.pop(object);
             masm.jump(failure);
             masm.bind(&success);
             masm.pop(object);
         } else {
             masm.branchPtr(Assembler::NotEqual, expandoAddress, ImmWord(0), failure);
         }
+    } else if (obj->is<UnboxedArrayObject>()) {
+        MOZ_ASSERT(failure);
+        masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failure);
     } else if (obj->is<TypedObject>()) {
         attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
                                        Address(object, JSObject::offsetOfGroup()),
                                        ImmGCPtr(obj->group()), failure);
     } else {
         Shape* shape = obj->maybeShape();
         MOZ_ASSERT(shape);
 
@@ -1149,16 +1152,50 @@ GenerateArrayLength(JSContext* cx, Macro
 
     /* Failure. */
     masm.bind(&failures);
     attacher.jumpNextStub(masm);
 
     return true;
 }
 
+static void
+GenerateUnboxedArrayLength(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
+                           JSObject* array, Register object, TypedOrValueRegister output)
+{
+    Label failures;
+
+    Register outReg;
+    if (output.hasValue()) {
+        outReg = output.valueReg().scratchReg();
+    } else {
+        MOZ_ASSERT(output.type() == MIRType_Int32);
+        outReg = output.typedReg().gpr();
+    }
+    MOZ_ASSERT(object != outReg);
+
+    TestMatchingReceiver(masm, attacher, object, array, &failures);
+
+    // Load length.
+    masm.load32(Address(object, UnboxedArrayObject::offsetOfLength()), outReg);
+
+    // Check for a length that fits in an int32.
+    masm.branchTest32(Assembler::Signed, outReg, outReg, &failures);
+
+    if (output.hasValue())
+        masm.tagValue(JSVAL_TYPE_INT32, outReg, output.valueReg());
+
+    // Success.
+    attacher.jumpRejoin(masm);
+
+    // Failure.
+    masm.bind(&failures);
+    attacher.jumpNextStub(masm);
+}
+
 // In this case, the code for TypedArray and SharedTypedArray is not the same,
 // because the code embeds pointers to the respective class arrays.  Code that
 // caches the stub code must distinguish between the two cases.
 static void
 GenerateTypedArrayLength(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
                          const TypedArrayLayout& layout, Register object, TypedOrValueRegister output)
 {
     Label failures;
@@ -1239,17 +1276,17 @@ CanAttachNativeGetProp(JSContext* cx, co
         IsCacheableNoProperty(obj, holder, shape, pc, cache.output()))
     {
         return GetPropertyIC::CanAttachReadSlot;
     }
 
     // |length| is a non-configurable getter property on ArrayObjects. Any time this
     // check would have passed, we can install a getter stub instead. Allow people to
     // make that decision themselves with skipArrayLen
-    if (!skipArrayLen && cx->names().length == name && cache.allowArrayLength(cx, obj) &&
+    if (!skipArrayLen && cx->names().length == name && cache.allowArrayLength(cx) &&
         IsCacheableArrayLength(cx, obj, name, cache.output()))
     {
         // The array length property is non-configurable, which means both that
         // checking the class of the object and the name of the property is enough
         // and that we don't need to worry about monitoring, since we know the
         // return type statically.
         return GetPropertyIC::CanAttachArrayLength;
     }
@@ -1272,17 +1309,17 @@ CanAttachNativeGetProp(JSContext* cx, co
         // effectful. This is handled by allowGetters()
         return GetPropertyIC::CanAttachCallGetter;
     }
 
     return GetPropertyIC::CanAttachNone;
 }
 
 bool
-GetPropertyIC::allowArrayLength(JSContext* cx, HandleObject obj) const
+GetPropertyIC::allowArrayLength(JSContext* cx) const
 {
     if (!idempotent())
         return true;
 
     uint32_t locationIndex, numLocations;
     getLocationInfo(&locationIndex, &numLocations);
 
     IonScript* ion = GetTopJitJSScript(cx)->ionScript();
@@ -1398,16 +1435,43 @@ GetPropertyIC::tryAttachUnboxedExpando(J
 
     StubAttacher attacher(*this);
     GenerateReadSlot(cx, ion, masm, attacher, obj, obj,
                      shape, object(), output());
     return linkAndAttachStub(cx, masm, attacher, ion, "read unboxed expando");
 }
 
 bool
+GetPropertyIC::tryAttachUnboxedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
+                                           HandleObject obj, HandlePropertyName name,
+                                           void* returnAddr, bool* emitted)
+{
+    MOZ_ASSERT(canAttachStub());
+    MOZ_ASSERT(!*emitted);
+    MOZ_ASSERT(outerScript->ionScript() == ion);
+
+    if (!obj->is<UnboxedArrayObject>())
+        return true;
+
+    if (cx->names().length != name)
+        return true;
+
+    if (obj->as<UnboxedArrayObject>().length() > INT32_MAX)
+        return true;
+
+    *emitted = true;
+
+    MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
+
+    StubAttacher attacher(*this);
+    GenerateUnboxedArrayLength(cx, masm, attacher, obj, object(), output());
+    return linkAndAttachStub(cx, masm, attacher, ion, "unboxed array length");
+}
+
+bool
 GetPropertyIC::tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
                                          HandleObject obj, HandlePropertyName name, bool* emitted)
 {
     MOZ_ASSERT(canAttachStub());
     MOZ_ASSERT(!*emitted);
 
     if (!IsAnyTypedArray(obj))
         return true;
@@ -1836,16 +1900,19 @@ GetPropertyIC::tryAttachStub(JSContext* 
         return false;
 
     if (!*emitted && !tryAttachUnboxed(cx, outerScript, ion, obj, name, returnAddr, emitted))
         return false;
 
     if (!*emitted && !tryAttachUnboxedExpando(cx, outerScript, ion, obj, name, returnAddr, emitted))
         return false;
 
+    if (!*emitted && !tryAttachUnboxedArrayLength(cx, outerScript, ion, obj, name, returnAddr, emitted))
+        return false;
+
     if (!*emitted && !tryAttachTypedArrayLength(cx, outerScript, ion, obj, name, emitted))
         return false;
 
     if (!*emitted)
         JitSpew(JitSpew_IonIC, "Failed to attach GETPROP cache");
 
     return true;
 }
@@ -3565,66 +3632,73 @@ GetElementIC::attachDenseElementHole(JSC
     MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     StubAttacher attacher(*this);
     GenerateDenseElementHole(cx, masm, attacher, ion, obj, idval, object(), index(), output());
 
     return linkAndAttachStub(cx, masm, attacher, ion, "dense hole");
 }
 
 /* static */ bool
-GetElementIC::canAttachTypedArrayElement(JSObject* obj, const Value& idval,
-                                         TypedOrValueRegister output)
+GetElementIC::canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& idval,
+                                                  TypedOrValueRegister output)
 {
-    if (!IsAnyTypedArray(obj))
+    if (!IsAnyTypedArray(obj) && !obj->is<UnboxedArrayObject>())
         return false;
 
     if (!idval.isInt32() && !idval.isString())
         return false;
 
-
     // Don't emit a stub if the access is out of bounds. We make to make
     // certain that we monitor the type coming out of the typed array when
     // we generate the stub. Out of bounds accesses will hit the fallback
     // path.
     uint32_t index;
     if (idval.isInt32()) {
         index = idval.toInt32();
     } else {
         index = GetIndexFromString(idval.toString());
         if (index == UINT32_MAX)
             return false;
     }
-    if (index >= AnyTypedArrayLength(obj))
+
+    if (IsAnyTypedArray(obj)) {
+        if (index >= AnyTypedArrayLength(obj))
+            return false;
+
+        // The output register is not yet specialized as a float register, the only
+        // way to accept float typed arrays for now is to return a Value type.
+        uint32_t arrayType = AnyTypedArrayType(obj);
+        if (arrayType == Scalar::Float32 || arrayType == Scalar::Float64)
+            return output.hasValue();
+
+        return output.hasValue() || !output.typedReg().isFloat();
+    }
+
+    if (index >= obj->as<UnboxedArrayObject>().initializedLength())
         return false;
 
-    // The output register is not yet specialized as a float register, the only
-    // way to accept float typed arrays for now is to return a Value type.
-    uint32_t arrayType = AnyTypedArrayType(obj);
-    if (arrayType == Scalar::Float32 || arrayType == Scalar::Float64)
+    JSValueType elementType = obj->as<UnboxedArrayObject>().elementType();
+    if (elementType == JSVAL_TYPE_DOUBLE)
         return output.hasValue();
 
     return output.hasValue() || !output.typedReg().isFloat();
 }
 
 static void
-GenerateGetTypedArrayElement(JSContext* cx, MacroAssembler& masm, IonCache::StubAttacher& attacher,
-                             HandleObject tarr, const Value& idval, Register object,
-                             ConstantOrRegister index, TypedOrValueRegister output,
-                             bool allowDoubleResult)
+GenerateGetTypedOrUnboxedArrayElement(JSContext* cx, MacroAssembler& masm,
+                                      IonCache::StubAttacher& attacher,
+                                      HandleObject array, const Value& idval, Register object,
+                                      ConstantOrRegister index, TypedOrValueRegister output,
+                                      bool allowDoubleResult)
 {
-    MOZ_ASSERT(GetElementIC::canAttachTypedArrayElement(tarr, idval, output));
+    MOZ_ASSERT(GetElementIC::canAttachTypedOrUnboxedArrayElement(array, idval, output));
 
     Label failures;
 
-    // The array type is the object within the table of typed array classes.
-    Scalar::Type arrayType = AnyTypedArrayType(tarr);
-
-    // Guard on the shape.
-    Shape* shape = AnyTypedArrayShape(tarr);
-    masm.branchTestObjShape(Assembler::NotEqual, object, shape, &failures);
+    TestMatchingReceiver(masm, attacher, object, array, &failures);
 
     // Decide to what type index the stub should be optimized
     Register tmpReg = output.scratchReg().gpr();
     MOZ_ASSERT(tmpReg != InvalidReg);
     Register indexReg = tmpReg;
     MOZ_ASSERT(!index.constant());
     if (idval.isString()) {
         MOZ_ASSERT(GetIndexFromString(idval.toString()) != UINT32_MAX);
@@ -3670,58 +3744,80 @@ GenerateGetTypedArrayElement(JSContext* 
             // Unbox the index.
             masm.unboxInt32(val, indexReg);
         } else {
             MOZ_ASSERT(!index.reg().typedReg().isFloat());
             indexReg = index.reg().typedReg().gpr();
         }
     }
 
-    // Guard on the initialized length.
-    Address length(object, TypedArrayLayout::lengthOffset());
-    masm.branch32(Assembler::BelowOrEqual, length, indexReg, &failures);
-
-    // Save the object register on the stack in case of failure.
-    Label popAndFail;
-    Register elementReg = object;
-    masm.push(object);
-
-    // Load elements vector.
-    masm.loadPtr(Address(object, TypedArrayLayout::dataOffset()), elementReg);
-
-    // Load the value. We use an invalid register because the destination
-    // register is necessary a non double register.
-    int width = Scalar::byteSize(arrayType);
-    BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(width));
-    if (output.hasValue()) {
-        masm.loadFromTypedArray(arrayType, source, output.valueReg(), allowDoubleResult,
-                                elementReg, &popAndFail);
+    Label popObjectAndFail;
+
+    if (IsAnyTypedArray(array)) {
+        // Guard on the initialized length.
+        Address length(object, TypedArrayLayout::lengthOffset());
+        masm.branch32(Assembler::BelowOrEqual, length, indexReg, &failures);
+
+        // Save the object register on the stack in case of failure.
+        Register elementReg = object;
+        masm.push(object);
+
+        // Load elements vector.
+        masm.loadPtr(Address(object, TypedArrayLayout::dataOffset()), elementReg);
+
+        // Load the value. We use an invalid register because the destination
+        // register is necessary a non double register.
+        Scalar::Type arrayType = AnyTypedArrayType(array);
+        int width = Scalar::byteSize(arrayType);
+        BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(width));
+        if (output.hasValue()) {
+            masm.loadFromTypedArray(arrayType, source, output.valueReg(), allowDoubleResult,
+                                    elementReg, &popObjectAndFail);
+        } else {
+            masm.loadFromTypedArray(arrayType, source, output.typedReg(), elementReg, &popObjectAndFail);
+        }
     } else {
-        masm.loadFromTypedArray(arrayType, source, output.typedReg(), elementReg, &popAndFail);
+        // Save the object register on the stack in case of failure.
+        masm.push(object);
+
+        // Guard on the initialized length.
+        masm.load32(Address(object, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()), object);
+        masm.and32(Imm32(UnboxedArrayObject::InitializedLengthMask), object);
+        masm.branch32(Assembler::BelowOrEqual, object, indexReg, &popObjectAndFail);
+
+        // Load elements vector.
+        Register elementReg = object;
+        masm.loadPtr(Address(masm.getStackPointer(), 0), object);
+        masm.loadPtr(Address(object, UnboxedArrayObject::offsetOfElements()), elementReg);
+
+        JSValueType elementType = array->as<UnboxedArrayObject>().elementType();
+        BaseIndex source(elementReg, indexReg, ScaleFromElemWidth(UnboxedTypeSize(elementType)));
+        masm.loadUnboxedProperty(source, elementType, output);
     }
 
     masm.pop(object);
     attacher.jumpRejoin(masm);
 
     // Restore the object before continuing to the next stub.
-    masm.bind(&popAndFail);
+    masm.bind(&popObjectAndFail);
     masm.pop(object);
     masm.bind(&failures);
 
     attacher.jumpNextStub(masm);
 }
 
 bool
-GetElementIC::attachTypedArrayElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                      HandleObject tarr, const Value& idval)
+GetElementIC::attachTypedOrUnboxedArrayElement(JSContext* cx, HandleScript outerScript,
+                                               IonScript* ion, HandleObject tarr,
+                                               const Value& idval)
 {
     MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     StubAttacher attacher(*this);
-    GenerateGetTypedArrayElement(cx, masm, attacher, tarr, idval, object(), index(), output(),
-                                 allowDoubleResult());
+    GenerateGetTypedOrUnboxedArrayElement(cx, masm, attacher, tarr, idval, object(), index(),
+                                          output(), allowDoubleResult());
     return linkAndAttachStub(cx, masm, attacher, ion, "typed array");
 }
 
 bool
 GetElementIC::attachArgumentsElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
                                      HandleObject obj)
 {
     MOZ_ASSERT(obj->is<ArgumentsObject>());
@@ -3882,18 +3978,18 @@ GetElementIC::update(JSContext* cx, Hand
         }
         if (!attachedStub && cache.monitoredResult() &&
             canAttachDenseElementHole(obj, idval, cache.output()))
         {
             if (!cache.attachDenseElementHole(cx, outerScript, ion, obj, idval))
                 return false;
             attachedStub = true;
         }
-        if (!attachedStub && canAttachTypedArrayElement(obj, idval, cache.output())) {
-            if (!cache.attachTypedArrayElement(cx, outerScript, ion, obj, idval))
+        if (!attachedStub && canAttachTypedOrUnboxedArrayElement(obj, idval, cache.output())) {
+            if (!cache.attachTypedOrUnboxedArrayElement(cx, outerScript, ion, obj, idval))
                 return false;
             attachedStub = true;
         }
     }
 
     if (!GetObjectElementOperation(cx, JSOp(*pc), obj, obj, idval, res))
         return false;
 
--- a/js/src/jit/IonCaches.h
+++ b/js/src/jit/IonCaches.h
@@ -462,17 +462,17 @@ class GetPropertyIC : public IonCache
     enum NativeGetPropCacheability {
         CanAttachNone,
         CanAttachReadSlot,
         CanAttachArrayLength,
         CanAttachCallGetter
     };
 
     // Helpers for CanAttachNativeGetProp
-    bool allowArrayLength(JSContext* cx, HandleObject obj) const;
+    bool allowArrayLength(JSContext* cx) const;
     bool allowGetters() const {
         return monitoredResult() && !idempotent();
     }
 
     // Attach the proper stub, if possible
     bool tryAttachStub(JSContext* cx, HandleScript outerScript, IonScript* ion,
                        HandleObject obj, HandlePropertyName name, bool* emitted);
 
@@ -498,16 +498,20 @@ class GetPropertyIC : public IonCache
     bool tryAttachUnboxed(JSContext* cx, HandleScript outerScript, IonScript* ion,
                           HandleObject obj, HandlePropertyName name,
                           void* returnAddr, bool* emitted);
 
     bool tryAttachUnboxedExpando(JSContext* cx, HandleScript outerScript, IonScript* ion,
                                  HandleObject obj, HandlePropertyName name,
                                  void* returnAddr, bool* emitted);
 
+    bool tryAttachUnboxedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
+                                     HandleObject obj, HandlePropertyName name,
+                                     void* returnAddr, bool* emitted);
+
     bool tryAttachTypedArrayLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
                                    HandleObject obj, HandlePropertyName name, bool* emitted);
 
     bool tryAttachArgumentsLength(JSContext* cx, HandleScript outerScript, IonScript* ion,
                                   HandleObject obj, HandlePropertyName name, bool* emitted);
 
     static bool update(JSContext* cx, HandleScript outerScript, size_t cacheIndex,
                        HandleObject obj, MutableHandleValue vp);
@@ -663,39 +667,39 @@ class GetElementIC : public IonCache
     void setHasDenseStub() {
         MOZ_ASSERT(!hasDenseStub());
         hasDenseStub_ = true;
     }
 
     // Helpers for CanAttachNativeGetProp
     typedef JSContext * Context;
     bool allowGetters() const { MOZ_ASSERT(!idempotent()); return true; }
-    bool allowArrayLength(Context, HandleObject) const { return false; }
+    bool allowArrayLength(Context) const { return false; }
     bool canMonitorSingletonUndefinedSlot(HandleObject holder, HandleShape shape) const {
         return monitoredResult();
     }
 
     static bool canAttachGetProp(JSObject* obj, const Value& idval, jsid id);
     static bool canAttachDenseElement(JSObject* obj, const Value& idval);
     static bool canAttachDenseElementHole(JSObject* obj, const Value& idval,
                                           TypedOrValueRegister output);
-    static bool canAttachTypedArrayElement(JSObject* obj, const Value& idval,
-                                           TypedOrValueRegister output);
+    static bool canAttachTypedOrUnboxedArrayElement(JSObject* obj, const Value& idval,
+                                                    TypedOrValueRegister output);
 
     bool attachGetProp(JSContext* cx, HandleScript outerScript, IonScript* ion,
                        HandleObject obj, const Value& idval, HandlePropertyName name);
 
     bool attachDenseElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
                             HandleObject obj, const Value& idval);
 
     bool attachDenseElementHole(JSContext* cx, HandleScript outerScript, IonScript* ion,
                                 HandleObject obj, const Value& idval);
 
-    bool attachTypedArrayElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
-                                 HandleObject tarr, const Value& idval);
+    bool attachTypedOrUnboxedArrayElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
+                                          HandleObject tarr, const Value& idval);
 
     bool attachArgumentsElement(JSContext* cx, HandleScript outerScript, IonScript* ion,
                                 HandleObject obj);
 
     static bool
     update(JSContext* cx, HandleScript outerScript, size_t cacheIndex, HandleObject obj,
            HandleValue idval, MutableHandleValue vp);
 
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -4137,16 +4137,20 @@ class LMaybeCopyElementsForWrite : publi
 
     const LAllocation* object() {
         return getOperand(0);
     }
 
     const LDefinition* temp() {
         return getTemp(0);
     }
+
+    const MMaybeCopyElementsForWrite* mir() const {
+        return mir_->toMaybeCopyElementsForWrite();
+    }
 };
 
 // Load the initialized length from an elements header.
 class LInitializedLength : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(InitializedLength)
 
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -662,42 +662,54 @@ IonBuilder::inlineArrayPopShift(CallInfo
     // deleted properties in active iterators.
     ObjectGroupFlags unhandledFlags =
         OBJECT_FLAG_SPARSE_INDEXES |
         OBJECT_FLAG_LENGTH_OVERFLOW |
         OBJECT_FLAG_ITERATED;
 
     MDefinition* obj = callInfo.thisArg();
     TemporaryTypeSet* thisTypes = obj->resultTypeSet();
-    if (!thisTypes || thisTypes->getKnownClass(constraints()) != &ArrayObject::class_)
+    if (!thisTypes)
+        return InliningStatus_NotInlined;
+    const Class* clasp = thisTypes->getKnownClass(constraints());
+    if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
         return InliningStatus_NotInlined;
     if (thisTypes->hasObjectFlags(constraints(), unhandledFlags)) {
         trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
         return InliningStatus_NotInlined;
     }
 
     if (ArrayPrototypeHasIndexedProperty(constraints(), script())) {
         trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
         return InliningStatus_NotInlined;
     }
 
+    JSValueType unboxedType = JSVAL_TYPE_MAGIC;
+    if (clasp == &UnboxedArrayObject::class_) {
+        unboxedType = UnboxedArrayElementType(constraints(), obj, nullptr);
+        if (unboxedType == JSVAL_TYPE_MAGIC)
+            return InliningStatus_NotInlined;
+    }
+
     callInfo.setImplicitlyUsedUnchecked();
 
-    obj = addMaybeCopyElementsForWrite(obj);
+    if (clasp == &ArrayObject::class_)
+        obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
 
     TemporaryTypeSet* returnTypes = getInlineReturnTypeSet();
     bool needsHoleCheck = thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_NON_PACKED);
     bool maybeUndefined = returnTypes->hasType(TypeSet::UndefinedType());
 
     BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(),
                                                        obj, nullptr, returnTypes);
     if (barrier != BarrierKind::NoBarrier)
         returnType = MIRType_Value;
 
-    MArrayPopShift* ins = MArrayPopShift::New(alloc(), obj, mode, needsHoleCheck, maybeUndefined);
+    MArrayPopShift* ins = MArrayPopShift::New(alloc(), obj, mode,
+                                              unboxedType, needsHoleCheck, maybeUndefined);
     current->add(ins);
     current->push(ins);
     ins->setResultType(returnType);
 
     if (!resumeAfter(ins))
         return InliningStatus_Error;
 
     if (!pushTypeBarrier(ins, returnTypes, barrier))
@@ -790,17 +802,20 @@ IonBuilder::inlineArrayPush(CallInfo& ca
     MOZ_ASSERT(obj == callInfo.thisArg() && value == callInfo.getArg(0));
 
     if (getInlineReturnType() != MIRType_Int32)
         return InliningStatus_NotInlined;
     if (callInfo.thisArg()->type() != MIRType_Object)
         return InliningStatus_NotInlined;
 
     TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet();
-    if (!thisTypes || thisTypes->getKnownClass(constraints()) != &ArrayObject::class_)
+    if (!thisTypes)
+        return InliningStatus_NotInlined;
+    const Class* clasp = thisTypes->getKnownClass(constraints());
+    if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
         return InliningStatus_NotInlined;
     if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
                                   OBJECT_FLAG_LENGTH_OVERFLOW))
     {
         trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
         return InliningStatus_NotInlined;
     }
 
@@ -811,33 +826,41 @@ IonBuilder::inlineArrayPush(CallInfo& ca
 
     TemporaryTypeSet::DoubleConversion conversion =
         thisTypes->convertDoubleElements(constraints());
     if (conversion == TemporaryTypeSet::AmbiguousDoubleConversion) {
         trackOptimizationOutcome(TrackedOutcome::ArrayDoubleConversion);
         return InliningStatus_NotInlined;
     }
 
+    JSValueType unboxedType = JSVAL_TYPE_MAGIC;
+    if (clasp == &UnboxedArrayObject::class_) {
+        unboxedType = UnboxedArrayElementType(constraints(), callInfo.thisArg(), nullptr);
+        if (unboxedType == JSVAL_TYPE_MAGIC)
+            return InliningStatus_NotInlined;
+    }
+
     callInfo.setImplicitlyUsedUnchecked();
     value = callInfo.getArg(0);
 
     if (conversion == TemporaryTypeSet::AlwaysConvertToDoubles ||
         conversion == TemporaryTypeSet::MaybeConvertToDoubles)
     {
         MInstruction* valueDouble = MToDouble::New(alloc(), value);
         current->add(valueDouble);
         value = valueDouble;
     }
 
-    obj = addMaybeCopyElementsForWrite(obj);
+    if (unboxedType == JSVAL_TYPE_MAGIC)
+        obj = addMaybeCopyElementsForWrite(obj, /* checkNative = */ false);
 
     if (NeedsPostBarrier(info(), value))
         current->add(MPostWriteBarrier::New(alloc(), obj, value));
 
-    MArrayPush* ins = MArrayPush::New(alloc(), obj, value);
+    MArrayPush* ins = MArrayPush::New(alloc(), obj, value, unboxedType);
     current->add(ins);
     current->push(ins);
 
     if (!resumeAfter(ins))
         return InliningStatus_Error;
     return InliningStatus_Inlined;
 }
 
@@ -858,34 +881,44 @@ IonBuilder::inlineArrayConcat(CallInfo& 
         return InliningStatus_NotInlined;
 
     // |this| and the argument must be dense arrays.
     TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet();
     TemporaryTypeSet* argTypes = callInfo.getArg(0)->resultTypeSet();
     if (!thisTypes || !argTypes)
         return InliningStatus_NotInlined;
 
-    if (thisTypes->getKnownClass(constraints()) != &ArrayObject::class_)
+    const Class* clasp = thisTypes->getKnownClass(constraints());
+    if (clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_)
         return InliningStatus_NotInlined;
     if (thisTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
                                   OBJECT_FLAG_LENGTH_OVERFLOW))
     {
         trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
         return InliningStatus_NotInlined;
     }
 
-    if (argTypes->getKnownClass(constraints()) != &ArrayObject::class_)
+    if (argTypes->getKnownClass(constraints()) != clasp)
         return InliningStatus_NotInlined;
     if (argTypes->hasObjectFlags(constraints(), OBJECT_FLAG_SPARSE_INDEXES |
                                  OBJECT_FLAG_LENGTH_OVERFLOW))
     {
         trackOptimizationOutcome(TrackedOutcome::ArrayBadFlags);
         return InliningStatus_NotInlined;
     }
 
+    JSValueType unboxedType = JSVAL_TYPE_MAGIC;
+    if (clasp == &UnboxedArrayObject::class_) {
+        unboxedType = UnboxedArrayElementType(constraints(), callInfo.thisArg(), nullptr);
+        if (unboxedType == JSVAL_TYPE_MAGIC)
+            return InliningStatus_NotInlined;
+        if (unboxedType != UnboxedArrayElementType(constraints(), callInfo.getArg(0), nullptr))
+            return InliningStatus_NotInlined;
+    }
+
     // Watch out for indexed properties on the prototype.
     if (ArrayPrototypeHasIndexedProperty(constraints(), script())) {
         trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
         return InliningStatus_NotInlined;
     }
 
     // Require the 'this' types to have a specific type matching the current
     // global, so we can create the result object inline.
@@ -929,23 +962,24 @@ IonBuilder::inlineArrayConcat(CallInfo& 
         if (!elemTypes.knownSubset(constraints(), thisElemTypes))
             return InliningStatus_NotInlined;
     }
 
     // Inline the call.
     JSObject* templateObj = inspector->getTemplateObjectForNative(pc, js::array_concat);
     if (!templateObj || templateObj->group() != thisGroup)
         return InliningStatus_NotInlined;
-    MOZ_ASSERT(templateObj->is<ArrayObject>());
 
     callInfo.setImplicitlyUsedUnchecked();
 
-    MArrayConcat* ins = MArrayConcat::New(alloc(), constraints(), callInfo.thisArg(), callInfo.getArg(0),
-                                          &templateObj->as<ArrayObject>(),
-                                          templateObj->group()->initialHeap(constraints()));
+    MArrayConcat* ins = MArrayConcat::New(alloc(), constraints(),
+                                          callInfo.thisArg(), callInfo.getArg(0),
+                                          templateObj,
+                                          templateObj->group()->initialHeap(constraints()),
+                                          unboxedType);
     current->add(ins);
     current->push(ins);
 
     if (!resumeAfter(ins))
         return InliningStatus_Error;
     return InliningStatus_Inlined;
 }
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -355,16 +355,19 @@ class AliasSet {
     static AliasSet Load(uint32_t flags) {
         MOZ_ASSERT(flags && !(flags & Store_));
         return AliasSet(flags);
     }
     static AliasSet Store(uint32_t flags) {
         MOZ_ASSERT(flags && !(flags & Store_));
         return AliasSet(flags | Store_);
     }
+    static uint32_t BoxedOrUnboxedElements(JSValueType type) {
+        return (type == JSVAL_TYPE_MAGIC) ? Element : UnboxedElement;
+    }
 };
 
 // An MDefinition is an SSA name.
 class MDefinition : public MNode
 {
     friend class MBasicBlock;
 
   public:
@@ -7753,37 +7756,43 @@ class MMaybeToDoubleElement
     }
 };
 
 // Passes through an object, after ensuring its elements are not copy on write.
 class MMaybeCopyElementsForWrite
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
-    explicit MMaybeCopyElementsForWrite(MDefinition* object)
-      : MUnaryInstruction(object)
+    bool checkNative_;
+
+    explicit MMaybeCopyElementsForWrite(MDefinition* object, bool checkNative)
+      : MUnaryInstruction(object), checkNative_(checkNative)
     {
         setGuard();
         setMovable();
         setResultType(MIRType_Object);
         setResultTypeSet(object->resultTypeSet());
     }
 
   public:
     INSTRUCTION_HEADER(MaybeCopyElementsForWrite)
 
-    static MMaybeCopyElementsForWrite* New(TempAllocator& alloc, MDefinition* object) {
-        return new(alloc) MMaybeCopyElementsForWrite(object);
+    static MMaybeCopyElementsForWrite* New(TempAllocator& alloc, MDefinition* object, bool checkNative) {
+        return new(alloc) MMaybeCopyElementsForWrite(object, checkNative);
     }
 
     MDefinition* object() const {
         return getOperand(0);
     }
-    bool congruentTo(const MDefinition* ins) const override {
-        return congruentIfOperandsEqual(ins);
+    bool checkNative() const {
+        return checkNative_;
+    }
+    bool congruentTo(const MDefinition* ins) const override {
+        return congruentIfOperandsEqual(ins) &&
+               checkNative() == ins->toMaybeCopyElementsForWrite()->checkNative();
     }
     AliasSet getAliasSet() const override {
         return AliasSet::Store(AliasSet::ObjectFields);
     }
 #ifdef DEBUG
     bool needsResumePoint() const override {
         // This instruction is idempotent and does not change observable
         // behavior, so does not need its own resume point.
@@ -8537,19 +8546,17 @@ class MLoadElementHole
             return false;
         if (needsHoleCheck() != other->needsHoleCheck())
             return false;
         if (needsNegativeIntCheck() != other->needsNegativeIntCheck())
             return false;
         return congruentIfOperandsEqual(other);
     }
     AliasSet getAliasSet() const override {
-        return AliasSet::Load(unboxedType() == JSVAL_TYPE_MAGIC
-                              ? AliasSet::Element
-                              : AliasSet::UnboxedElement);
+        return AliasSet::Load(AliasSet::BoxedOrUnboxedElements(unboxedType()));
     }
     void collectRangeInfoPreTrunc() override;
 
     ALLOW_CLONE(MLoadElementHole)
 };
 
 class MLoadUnboxedObjectOrNull
   : public MBinaryInstruction,
@@ -8799,70 +8806,73 @@ class MStoreElementHole
     }
     JSValueType unboxedType() const {
         return unboxedType_;
     }
     AliasSet getAliasSet() const override {
         // StoreElementHole can update the initialized length, the array length
         // or reallocate obj->elements.
         return AliasSet::Store(AliasSet::ObjectFields |
-                               ((unboxedType() == JSVAL_TYPE_MAGIC)
-                                ? AliasSet::Element
-                                : AliasSet::UnboxedElement));
+                               AliasSet::BoxedOrUnboxedElements(unboxedType()));
     }
 
     ALLOW_CLONE(MStoreElementHole)
 };
 
 // Store an unboxed object or null pointer to a v\ector.
 class MStoreUnboxedObjectOrNull
   : public MAryInstruction<4>,
     public StoreUnboxedObjectOrNullPolicy::Data
 {
     int32_t offsetAdjustment_;
+    bool preBarrier_;
 
     MStoreUnboxedObjectOrNull(MDefinition* elements, MDefinition* index,
                               MDefinition* value, MDefinition* typedObj,
-                              int32_t offsetAdjustment)
-      : offsetAdjustment_(offsetAdjustment)
+                              int32_t offsetAdjustment, bool preBarrier)
+      : offsetAdjustment_(offsetAdjustment), preBarrier_(preBarrier)
     {
         initOperand(0, elements);
         initOperand(1, index);
         initOperand(2, value);
         initOperand(3, typedObj);
         MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
         MOZ_ASSERT(index->type() == MIRType_Int32);
         MOZ_ASSERT(typedObj->type() == MIRType_Object);
     }
 
   public:
     INSTRUCTION_HEADER(StoreUnboxedObjectOrNull)
 
     static MStoreUnboxedObjectOrNull* New(TempAllocator& alloc,
                                           MDefinition* elements, MDefinition* index,
                                           MDefinition* value, MDefinition* typedObj,
-                                          int32_t offsetAdjustment = 0) {
+                                          int32_t offsetAdjustment = 0,
+                                          bool preBarrier = true) {
         return new(alloc) MStoreUnboxedObjectOrNull(elements, index, value, typedObj,
-                                                    offsetAdjustment);
+                                                    offsetAdjustment, preBarrier);
     }
     MDefinition* elements() const {
         return getOperand(0);
     }
     MDefinition* index() const {
         return getOperand(1);
     }
     MDefinition* value() const {
         return getOperand(2);
     }
     MDefinition* typedObj() const {
         return getOperand(3);
     }
     int32_t offsetAdjustment() const {
         return offsetAdjustment_;
     }
+    bool preBarrier() const {
+        return preBarrier_;
+    }
     AliasSet getAliasSet() const override {
         return AliasSet::Store(AliasSet::UnboxedElement);
     }
 
     // For StoreUnboxedObjectOrNullPolicy.
     void setValue(MDefinition* def) {
         replaceOperand(2, def);
     }
@@ -8871,48 +8881,54 @@ class MStoreUnboxedObjectOrNull
 };
 
 // Store an unboxed object or null pointer to a vector.
 class MStoreUnboxedString
   : public MAryInstruction<3>,
     public MixPolicy<SingleObjectPolicy, ConvertToStringPolicy<2> >::Data
 {
     int32_t offsetAdjustment_;
+    bool preBarrier_;
 
     MStoreUnboxedString(MDefinition* elements, MDefinition* index, MDefinition* value,
-                        int32_t offsetAdjustment)
-      : offsetAdjustment_(offsetAdjustment)
+                        int32_t offsetAdjustment, bool preBarrier)
+      : offsetAdjustment_(offsetAdjustment), preBarrier_(preBarrier)
     {
         initOperand(0, elements);
         initOperand(1, index);
         initOperand(2, value);
         MOZ_ASSERT(IsValidElementsType(elements, offsetAdjustment));
         MOZ_ASSERT(index->type() == MIRType_Int32);
     }
 
   public:
     INSTRUCTION_HEADER(StoreUnboxedString)
 
     static MStoreUnboxedString* New(TempAllocator& alloc,
                                     MDefinition* elements, MDefinition* index,
-                                    MDefinition* value, int32_t offsetAdjustment = 0) {
-        return new(alloc) MStoreUnboxedString(elements, index, value, offsetAdjustment);
+                                    MDefinition* value, int32_t offsetAdjustment = 0,
+                                    bool preBarrier = true) {
+        return new(alloc) MStoreUnboxedString(elements, index, value,
+                                              offsetAdjustment, preBarrier);
     }
     MDefinition* elements() const {
         return getOperand(0);
     }
     MDefinition* index() const {
         return getOperand(1);
     }
     MDefinition* value() const {
         return getOperand(2);
     }
     int32_t offsetAdjustment() const {
         return offsetAdjustment_;
     }
+    bool preBarrier() const {
+        return preBarrier_;
+    }
     AliasSet getAliasSet() const override {
         return AliasSet::Store(AliasSet::UnboxedElement);
     }
 
     ALLOW_CLONE(MStoreUnboxedString)
 };
 
 // Passes through an object, after ensuring it is converted from an unboxed
@@ -8977,122 +8993,144 @@ class MArrayPopShift
   public:
     enum Mode {
         Pop,
         Shift
     };
 
   private:
     Mode mode_;
+    JSValueType unboxedType_;
     bool needsHoleCheck_;
     bool maybeUndefined_;
 
-    MArrayPopShift(MDefinition* object, Mode mode, bool needsHoleCheck, bool maybeUndefined)
-      : MUnaryInstruction(object), mode_(mode), needsHoleCheck_(needsHoleCheck),
-        maybeUndefined_(maybeUndefined)
+    MArrayPopShift(MDefinition* object, Mode mode, JSValueType unboxedType,
+                   bool needsHoleCheck, bool maybeUndefined)
+      : MUnaryInstruction(object), mode_(mode), unboxedType_(unboxedType),
+        needsHoleCheck_(needsHoleCheck), maybeUndefined_(maybeUndefined)
     { }
 
   public:
     INSTRUCTION_HEADER(ArrayPopShift)
 
     static MArrayPopShift* New(TempAllocator& alloc, MDefinition* object, Mode mode,
-                               bool needsHoleCheck, bool maybeUndefined)
-    {
-        return new(alloc) MArrayPopShift(object, mode, needsHoleCheck, maybeUndefined);
+                               JSValueType unboxedType, bool needsHoleCheck, bool maybeUndefined)
+    {
+        return new(alloc) MArrayPopShift(object, mode, unboxedType, needsHoleCheck, maybeUndefined);
     }
 
     MDefinition* object() const {
         return getOperand(0);
     }
     bool needsHoleCheck() const {
         return needsHoleCheck_;
     }
     bool maybeUndefined() const {
         return maybeUndefined_;
     }
     bool mode() const {
         return mode_;
     }
-    AliasSet getAliasSet() const override {
-        return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
+    JSValueType unboxedType() const {
+        return unboxedType_;
+    }
+    AliasSet getAliasSet() const override {
+        return AliasSet::Store(AliasSet::ObjectFields |
+                               AliasSet::BoxedOrUnboxedElements(unboxedType()));
     }
 
     ALLOW_CLONE(MArrayPopShift)
 };
 
 // Array.prototype.push on a dense array. Returns the new array length.
 class MArrayPush
   : public MBinaryInstruction,
     public MixPolicy<SingleObjectPolicy, NoFloatPolicy<1> >::Data
 {
-    MArrayPush(MDefinition* object, MDefinition* value)
-      : MBinaryInstruction(object, value)
+    JSValueType unboxedType_;
+
+    MArrayPush(MDefinition* object, MDefinition* value, JSValueType unboxedType)
+      : MBinaryInstruction(object, value), unboxedType_(unboxedType)
     {
         setResultType(MIRType_Int32);
     }
 
   public:
     INSTRUCTION_HEADER(ArrayPush)
 
-    static MArrayPush* New(TempAllocator& alloc, MDefinition* object, MDefinition* value) {
-        return new(alloc) MArrayPush(object, value);
+    static MArrayPush* New(TempAllocator& alloc, MDefinition* object, MDefinition* value,
+                           JSValueType unboxedType) {
+        return new(alloc) MArrayPush(object, value, unboxedType);
     }
 
     MDefinition* object() const {
         return getOperand(0);
     }
     MDefinition* value() const {
         return getOperand(1);
     }
-    AliasSet getAliasSet() const override {
-        return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
+    JSValueType unboxedType() const {
+        return unboxedType_;
+    }
+    AliasSet getAliasSet() const override {
+        return AliasSet::Store(AliasSet::ObjectFields |
+                               AliasSet::BoxedOrUnboxedElements(unboxedType()));
     }
     void computeRange(TempAllocator& alloc) override;
 
     ALLOW_CLONE(MArrayPush)
 };
 
 // Array.prototype.concat on two dense arrays.
 class MArrayConcat
   : public MBinaryInstruction,
     public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
 {
-    AlwaysTenured<ArrayObject*> templateObj_;
+    AlwaysTenuredObject templateObj_;
     gc::InitialHeap initialHeap_;
+    JSValueType unboxedType_;
 
     MArrayConcat(CompilerConstraintList* constraints, MDefinition* lhs, MDefinition* rhs,
-                 ArrayObject* templateObj, gc::InitialHeap initialHeap)
+                 JSObject* templateObj, gc::InitialHeap initialHeap, JSValueType unboxedType)
       : MBinaryInstruction(lhs, rhs),
         templateObj_(templateObj),
-        initialHeap_(initialHeap)
+        initialHeap_(initialHeap),
+        unboxedType_(unboxedType)
     {
         setResultType(MIRType_Object);
         setResultTypeSet(MakeSingletonTypeSet(constraints, templateObj));
     }
 
   public:
     INSTRUCTION_HEADER(ArrayConcat)
 
     static MArrayConcat* New(TempAllocator& alloc, CompilerConstraintList* constraints,
                              MDefinition* lhs, MDefinition* rhs,
-                             ArrayObject* templateObj, gc::InitialHeap initialHeap)
-    {
-        return new(alloc) MArrayConcat(constraints, lhs, rhs, templateObj, initialHeap);
-    }
-
-    ArrayObject* templateObj() const {
+                             JSObject* templateObj, gc::InitialHeap initialHeap,
+                             JSValueType unboxedType)
+    {
+        return new(alloc) MArrayConcat(constraints, lhs, rhs, templateObj,
+                                       initialHeap, unboxedType);
+    }
+
+    JSObject* templateObj() const {
         return templateObj_;
     }
 
     gc::InitialHeap initialHeap() const {
         return initialHeap_;
     }
 
-    AliasSet getAliasSet() const override {
-        return AliasSet::Store(AliasSet::Element | AliasSet::ObjectFields);
+    JSValueType unboxedType() const {
+        return unboxedType_;
+    }
+
+    AliasSet getAliasSet() const override {
+        return AliasSet::Store(AliasSet::BoxedOrUnboxedElements(unboxedType()) |
+                               AliasSet::ObjectFields);
     }
     bool possiblyCalls() const override {
         return true;
     }
 };
 
 class MArrayJoin
     : public MBinaryInstruction,
@@ -10470,17 +10508,18 @@ class MGuardObjectGroup
         bailoutKind_(bailoutKind)
     {
         setGuard();
         setMovable();
         setResultType(MIRType_Object);
 
         // Unboxed groups which might be converted to natives can't be guarded
         // on, due to MConvertUnboxedObjectToNative.
-        MOZ_ASSERT_IF(group->maybeUnboxedLayout(), !group->unboxedLayout().nativeGroup());
+        MOZ_ASSERT_IF(group->maybeUnboxedLayoutDontCheckGeneration(),
+                      !group->unboxedLayoutDontCheckGeneration().nativeGroup());
     }
 
   public:
     INSTRUCTION_HEADER(GuardObjectGroup)
 
     static MGuardObjectGroup* New(TempAllocator& alloc, MDefinition* obj, ObjectGroup* group,
                                   bool bailOnEquality, BailoutKind bailoutKind) {
         return new(alloc) MGuardObjectGroup(obj, group, bailOnEquality, bailoutKind);
--- a/js/src/jit/OptimizationTracking.cpp
+++ b/js/src/jit/OptimizationTracking.cpp
@@ -11,16 +11,19 @@
 #include "jsprf.h"
 
 #include "ds/Sort.h"
 #include "jit/IonBuilder.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitSpewer.h"
 #include "js/TrackedOptimizationInfo.h"
 
+#include "vm/ObjectGroup-inl.h"
+#include "vm/TypeInference-inl.h"
+
 using namespace js;
 using namespace js::jit;
 
 using mozilla::Maybe;
 using mozilla::Some;
 using mozilla::Nothing;
 
 using JS::TrackedStrategy;
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -264,17 +264,17 @@ ArraySpliceDense(JSContext* cx, HandleOb
     argv[3].set(Int32Value(deleteCount));
 
     return js::array_splice_impl(cx, 2, argv.begin(), false);
 }
 
 bool
 ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval)
 {
-    MOZ_ASSERT(obj->is<ArrayObject>());
+    MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
 
     AutoDetectInvalidation adi(cx, rval);
 
     JS::AutoValueArray<2> argv(cx);
     argv[0].setUndefined();
     argv[1].setObject(*obj);
     if (!js::array_pop(cx, 0, argv.begin()))
         return false;
@@ -283,48 +283,41 @@ ArrayPopDense(JSContext* cx, HandleObjec
     // have to monitor the return value.
     rval.set(argv[0]);
     if (rval.isUndefined())
         TypeScript::Monitor(cx, rval);
     return true;
 }
 
 bool
-ArrayPushDense(JSContext* cx, HandleArrayObject obj, HandleValue v, uint32_t* length)
+ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length)
 {
-    if (MOZ_LIKELY(obj->lengthIsWritable())) {
-        uint32_t idx = obj->length();
-        DenseElementResult result = obj->ensureDenseElements(cx, idx, 1);
-        if (result == DenseElementResult::Failure)
-            return false;
-
-        if (result == DenseElementResult::Success) {
-            obj->setDenseElement(idx, v);
-            MOZ_ASSERT(idx < INT32_MAX);
-            *length = idx + 1;
-            obj->setLengthInt32(*length);
-            return true;
-        }
+    *length = GetAnyBoxedOrUnboxedArrayLength(obj);
+    DenseElementResult result =
+        SetOrExtendAnyBoxedOrUnboxedDenseElements(cx, obj, *length, v.address(), 1, DontUpdateTypes);
+    if (result != DenseElementResult::Incomplete) {
+        (*length)++;
+        return result == DenseElementResult::Success;
     }
 
     JS::AutoValueArray<3> argv(cx);
     argv[0].setUndefined();
     argv[1].setObject(*obj);
     argv[2].set(v);
     if (!js::array_push(cx, 1, argv.begin()))
         return false;
 
     *length = argv[0].toInt32();
     return true;
 }
 
 bool
 ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval)
 {
-    MOZ_ASSERT(obj->is<ArrayObject>());
+    MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
 
     AutoDetectInvalidation adi(cx, rval);
 
     JS::AutoValueArray<2> argv(cx);
     argv[0].setUndefined();
     argv[1].setObject(*obj);
     if (!js::array_shift(cx, 0, argv.begin()))
         return false;
@@ -335,31 +328,27 @@ ArrayShiftDense(JSContext* cx, HandleObj
     if (rval.isUndefined())
         TypeScript::Monitor(cx, rval);
     return true;
 }
 
 JSObject*
 ArrayConcatDense(JSContext* cx, HandleObject obj1, HandleObject obj2, HandleObject objRes)
 {
-    Rooted<ArrayObject*> arr1(cx, &obj1->as<ArrayObject>());
-    Rooted<ArrayObject*> arr2(cx, &obj2->as<ArrayObject>());
-    Rooted<ArrayObject*> arrRes(cx, objRes ? &objRes->as<ArrayObject>() : nullptr);
-
-    if (arrRes) {
+    if (objRes) {
         // Fast path if we managed to allocate an object inline.
-        if (!js::array_concat_dense(cx, arr1, arr2, arrRes))
+        if (!js::array_concat_dense(cx, obj1, obj2, objRes))
             return nullptr;
-        return arrRes;
+        return objRes;
     }
 
     JS::AutoValueArray<3> argv(cx);
     argv[0].setUndefined();
-    argv[1].setObject(*arr1);
-    argv[2].setObject(*arr2);
+    argv[1].setObject(*obj1);
+    argv[2].setObject(*obj2);
     if (!js::array_concat(cx, 1, argv.begin()))
         return nullptr;
     return &argv[0].toObject();
 }
 
 JSString*
 ArrayJoin(JSContext* cx, HandleObject array, HandleString sep)
 {
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -653,17 +653,17 @@ bool LessThan(JSContext* cx, MutableHand
 bool LessThanOrEqual(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res);
 bool GreaterThan(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res);
 bool GreaterThanOrEqual(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs, bool* res);
 
 template<bool Equal>
 bool StringsEqual(JSContext* cx, HandleString left, HandleString right, bool* res);
 
 bool ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval);
-bool ArrayPushDense(JSContext* cx, HandleArrayObject obj, HandleValue v, uint32_t* length);
+bool ArrayPushDense(JSContext* cx, HandleObject obj, HandleValue v, uint32_t* length);
 bool ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval);
 JSObject* ArrayConcatDense(JSContext* cx, HandleObject obj1, HandleObject obj2, HandleObject res);
 JSString* ArrayJoin(JSContext* cx, HandleObject array, HandleString sep);
 
 bool CharCodeAt(JSContext* cx, HandleString str, int32_t index, uint32_t* code);
 JSFlatString* StringFromCharCode(JSContext* cx, int32_t code);
 
 bool SetProperty(JSContext* cx, HandleObject obj, HandlePropertyName name, HandleValue value,
--- a/js/src/jit/none/CodeGenerator-none.h
+++ b/js/src/jit/none/CodeGenerator-none.h
@@ -19,17 +19,17 @@ class CodeGeneratorNone : public CodeGen
 
     CodeGeneratorNone(MIRGenerator* gen, LIRGraph* graph, MacroAssembler* masm)
       : CodeGeneratorShared(gen, graph, masm)
     {
         MOZ_CRASH();
     }
 
     template <typename T> inline Register ToOperand(T) { MOZ_CRASH(); }
-    MoveOperand toMoveOperand(const LAllocation*) const { MOZ_CRASH(); }
+    MoveOperand toMoveOperand(const LAllocation) const { MOZ_CRASH(); }
     template <typename T1, typename T2>
     void bailoutCmp32(Assembler::Condition, T1, T2, LSnapshot*) { MOZ_CRASH(); }
     template<typename T>
     void bailoutTest32(Assembler::Condition, Register, T, LSnapshot*) { MOZ_CRASH(); }
     template <typename T1, typename T2>
     void bailoutCmpPtr(Assembler::Condition, T1, T2, LSnapshot*) { MOZ_CRASH(); }
     void bailoutTestPtr(Assembler::Condition, Register, Register, LSnapshot*) { MOZ_CRASH(); }
     void bailoutIfFalseBool(Register, LSnapshot*) { MOZ_CRASH(); }
--- a/js/src/jsapi-tests/testGCHeapPostBarriers.cpp
+++ b/js/src/jsapi-tests/testGCHeapPostBarriers.cpp
@@ -3,16 +3,18 @@
 */
 /* 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 "js/RootingAPI.h"
 #include "jsapi-tests/tests.h"
 
+using mozilla::ScopedDeletePtr;
+
 BEGIN_TEST(testGCHeapPostBarriers)
 {
 #ifdef JS_GC_ZEAL
     AutoLeaveZeal nozeal(cx);
 #endif /* JS_GC_ZEAL */
 
     /* Sanity check - objects start in the nursery and then become tenured. */
     JS_GC(cx->runtime());
@@ -39,18 +41,18 @@ Passthrough(bool value)
 template <typename T>
 bool
 TestHeapPostBarriers(T initialObj)
 {
     CHECK(initialObj != nullptr);
     CHECK(js::gc::IsInsideNursery(initialObj));
 
     /* Construct Heap<> wrapper. */
-    JS::Heap<T>* heapData = new JS::Heap<T>();
-    CHECK(heapData);
+    ScopedDeletePtr<JS::Heap<T>> heapData(new JS::Heap<T>);
+    CHECK(heapData.get());
     CHECK(Passthrough(heapData->get() == nullptr));
     *heapData = initialObj;
 
     /* Store the pointer as an integer so that the hazard analysis will miss it. */
     uintptr_t initialObjAsInt = uintptr_t(initialObj);
 
     /* Perform minor GC and check heap wrapper is udated with new pointer. */
     cx->minorGC(JS::gcreason::API);
@@ -59,17 +61,16 @@ TestHeapPostBarriers(T initialObj)
 
     /* Check object is definitely still alive. */
     JS::Rooted<T> obj(cx, heapData->get());
     JS::RootedValue value(cx);
     CHECK(JS_GetProperty(cx, obj, "x", &value));
     CHECK(value.isInt32());
     CHECK(value.toInt32() == 42);
 
-    delete heapData;
     return true;
 }
 
 JSObject* NurseryObject()
 {
     JS::RootedObject obj(cx, JS_NewPlainObject(cx));
     if (!obj)
         return nullptr;
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -2064,28 +2064,48 @@ js::array_pop(JSContext* cx, unsigned ar
         if (!hole && !DeletePropertyOrThrow(cx, obj, index))
             return false;
     }
 
     /* Steps 4a, 5d. */
     return SetLengthProperty(cx, obj, index);
 }
 
-void
-js::ArrayShiftMoveElements(ArrayObject* obj)
+template <JSValueType Type>
+static inline DenseElementResult
+ShiftMoveBoxedOrUnboxedDenseElements(JSObject* obj)
 {
-    MOZ_ASSERT(obj->lengthIsWritable());
+    MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(obj));
 
     /*
      * At this point the length and initialized length have already been
      * decremented and the result fetched, so just shift the array elements
      * themselves.
      */
-    uint32_t initlen = obj->getDenseInitializedLength();
-    obj->moveDenseElementsNoPreBarrier(0, 1, initlen);
+    size_t initlen = GetBoxedOrUnboxedInitializedLength<Type>(obj);
+    if (Type == JSVAL_TYPE_MAGIC) {
+        obj->as<NativeObject>().moveDenseElementsNoPreBarrier(0, 1, initlen);
+    } else {
+        uint8_t* data = obj->as<UnboxedArrayObject>().elements();
+        size_t elementSize = UnboxedTypeSize(Type);
+        memmove(data, data + elementSize, initlen * elementSize);
+    }
+
+    return DenseElementResult::Success;
+}
+
+DefineBoxedOrUnboxedFunctor1(ShiftMoveBoxedOrUnboxedDenseElements, JSObject*);
+
+void
+js::ArrayShiftMoveElements(JSObject* obj)
+{
+    MOZ_ASSERT_IF(obj->is<ArrayObject>(), obj->as<ArrayObject>().lengthIsWritable());
+
+    ShiftMoveBoxedOrUnboxedDenseElementsFunctor functor(obj);
+    JS_ALWAYS_TRUE(CallBoxedOrUnboxedSpecialization(functor, obj) == DenseElementResult::Success);
 }
 
 template <JSValueType Type>
 DenseElementResult
 ArrayShiftDenseKernel(JSContext* cx, JSObject* obj, Value* rval)
 {
     if (ObjectMayHaveExtraIndexedProperties(obj))
         return DenseElementResult::Incomplete;
@@ -2258,33 +2278,33 @@ js::array_unshift(JSContext* cx, unsigne
     return true;
 }
 
 // Return a new array with the default prototype and specified allocated
 // capacity and length. If possible, try to reuse the group of the input
 // object. The resulting array will have the same boxed/unboxed elements
 // representation as the input object, and will either reuse the input
 // object's group or will have unknown property types.
-static inline JSObject*
-NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length)
+JSObject*
+js::NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
+                                        NewObjectKind newKind, bool forceAnalyze)
 {
     if (!obj->is<ArrayObject>() && !obj->is<UnboxedArrayObject>())
-        return NewDenseFullyAllocatedArray(cx, length);
+        return NewDenseFullyAllocatedArray(cx, length, nullptr, newKind);
 
     if (obj->getProto() != cx->global()->maybeGetArrayPrototype())
-        return NewDenseFullyAllocatedArray(cx, length);
+        return NewDenseFullyAllocatedArray(cx, length, nullptr, newKind);
 
     RootedObjectGroup group(cx, obj->getGroup(cx));
     if (!group)
         return nullptr;
 
     if (group->maybePreliminaryObjects())
-        group->maybePreliminaryObjects()->maybeAnalyze(cx, group);
-
-    NewObjectKind newKind = GenericObject;
+        group->maybePreliminaryObjects()->maybeAnalyze(cx, group, forceAnalyze);
+
     if (group->shouldPreTenure() || group->maybePreliminaryObjects())
         newKind = TenuredObject;
 
     if (group->maybeUnboxedLayout()) {
         if (length > UnboxedArrayObject::MaximumCapacity)
             return NewDenseFullyAllocatedArray(cx, length, nullptr, newKind);
 
         return UnboxedArrayObject::create(cx, group, length, newKind);
@@ -2437,17 +2457,17 @@ js::array_splice_impl(JSContext* cx, uns
     /* Steps 2, 8-9. */
     RootedObject arr(cx);
     if (CanOptimizeForDenseStorage(obj, actualStart, actualDeleteCount, cx)) {
         if (returnValueIsUsed) {
             arr = NewFullyAllocatedArrayTryReuseGroup(cx, obj, actualDeleteCount);
             if (!arr)
                 return false;
             DebugOnly<DenseElementResult> result =
-                CopyAnyBoxedOrUnboxedDenseElements(cx, arr, obj, actualStart, actualDeleteCount);
+                CopyAnyBoxedOrUnboxedDenseElements(cx, arr, obj, 0, actualStart, actualDeleteCount);
             MOZ_ASSERT(result.value == DenseElementResult::Success);
         }
     } else {
         arr = NewFullyAllocatedArrayTryReuseGroup(cx, obj, actualDeleteCount);
         if (!arr)
             return false;
 
         RootedValue fromValue(cx);
@@ -2602,39 +2622,59 @@ js::array_splice_impl(JSContext* cx, uns
 
     /* Step 17. */
     if (returnValueIsUsed)
         args.rval().setObject(*arr);
 
     return true;
 }
 
-bool
-js::array_concat_dense(JSContext* cx, Handle<ArrayObject*> arr1, Handle<ArrayObject*> arr2,
-                       Handle<ArrayObject*> result)
+template <JSValueType Type>
+DenseElementResult
+ArrayConcatDenseKernel(JSContext* cx, JSObject* obj1, JSObject* obj2, JSObject* result)
 {
-    uint32_t initlen1 = arr1->getDenseInitializedLength();
-    MOZ_ASSERT(initlen1 == arr1->length());
-
-    uint32_t initlen2 = arr2->getDenseInitializedLength();
-    MOZ_ASSERT(initlen2 == arr2->length());
+    uint32_t initlen1 = GetBoxedOrUnboxedInitializedLength<Type>(obj1);
+    MOZ_ASSERT(initlen1 == GetAnyBoxedOrUnboxedArrayLength(obj1));
+
+    uint32_t initlen2 = GetBoxedOrUnboxedInitializedLength<Type>(obj2);
+    MOZ_ASSERT(initlen2 == GetAnyBoxedOrUnboxedArrayLength(obj2));
 
     /* No overflow here due to nelements limit. */
     uint32_t len = initlen1 + initlen2;
 
-    if (!result->ensureElements(cx, len))
-        return false;
-
-    MOZ_ASSERT(!result->getDenseInitializedLength());
-    result->setDenseInitializedLength(len);
-
-    result->initDenseElements(0, arr1->getDenseElements(), initlen1);
-    result->initDenseElements(initlen1, arr2->getDenseElements(), initlen2);
-    result->setLengthInt32(len);
-    return true;
+    MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<Type>(result) == 0);
+
+    if (Type == JSVAL_TYPE_MAGIC) {
+        if (!result->as<ArrayObject>().ensureElements(cx, len))
+            return DenseElementResult::Failure;
+    } else {
+        if (result->as<UnboxedArrayObject>().capacity() < len) {
+            if (!result->as<UnboxedArrayObject>().growElements(cx, len))
+                return DenseElementResult::Failure;
+        }
+    }
+
+    CopyBoxedOrUnboxedDenseElements<Type>(cx, result, obj1, 0, 0, initlen1);
+    CopyBoxedOrUnboxedDenseElements<Type>(cx, result, obj2, initlen1, 0, initlen2);
+
+    SetAnyBoxedOrUnboxedArrayLength(cx, result, len);
+    return DenseElementResult::Success;
+}
+
+DefineBoxedOrUnboxedFunctor4(ArrayConcatDenseKernel,
+                             JSContext*, JSObject*, JSObject*, JSObject*);
+
+bool
+js::array_concat_dense(JSContext* cx, HandleObject obj1, HandleObject obj2,
+                       HandleObject result)
+{
+    ArrayConcatDenseKernelFunctor functor(cx, obj1, obj2, result);
+    DenseElementResult rv = CallBoxedOrUnboxedSpecialization(functor, result);
+    MOZ_ASSERT(rv != DenseElementResult::Incomplete);
+    return rv == DenseElementResult::Success;
 }
 
 /*
  * Python-esque sequence operations.
  */
 bool
 js::array_concat(JSContext* cx, unsigned argc, Value* vp)
 {
@@ -2656,17 +2696,17 @@ js::array_concat(JSContext* cx, unsigned
 
         size_t initlen = GetAnyBoxedOrUnboxedInitializedLength(aobj);
         narr = NewFullyAllocatedArrayTryReuseGroup(cx, aobj, initlen);
         if (!narr)
             return false;
         SetAnyBoxedOrUnboxedArrayLength(cx, narr, length);
 
         DebugOnly<DenseElementResult> result =
-            CopyAnyBoxedOrUnboxedDenseElements(cx, narr, aobj, 0, initlen);
+            CopyAnyBoxedOrUnboxedDenseElements(cx, narr, aobj, 0, 0, initlen);
         MOZ_ASSERT(result.value == DenseElementResult::Success);
 
         args.rval().setObject(*narr);
         if (argc == 0)
             return true;
         argc--;
         p++;
     } else {
@@ -2912,17 +2952,17 @@ js::array_slice(JSContext* cx, unsigned 
 
         RootedObject narr(cx, NewFullyAllocatedArrayTryReuseGroup(cx, obj, count));
         if (!narr)
             return false;
         SetAnyBoxedOrUnboxedArrayLength(cx, narr, end - begin);
 
         if (count) {
             DebugOnly<DenseElementResult> result =
-                CopyAnyBoxedOrUnboxedDenseElements(cx, narr, obj, begin, count);
+                CopyAnyBoxedOrUnboxedDenseElements(cx, narr, obj, 0, begin, count);
             MOZ_ASSERT(result.value == DenseElementResult::Success);
         }
         args.rval().setObject(*narr);
         return true;
     }
 
     RootedArrayObject narr(cx, NewPartlyAllocatedArrayTryReuseGroup(cx, obj, end - begin));
     if (!narr)
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -90,16 +90,22 @@ NewDenseCopiedArray(JSContext* cx, uint3
 /* Create a dense array based on templateObject with the given length. */
 extern ArrayObject*
 NewDenseFullyAllocatedArrayWithTemplate(JSContext* cx, uint32_t length, JSObject* templateObject);
 
 /* Create a dense array with the same copy-on-write elements as another object. */
 extern JSObject*
 NewDenseCopyOnWriteArray(JSContext* cx, HandleArrayObject templateObject, gc::InitialHeap heap);
 
+/* Create a dense or unboxed array, using the same group as |obj| if possible. */
+extern JSObject*
+NewFullyAllocatedArrayTryReuseGroup(JSContext* cx, JSObject* obj, size_t length,
+                                    NewObjectKind newKind = GenericObject,
+                                    bool forceAnalyze = false);
+
 /*
  * Determines whether a write to the given element on |obj| should fail because
  * |obj| is an Array with a non-writable length, and writing that element would
  * increase the length of the array.
  */
 extern bool
 WouldDefinePastNonwritableLength(HandleNativeObject obj, uint32_t index);
 
@@ -148,27 +154,27 @@ array_splice_impl(JSContext* cx, unsigne
 extern bool
 array_concat(JSContext* cx, unsigned argc, js::Value* vp);
 
 template <bool Locale>
 JSString*
 ArrayJoin(JSContext* cx, HandleObject obj, HandleLinearString sepstr, uint32_t length);
 
 extern bool
-array_concat_dense(JSContext* cx, Handle<ArrayObject*> arr1, Handle<ArrayObject*> arr2,
-                   Handle<ArrayObject*> result);
+array_concat_dense(JSContext* cx, HandleObject arr1, HandleObject arr2,
+                   HandleObject result);
 
 bool
 array_join(JSContext* cx, unsigned argc, js::Value* vp);
 
 extern JSString*
 array_join_impl(JSContext* cx, HandleValue array, HandleString sep);
 
 extern void
-ArrayShiftMoveElements(ArrayObject* obj);
+ArrayShiftMoveElements(JSObject* obj);
 
 extern bool
 array_shift(JSContext* cx, unsigned argc, js::Value* vp);
 
 extern bool
 array_unshift(JSContext* cx, unsigned argc, js::Value* vp);
 
 extern bool
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -1147,32 +1147,40 @@ js::GetDOMProxyShadowsCheck()
 }
 
 bool
 js::detail::IdMatchesAtom(jsid id, JSAtom* atom)
 {
     return id == INTERNED_STRING_TO_JSID(nullptr, atom);
 }
 
-JS_FRIEND_API(JSContext*)
-js::DefaultJSContext(JSRuntime* rt)
+JS_FRIEND_API(bool)
+js::PrepareScriptEnvironmentAndInvoke(JSRuntime* rt, HandleObject scope, ScriptEnvironmentPreparer::Closure& closure)
 {
-    if (rt->defaultJSContextCallback) {
-        JSContext* cx = rt->defaultJSContextCallback(rt);
-        MOZ_ASSERT(cx);
-        return cx;
+    if (rt->scriptEnvironmentPreparer)
+        return rt->scriptEnvironmentPreparer->invoke(scope, closure);
+
+    MOZ_ASSERT(rt->contextList.getFirst() == rt->contextList.getLast());
+    JSContext* cx = rt->contextList.getFirst();
+    JSAutoCompartment ac(cx, scope);
+    bool ok = closure(cx);
+
+    // NB: This does not affect Gecko, which has a prepareScriptEnvironment
+    // callback.
+    if (JS_IsExceptionPending(cx)) {
+        JS_ReportPendingException(cx);
     }
-    MOZ_ASSERT(rt->contextList.getFirst() == rt->contextList.getLast());
-    return rt->contextList.getFirst();
+
+    return ok;
 }
 
 JS_FRIEND_API(void)
-js::SetDefaultJSContextCallback(JSRuntime* rt, DefaultJSContextCallback cb)
+js::SetScriptEnvironmentPreparer(JSRuntime* rt, ScriptEnvironmentPreparer* preparer)
 {
-    rt->defaultJSContextCallback = cb;
+    rt->scriptEnvironmentPreparer = preparer;
 }
 
 #ifdef DEBUG
 JS_FRIEND_API(void)
 js::Debug_SetActiveJSContext(JSRuntime* rt, JSContext* cx)
 {
     rt->activeContext = cx;
 }
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -2556,28 +2556,42 @@ IdToValue(jsid id)
         return JS::Int32Value(JSID_TO_INT(id));
     if (JSID_IS_SYMBOL(id))
         return JS::SymbolValue(JSID_TO_SYMBOL(id));
     MOZ_ASSERT(JSID_IS_VOID(id));
     return JS::UndefinedValue();
 }
 
 /*
- * If the embedder has registered a default JSContext callback, returns the
- * result of the callback. Otherwise, asserts that |rt| has exactly one
- * JSContext associated with it, and returns that context.
+ * If the embedder has registered a ScriptEnvironmentPreparer,
+ * PrepareScriptEnvironmentAndInvoke will call the preparer's 'invoke' method
+ * with the given |closure|, with the assumption that the preparer will set up
+ * any state necessary to run script in |scope|, invoke |closure| with a valid
+ * JSContext*, and return.
+ *
+ * If no preparer is registered, PrepareScriptEnvironmentAndInvoke will assert
+ * that |rt| has exactly one JSContext associated with it, enter the compartment
+ * of |scope| on that context, and invoke |closure|.
  */
-extern JS_FRIEND_API(JSContext*)
-DefaultJSContext(JSRuntime* rt);
-
-typedef JSContext*
-(* DefaultJSContextCallback)(JSRuntime* rt);
+
+struct ScriptEnvironmentPreparer {
+    struct Closure {
+        virtual bool operator()(JSContext* cx) = 0;
+    };
+
+    virtual bool invoke(JS::HandleObject scope, Closure& closure) = 0;
+};
+
+extern JS_FRIEND_API(bool)
+PrepareScriptEnvironmentAndInvoke(JSRuntime* rt, JS::HandleObject scope,
+                                  ScriptEnvironmentPreparer::Closure& closure);
 
 JS_FRIEND_API(void)
-SetDefaultJSContextCallback(JSRuntime* rt, DefaultJSContextCallback cb);
+SetScriptEnvironmentPreparer(JSRuntime* rt, ScriptEnvironmentPreparer*
+preparer);
 
 /*
  * To help embedders enforce their invariants, we allow them to specify in
  * advance which JSContext should be passed to JSAPI calls. If this is set
  * to a non-null value, the assertSameCompartment machinery does double-
  * duty (in debug builds) to verify that it matches the cx being used.
  */
 #ifdef DEBUG
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -383,31 +383,31 @@ ResolveInterpretedFunctionPrototype(JSCo
     bool isStarGenerator = fun->isStarGenerator();
     Rooted<GlobalObject*> global(cx, &fun->global());
     RootedObject objProto(cx);
     if (isStarGenerator)
         objProto = GlobalObject::getOrCreateStarGeneratorObjectPrototype(cx, global);
     else
         objProto = fun->global().getOrCreateObjectPrototype(cx);
     if (!objProto)
-        return nullptr;
+        return false;
 
     RootedPlainObject proto(cx, NewObjectWithGivenProto<PlainObject>(cx, objProto,
                                                                      SingletonObject));
     if (!proto)
-        return nullptr;
+        return false;
 
     // Per ES5 13.2 the prototype's .constructor property is configurable,
     // non-enumerable, and writable.  However, per the 15 July 2013 ES6 draft,
     // section 15.19.3, the .prototype of a generator function does not link
     // back with a .constructor.
     if (!isStarGenerator) {
         RootedValue objVal(cx, ObjectValue(*fun));
         if (!DefineProperty(cx, proto, cx->names().constructor, objVal, nullptr, nullptr, 0))
-            return nullptr;
+            return false;
     }
 
     // Per ES5 15.3.5.2 a user-defined function's .prototype property is
     // initially non-configurable, non-enumerable, and writable.
     RootedValue protoVal(cx, ObjectValue(*proto));
     return DefineProperty(cx, fun, id, protoVal, nullptr, nullptr,
                           JSPROP_PERMANENT | JSPROP_RESOLVING);
 }
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -7101,17 +7101,26 @@ JS::dbg::GarbageCollectionEvent::Ptr
 JS::GCDescription::toGCEvent(JSRuntime* rt) const
 {
     return JS::dbg::GarbageCollectionEvent::Create(rt, rt->gc.stats, rt->gc.majorGCCount());
 }
 
 char16_t*
 JS::GCDescription::formatJSON(JSRuntime* rt, uint64_t timestamp) const
 {
-    return rt->gc.stats.formatJSON(timestamp);
+    UniqueChars cstr = rt->gc.stats.formatJsonMessage(timestamp);
+
+    size_t nchars = strlen(cstr.get());
+    UniquePtr<char16_t, JS::FreePolicy> out(js_pod_malloc<char16_t>(nchars + 1));
+    if (!out)
+        return nullptr;
+    out.get()[nchars] = 0;
+
+    CopyAndInflateChars(out.get(), cstr.get(), nchars);
+    return out.release();
 }
 
 JS_PUBLIC_API(JS::GCSliceCallback)
 JS::SetGCSliceCallback(JSRuntime* rt, GCSliceCallback callback)
 {
     return rt->gc.setSliceCallback(callback);
 }
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2805,16 +2805,22 @@ js::LookupPropertyPure(ExclusiveContext*
             if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj))
                 return false;
         } else if (obj->is<UnboxedPlainObject>()) {
             if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id)) {
                 *objp = obj;
                 MarkNonNativePropertyFound<NoGC>(propp);
                 return true;
             }
+        } else if (obj->is<UnboxedArrayObject>()) {
+            if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
+                *objp = obj;
+                MarkNonNativePropertyFound<NoGC>(propp);
+                return true;
+            }
         } else if (obj->is<TypedObject>()) {
             if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) {
                 *objp = obj;
                 MarkNonNativePropertyFound<NoGC>(propp);
                 return true;
             }
         } else {
             return false;
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -17,16 +17,17 @@
 #include "vm/ArgumentsObject.h"
 
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 
 #include "vm/ScopeObject-inl.h"
 #include "vm/Stack-inl.h"
 #include "vm/String-inl.h"
+#include "vm/UnboxedObject-inl.h"
 
 namespace js {
 
 inline bool
 ComputeThis(JSContext* cx, AbstractFramePtr frame)
 {
     MOZ_ASSERT_IF(frame.isInterpreterFrame(), !frame.asInterpreterFrame()->runningInJit());
 
new file mode 100644
--- /dev/null
+++ b/js/src/vm/ObjectGroup-inl.h
@@ -0,0 +1,127 @@
+/* -*- 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 vm_ObjectGroup_inl_h
+#define vm_ObjectGroup_inl_h
+
+#include "vm/ObjectGroup.h"
+
+namespace js {
+
+inline bool
+ObjectGroup::needsSweep()
+{
+    // Note: this can be called off thread during compacting GCs, in which case
+    // nothing will be running on the main thread.
+    return generation() != zoneFromAnyThread()->types.generation;
+}
+
+inline void
+ObjectGroup::maybeSweep(AutoClearTypeInferenceStateOnOOM* oom)
+{
+    if (needsSweep())
+        sweep(oom);
+}
+
+inline ObjectGroupFlags
+ObjectGroup::flags()
+{
+    maybeSweep(nullptr);
+    return flagsDontCheckGeneration();
+}
+
+inline void
+ObjectGroup::addFlags(ObjectGroupFlags flags)
+{
+    maybeSweep(nullptr);
+    flags_ |= flags;
+}
+
+inline void
+ObjectGroup::clearFlags(ObjectGroupFlags flags)
+{
+    maybeSweep(nullptr);
+    flags_ &= ~flags;
+}
+
+inline bool
+ObjectGroup::hasAnyFlags(ObjectGroupFlags flags)
+{
+    MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags);
+    return !!(this->flags() & flags);
+}
+
+inline bool
+ObjectGroup::hasAllFlags(ObjectGroupFlags flags)
+{
+    MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags);
+    return (this->flags() & flags) == flags;
+}
+
+inline bool
+ObjectGroup::unknownProperties()
+{
+    MOZ_ASSERT_IF(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES,
+                  hasAllFlags(OBJECT_FLAG_DYNAMIC_MASK));
+    return !!(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES);
+}
+
+inline bool
+ObjectGroup::shouldPreTenure()
+{
+    return hasAnyFlags(OBJECT_FLAG_PRE_TENURE) && !unknownProperties();
+}
+
+inline bool
+ObjectGroup::canPreTenure()
+{
+    return !unknownProperties();
+}
+
+inline bool
+ObjectGroup::fromAllocationSite()
+{
+    return flags() & OBJECT_FLAG_FROM_ALLOCATION_SITE;
+}
+
+inline void
+ObjectGroup::setShouldPreTenure(ExclusiveContext* cx)
+{
+    MOZ_ASSERT(canPreTenure());
+    setFlags(cx, OBJECT_FLAG_PRE_TENURE);
+}
+
+inline TypeNewScript*
+ObjectGroup::newScript()
+{
+    maybeSweep(nullptr);
+    return newScriptDontCheckGeneration();
+}
+
+inline PreliminaryObjectArrayWithTemplate*
+ObjectGroup::maybePreliminaryObjects()
+{
+    maybeSweep(nullptr);
+    return maybePreliminaryObjectsDontCheckGeneration();
+}
+
+inline UnboxedLayout*
+ObjectGroup::maybeUnboxedLayout()
+{
+    maybeSweep(nullptr);
+    return maybeUnboxedLayoutDontCheckGeneration();
+}
+
+inline UnboxedLayout&
+ObjectGroup::unboxedLayout()
+{
+    maybeSweep(nullptr);
+    return unboxedLayoutDontCheckGeneration();
+}
+
+} // namespace js
+
+#endif /* vm_ObjectGroup_inl_h */
--- a/js/src/vm/ObjectGroup.h
+++ b/js/src/vm/ObjectGroup.h
@@ -264,95 +264,70 @@ class ObjectGroup : public gc::TenuredCe
     }
 
     TypeNewScript* newScriptDontCheckGeneration() const {
         if (addendumKind() == Addendum_NewScript)
             return reinterpret_cast<TypeNewScript*>(addendum_);
         return nullptr;
     }
 
-    UnboxedLayout* maybeUnboxedLayoutDontCheckGeneration() const {
-        if (addendumKind() == Addendum_UnboxedLayout)
-            return reinterpret_cast<UnboxedLayout*>(addendum_);
-        return nullptr;
-    }
-
     TypeNewScript* anyNewScript();
     void detachNewScript(bool writeBarrier, ObjectGroup* replacement);
 
     ObjectGroupFlags flagsDontCheckGeneration() const {
         return flags_;
     }
 
   public:
 
-    ObjectGroupFlags flags() {
-        maybeSweep(nullptr);
-        return flagsDontCheckGeneration();
-    }
-
-    void addFlags(ObjectGroupFlags flags) {
-        maybeSweep(nullptr);
-        flags_ |= flags;
-    }
-
-    void clearFlags(ObjectGroupFlags flags) {
-        maybeSweep(nullptr);
-        flags_ &= ~flags;
-    }
-
-    TypeNewScript* newScript() {
-        maybeSweep(nullptr);
-        return newScriptDontCheckGeneration();
-    }
+    inline ObjectGroupFlags flags();
+    inline void addFlags(ObjectGroupFlags flags);
+    inline void clearFlags(ObjectGroupFlags flags);
+    inline TypeNewScript* newScript();
 
     void setNewScript(TypeNewScript* newScript) {
         setAddendum(Addendum_NewScript, newScript);
     }
 
-    PreliminaryObjectArrayWithTemplate* maybePreliminaryObjects() {
-        maybeSweep(nullptr);
-        return maybePreliminaryObjectsDontCheckGeneration();
-    }
+    inline PreliminaryObjectArrayWithTemplate* maybePreliminaryObjects();
 
     PreliminaryObjectArrayWithTemplate* maybePreliminaryObjectsDontCheckGeneration() {
         if (addendumKind() == Addendum_PreliminaryObjects)
             return reinterpret_cast<PreliminaryObjectArrayWithTemplate*>(addendum_);
         return nullptr;
     }
 
     void setPreliminaryObjects(PreliminaryObjectArrayWithTemplate* preliminaryObjects) {
         setAddendum(Addendum_PreliminaryObjects, preliminaryObjects);
     }
 
     void detachPreliminaryObjects() {
-        MOZ_ASSERT(maybePreliminaryObjects());
+        MOZ_ASSERT(maybePreliminaryObjectsDontCheckGeneration());
         setAddendum(Addendum_None, nullptr);
     }
 
     bool hasUnanalyzedPreliminaryObjects() {
         return (newScriptDontCheckGeneration() && !newScriptDontCheckGeneration()->analyzed()) ||
                maybePreliminaryObjectsDontCheckGeneration();
     }
 
-    UnboxedLayout* maybeUnboxedLayout() {
-        maybeSweep(nullptr);
-        return maybeUnboxedLayoutDontCheckGeneration();
+    inline UnboxedLayout* maybeUnboxedLayout();
+    inline UnboxedLayout& unboxedLayout();
+
+    UnboxedLayout* maybeUnboxedLayoutDontCheckGeneration() const {
+        if (addendumKind() == Addendum_UnboxedLayout)
+            return reinterpret_cast<UnboxedLayout*>(addendum_);
+        return nullptr;
     }
 
     UnboxedLayout& unboxedLayoutDontCheckGeneration() const {
         MOZ_ASSERT(addendumKind() == Addendum_UnboxedLayout);
         return *maybeUnboxedLayoutDontCheckGeneration();
     }
 
-    UnboxedLayout& unboxedLayout() {
-        maybeSweep(nullptr);
-        return unboxedLayoutDontCheckGeneration();
-    }
-
     void setUnboxedLayout(UnboxedLayout* layout) {
         setAddendum(Addendum_UnboxedLayout, layout);
     }
 
     ObjectGroup* maybeOriginalUnboxedGroup() const {
         if (addendumKind() == Addendum_OriginalUnboxedGroup)
             return reinterpret_cast<ObjectGroup*>(addendum_);
         return nullptr;
@@ -457,61 +432,39 @@ class ObjectGroup : public gc::TenuredCe
      * might update the property with a new type.
      */
     Property** propertySet;
   public:
 
     inline ObjectGroup(const Class* clasp, TaggedProto proto, JSCompartment* comp,
                        ObjectGroupFlags initialFlags);
 
-    inline bool hasAnyFlags(ObjectGroupFlags flags) {
-        MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags);
-        return !!(this->flags() & flags);
-    }
-
-    bool hasAllFlags(ObjectGroupFlags flags) {
-        MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags);
-        return (this->flags() & flags) == flags;
-    }
+    inline bool hasAnyFlags(ObjectGroupFlags flags);
+    inline bool hasAllFlags(ObjectGroupFlags flags);
 
     bool hasAllFlagsDontCheckGeneration(ObjectGroupFlags flags) {
         MOZ_ASSERT((flags & OBJECT_FLAG_DYNAMIC_MASK) == flags);
         return (this->flagsDontCheckGeneration() & flags) == flags;
     }
 
-    bool unknownProperties() {
-        MOZ_ASSERT_IF(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES,
-                      hasAllFlags(OBJECT_FLAG_DYNAMIC_MASK));
-        return !!(flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES);
-    }
+    inline bool unknownProperties();
 
     bool unknownPropertiesDontCheckGeneration() {
         MOZ_ASSERT_IF(flagsDontCheckGeneration() & OBJECT_FLAG_UNKNOWN_PROPERTIES,
                       hasAllFlagsDontCheckGeneration(OBJECT_FLAG_DYNAMIC_MASK));
         return !!(flagsDontCheckGeneration() & OBJECT_FLAG_UNKNOWN_PROPERTIES);
     }
 
-    bool shouldPreTenure() {
-        return hasAnyFlags(OBJECT_FLAG_PRE_TENURE) && !unknownProperties();
-    }
+    inline bool shouldPreTenure();
 
     gc::InitialHeap initialHeap(CompilerConstraintList* constraints);
 
-    bool canPreTenure() {
-        return !unknownProperties();
-    }
-
-    bool fromAllocationSite() {
-        return flags() & OBJECT_FLAG_FROM_ALLOCATION_SITE;
-    }
-
-    void setShouldPreTenure(ExclusiveContext* cx) {
-        MOZ_ASSERT(canPreTenure());
-        setFlags(cx, OBJECT_FLAG_PRE_TENURE);
-    }
+    inline bool canPreTenure();
+    inline bool fromAllocationSite();
+    inline void setShouldPreTenure(ExclusiveContext* cx);
 
     /*
      * Get or create a property of this object. Only call this for properties which
      * a script accesses explicitly.
      */
     inline HeapTypeSet* getProperty(ExclusiveContext* cx, JSObject* obj, jsid id);
 
     /* Get a property only if it already exists. */
@@ -538,23 +491,23 @@ class ObjectGroup : public gc::TenuredCe
     void maybeClearNewScriptOnOOM();
     void clearNewScript(ExclusiveContext* cx, ObjectGroup* replacement = nullptr);
     bool isPropertyNonData(jsid id);
     bool isPropertyNonWritable(jsid id);
 
     void print();
 
     inline void clearProperties();
-    void maybeSweep(AutoClearTypeInferenceStateOnOOM* oom);
     void traceChildren(JSTracer* trc);
 
+    inline bool needsSweep();
+    inline void maybeSweep(AutoClearTypeInferenceStateOnOOM* oom);
+
   private:
-#ifdef DEBUG
-    bool needsSweep();
-#endif
+    void sweep(AutoClearTypeInferenceStateOnOOM* oom);
 
     uint32_t generation() {
         return (flags_ & OBJECT_FLAG_GENERATION_MASK) >> OBJECT_FLAG_GENERATION_SHIFT;
     }
 
   public:
     void setGeneration(uint32_t generation) {
         MOZ_ASSERT(generation <= (OBJECT_FLAG_GENERATION_MASK >> OBJECT_FLAG_GENERATION_SHIFT));
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -203,17 +203,17 @@ JSRuntime::JSRuntime(JSRuntime* parentRu
     commonNames(nullptr),
     permanentAtoms(nullptr),
     wellKnownSymbols(nullptr),
     wrapObjectCallbacks(&DefaultWrapObjectCallbacks),
     preserveWrapperCallback(nullptr),
     jitSupportsFloatingPoint(false),
     jitSupportsSimd(false),
     ionPcScriptCache(nullptr),
-    defaultJSContextCallback(nullptr),
+    scriptEnvironmentPreparer(nullptr),
     ctypesActivityCallback(nullptr),
     offthreadIonCompilationEnabled_(true),
     parallelParsingEnabled_(true),
 #ifdef DEBUG
     enteredPolicy(nullptr),
 #endif
     largeAllocationFailureCallback(nullptr),
     oomCallback(nullptr),
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1322,17 +1322,17 @@ struct JSRuntime : public JS::shadow::Ru
     }
 
     bool                jitSupportsFloatingPoint;
     bool                jitSupportsSimd;
 
     // Cache for jit::GetPcScript().
     js::jit::PcScriptCache* ionPcScriptCache;
 
-    js::DefaultJSContextCallback defaultJSContextCallback;
+    js::ScriptEnvironmentPreparer* scriptEnvironmentPreparer;
 
     js::CTypesActivityCallback  ctypesActivityCallback;
 
   private:
     static mozilla::Atomic<size_t> liveRuntimesCount;
 
   public:
     static bool hasLiveRuntimes() {
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -378,17 +378,18 @@ ReadStructuredClone(JSContext* cx, uint6
     JSStructuredCloneReader r(in, cb, cbClosure);
     return r.read(vp);
 }
 
 // If the given buffer contains Transferables, free them. Note that custom
 // Transferables will use the JSStructuredCloneCallbacks::freeTransfer() to
 // delete their transferables.
 static void
-Discard(uint64_t* buffer, size_t nbytes, const JSStructuredCloneCallbacks* cb, void* cbClosure)
+DiscardTransferables(uint64_t* buffer, size_t nbytes,
+                     const JSStructuredCloneCallbacks* cb, void* cbClosure)
 {
     MOZ_ASSERT(nbytes % sizeof(uint64_t) == 0);
     if (nbytes < sizeof(uint64_t))
         return; // Empty buffer
 
     uint64_t* point = buffer;
     uint32_t tag, data;
     SCInput::getPair(point++, &tag, &data);
@@ -426,37 +427,25 @@ Discard(uint64_t* buffer, size_t nbytes,
         } else if (cb && cb->freeTransfer) {
             cb->freeTransfer(tag, JS::TransferableOwnership(ownership), content, extraData, cbClosure);
         } else {
             MOZ_ASSERT(false, "unknown ownership");
         }
     }
 }
 
-static void
-ClearStructuredClone(uint64_t* data, size_t nbytes,
-                     const JSStructuredCloneCallbacks* cb, void* cbClosure)
-{
-    Discard(data, nbytes, cb, cbClosure);
-    js_free(data);
-}
-
-bool
-StructuredCloneHasTransferObjects(const uint64_t* data, size_t nbytes, bool* hasTransferable)
+static bool
+StructuredCloneHasTransferObjects(const uint64_t* data, size_t nbytes)
 {
-    *hasTransferable = false;
+    if (!data)
+        return false;
 
-    if (data) {
-        uint64_t u = LittleEndian::readUint64(data);
-        uint32_t tag = uint32_t(u >> 32);
-        if (tag == SCTAG_TRANSFER_MAP_HEADER)
-            *hasTransferable = true;
-    }
-
-    return true;
+    uint64_t u = LittleEndian::readUint64(data);
+    uint32_t tag = uint32_t(u >> 32);
+    return (tag == SCTAG_TRANSFER_MAP_HEADER);
 }
 
 namespace js {
 
 SCInput::SCInput(JSContext* cx, uint64_t* data, size_t nbytes)
     : cx(cx), point(data), bufEnd(data + nbytes / 8)
 {
     // On 32-bit, we sometimes construct an SCInput from an SCOutput buffer,
@@ -729,17 +718,18 @@ SCOutput::extractBuffer(uint64_t** datap
 JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX);
 
 JSStructuredCloneWriter::~JSStructuredCloneWriter()
 {
     // Free any transferable data left lying around in the buffer
     uint64_t* data;
     size_t size;
     MOZ_ALWAYS_TRUE(extractBuffer(&data, &size));
-    ClearStructuredClone(data, size, callbacks, closure);
+    DiscardTransferables(data, size, callbacks, closure);
+    js_free(data);
 }
 
 bool
 JSStructuredCloneWriter::parseTransferable()
 {
     MOZ_ASSERT(transferableObjects.empty(), "parseTransferable called with stale data");
 
     if (transferable.isNull() || transferable.isUndefined())
@@ -1784,18 +1774,19 @@ JSStructuredCloneReader::readTransferMap
                 return false;
             }
             if (!callbacks->readTransfer(cx, this, tag, content, extraData, closure, &obj))
                 return false;
             MOZ_ASSERT(obj);
             MOZ_ASSERT(!cx->isExceptionPending());
         }
 
-        // On failure, the buffer will still own the data (since its ownership will not get set to SCTAG_TMO_UNOWNED),
-        // so the data will be freed by ClearStructuredClone
+        // On failure, the buffer will still own the data (since its ownership
+        // will not get set to SCTAG_TMO_UNOWNED), so the data will be freed by
+        // DiscardTransferables.
         if (!obj)
             return false;
 
         // Mark the SCTAG_TRANSFER_MAP_* entry as no longer owned by the input
         // buffer.
         *pos = PairToUInt64(tag, JS::SCTAG_TMO_UNOWNED);
         MOZ_ASSERT(headerPos < pos && pos < in.end());
 
@@ -1914,29 +1905,26 @@ JS_WriteStructuredClone(JSContext* cx, H
     return WriteStructuredClone(cx, value, bufp, nbytesp, callbacks, closure, transferable);
 }
 
 JS_PUBLIC_API(bool)
 JS_ClearStructuredClone(uint64_t* data, size_t nbytes,
                         const JSStructuredCloneCallbacks* optionalCallbacks,
                         void* closure)
 {
-    ClearStructuredClone(data, nbytes, optionalCallbacks, closure);
+    DiscardTransferables(data, nbytes, optionalCallbacks, closure);
+    js_free(data);
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS_StructuredCloneHasTransferables(const uint64_t* data, size_t nbytes,
                                    bool* hasTransferable)
 {
-    bool transferable;
-    if (!StructuredCloneHasTransferObjects(data, nbytes, &transferable))
-        return false;
-
-    *hasTransferable = transferable;
+    *hasTransferable = StructuredCloneHasTransferObjects(data, nbytes);
     return true;
 }
 
 JS_PUBLIC_API(bool)
 JS_StructuredClone(JSContext* cx, HandleValue value, MutableHandleValue vp,
                    const JSStructuredCloneCallbacks* optionalCallbacks,
                    void* closure)
 {
@@ -1974,81 +1962,87 @@ JS_StructuredClone(JSContext* cx, Handle
         }
     }
 
     return buf.read(cx, vp, callbacks, closure);
 }
 
 JSAutoStructuredCloneBuffer::JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other)
 {
+    ownTransferables_ = other.ownTransferables_;
     other.steal(&data_, &nbytes_, &version_);
 }
 
 JSAutoStructuredCloneBuffer&
 JSAutoStructuredCloneBuffer::operator=(JSAutoStructuredCloneBuffer&& other)
 {
     MOZ_ASSERT(&other != this);
     clear();
+    ownTransferables_ = other.ownTransferables_;
     other.steal(&data_, &nbytes_, &version_);
     return *this;
 }
 
 void
 JSAutoStructuredCloneBuffer::clear()
 {
     if (data_) {
-        ClearStructuredClone(data_, nbytes_, callbacks_, closure_);
+        if (ownTransferables_ == OwnsTransferablesIfAny)
+            DiscardTransferables(data_, nbytes_, callbacks_, closure_);
+        ownTransferables_ = NoTransferables;
+        js_free(data_);
         data_ = nullptr;
         nbytes_ = 0;
         version_ = 0;
     }
 }
 
 bool
 JSAutoStructuredCloneBuffer::copy(const uint64_t* srcData, size_t nbytes, uint32_t version)
 {
     // transferable objects cannot be copied
-    bool hasTransferable;
-    if (!StructuredCloneHasTransferObjects(data_, nbytes_, &hasTransferable) ||
-        hasTransferable)
+    if (StructuredCloneHasTransferObjects(data_, nbytes_))
         return false;
 
     uint64_t* newData = static_cast<uint64_t*>(js_malloc(nbytes));
     if (!newData)
         return false;
 
     js_memcpy(newData, srcData, nbytes);
 
     clear();
     data_ = newData;
     nbytes_ = nbytes;
     version_ = version;
+    ownTransferables_ = NoTransferables;
     return true;
 }
 
 void
 JSAutoStructuredCloneBuffer::adopt(uint64_t* data, size_t nbytes, uint32_t version)
 {
     clear();
     data_ = data;
     nbytes_ = nbytes;
     version_ = version;
+    ownTransferables_ = OwnsTransferablesIfAny;
 }
 
 void
 JSAutoStructuredCloneBuffer::steal(uint64_t** datap, size_t* nbytesp, uint32_t* versionp)
 {
     *datap = data_;
     *nbytesp = nbytes_;
     if (versionp)
         *versionp = version_;
 
     data_ = nullptr;
     nbytes_ = 0;
     version_ = 0;
+    ownTransferables_ = NoTransferables;
 }
 
 bool
 JSAutoStructuredCloneBuffer::read(JSContext* cx, MutableHandleValue vp,
                                   const JSStructuredCloneCallbacks* optionalCallbacks,
                                   void* closure)
 {
     MOZ_ASSERT(cx);
@@ -2068,23 +2062,27 @@ JSAutoStructuredCloneBuffer::write(JSCon
 
 bool
 JSAutoStructuredCloneBuffer::write(JSContext* cx, HandleValue value,
                                    HandleValue transferable,
                                    const JSStructuredCloneCallbacks* optionalCallbacks,
                                    void* closure)
 {
     clear();
-    bool ok = !!JS_WriteStructuredClone(cx, value, &data_, &nbytes_,
-                                        optionalCallbacks, closure,
-                                        transferable);
-    if (!ok) {
+    bool ok = JS_WriteStructuredClone(cx, value, &data_, &nbytes_,
+                                      optionalCallbacks, closure,
+                                      transferable);
+
+    if (ok) {
+        ownTransferables_ = OwnsTransferablesIfAny;
+    } else {
         data_ = nullptr;
         nbytes_ = 0;
         version_ = JS_STRUCTURED_CLONE_VERSION;
+        ownTransferables_ = NoTransferables;
     }
     return ok;
 }
 
 JS_PUBLIC_API(void)
 JS_SetStructuredCloneCallbacks(JSRuntime* rt, const JSStructuredCloneCallbacks* callbacks)
 {
     rt->structuredCloneCallbacks = callbacks;
--- a/js/src/vm/TypeInference-inl.h
+++ b/js/src/vm/TypeInference-inl.h
@@ -21,16 +21,18 @@
 #include "vm/SharedArrayObject.h"
 #include "vm/SharedTypedArrayObject.h"
 #include "vm/StringObject.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/UnboxedObject.h"
 
 #include "jscntxtinlines.h"
 
+#include "vm/ObjectGroup-inl.h"
+
 namespace js {
 
 /////////////////////////////////////////////////////////////////////
 // CompilerOutput & RecompileInfo
 /////////////////////////////////////////////////////////////////////
 
 inline jit::IonScript*
 CompilerOutput::ion() const
@@ -348,35 +350,18 @@ TrackPropertyTypes(ExclusiveContext* cx,
         return false;
 
     if (obj->isSingleton() && !obj->group()->maybeGetProperty(id))
         return false;
 
     return true;
 }
 
-inline void
-EnsureTrackPropertyTypes(JSContext* cx, JSObject* obj, jsid id)
-{
-    id = IdToTypeId(id);
-
-    if (obj->isSingleton()) {
-        AutoEnterAnalysis enter(cx);
-        if (obj->hasLazyGroup() && !obj->getGroup(cx)) {
-            CrashAtUnhandlableOOM("Could not allocate ObjectGroup in EnsureTrackPropertyTypes");
-            return;
-        }
-        if (!obj->group()->unknownProperties() && !obj->group()->getProperty(cx, obj, id)) {
-            MOZ_ASSERT(obj->group()->unknownProperties());
-            return;
-        }
-    }
-
-    MOZ_ASSERT(obj->group()->unknownProperties() || TrackPropertyTypes(cx, obj, id));
-}
+void
+EnsureTrackPropertyTypes(JSContext* cx, JSObject* obj, jsid id);
 
 inline bool
 CanHaveEmptyPropertyTypesForOwnProperty(JSObject* obj)
 {
     // Per the comment on TypeSet::propertySet, property type sets for global
     // objects may be empty for 'own' properties if the global property still
     // has its initial undefined value.
     return obj->is<GlobalObject>();
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -1222,16 +1222,36 @@ TypeSet::ObjectKey::ensureTrackedPropert
         if (isSingleton()) {
             JSObject* obj = singleton();
             if (obj->isNative() && obj->as<NativeObject>().containsPure(id))
                 EnsureTrackPropertyTypes(cx, obj, id);
         }
     }
 }
 
+void
+js::EnsureTrackPropertyTypes(JSContext* cx, JSObject* obj, jsid id)
+{
+    id = IdToTypeId(id);
+
+    if (obj->isSingleton()) {
+        AutoEnterAnalysis enter(cx);
+        if (obj->hasLazyGroup() && !obj->getGroup(cx)) {
+            CrashAtUnhandlableOOM("Could not allocate ObjectGroup in EnsureTrackPropertyTypes");
+            return;
+        }
+        if (!obj->group()->unknownProperties() && !obj->group()->getProperty(cx, obj, id)) {
+            MOZ_ASSERT(obj->group()->unknownProperties());
+            return;
+        }
+    }
+
+    MOZ_ASSERT(obj->group()->unknownProperties() || TrackPropertyTypes(cx, obj, id));
+}
+
 bool
 HeapTypeSetKey::instantiate(JSContext* cx)
 {
     if (maybeTypes())
         return true;
     if (object()->isSingleton() && !object()->singleton()->getGroup(cx)) {
         cx->clearPendingException();
         return false;
@@ -4016,26 +4036,16 @@ ConstraintTypeSet::sweep(Zone* zone, Aut
 
 inline void
 ObjectGroup::clearProperties()
 {
     setBasePropertyCount(0);
     propertySet = nullptr;
 }
 
-#ifdef DEBUG
-bool
-ObjectGroup::needsSweep()
-{
-    // Note: this can be called off thread during compacting GCs, in which case
-    // nothing will be running on the main thread.
-    return generation() != zoneFromAnyThread()->types.generation;
-}
-#endif
-
 static void
 EnsureHasAutoClearTypeInferenceStateOnOOM(AutoClearTypeInferenceStateOnOOM*& oom, Zone* zone,
                                           Maybe<AutoClearTypeInferenceStateOnOOM>& fallback)
 {
     if (!oom) {
         if (zone->types.activeAnalysis) {
             oom = &zone->types.activeAnalysis->oom;
         } else {
@@ -4048,22 +4058,19 @@ EnsureHasAutoClearTypeInferenceStateOnOO
 /*
  * Before sweeping the arenas themselves, scan all groups in a compartment to
  * fixup weak references: property type sets referencing dead JS and type
  * objects, and singleton JS objects whose type is not referenced elsewhere.
  * This is done either incrementally as part of the sweep, or on demand as type
  * objects are accessed before their contents have been swept.
  */
 void
-ObjectGroup::maybeSweep(AutoClearTypeInferenceStateOnOOM* oom)
+ObjectGroup::sweep(AutoClearTypeInferenceStateOnOOM* oom)
 {
-    if (generation() == zoneFromAnyThread()->types.generation) {
-        // No sweeping required.
-        return;
-    }
+    MOZ_ASSERT(generation() != zoneFromAnyThread()->types.generation);
 
     setGeneration(zone()->types.generation);
 
     MOZ_ASSERT(zone()->isGCSweepingOrCompacting());
     MOZ_ASSERT(!zone()->runtimeFromMainThread()->isHeapMinorCollecting());
 
     Maybe<AutoClearTypeInferenceStateOnOOM> fallbackOOM;
     EnsureHasAutoClearTypeInferenceStateOnOOM(oom, zone(), fallbackOOM);
--- a/js/src/vm/UbiNode.cpp
+++ b/js/src/vm/UbiNode.cpp
@@ -53,23 +53,23 @@ JS::Zone* Concrete<void>::zone() const  
 JSCompartment* Concrete<void>::compartment() const        { MOZ_CRASH("null ubi::Node"); }
 
 size_t
 Concrete<void>::size(mozilla::MallocSizeOf mallocSizeof) const
 {
     MOZ_CRASH("null ubi::Node");
 }
 
-struct Node::ConstructFunctor : public BoolDefaultAdaptor<Value, false> {
+struct Node::ConstructFunctor : public js::BoolDefaultAdaptor<Value, false> {
     template <typename T> bool operator()(T* t, Node* node) { node->construct(t); return true; }
 };
 
 Node::Node(JS::TraceKind kind, void* ptr)
 {
-    CallTyped(ConstructFunctor(), ptr, kind, this);
+    js::gc::CallTyped(ConstructFunctor(), ptr, kind, this);
 }
 
 Node::Node(HandleValue value)
 {
     if (!DispatchValueTyped(ConstructFunctor(), value, this))
         construct<void>(nullptr);
 }
 
--- a/js/src/vm/UnboxedObject-inl.h
+++ b/js/src/vm/UnboxedObject-inl.h
@@ -157,19 +157,35 @@ SetUnboxedValue(ExclusiveContext* cx, JS
         return false;
 
       default:
         MOZ_CRASH("Invalid type for unboxed value");
     }
 }
 
 /////////////////////////////////////////////////////////////////////
+// UnboxedPlainObject
+/////////////////////////////////////////////////////////////////////
+
+inline const UnboxedLayout&
+UnboxedPlainObject::layout() const
+{
+    return group()->unboxedLayout();
+}
+
+/////////////////////////////////////////////////////////////////////
 // UnboxedArrayObject
 /////////////////////////////////////////////////////////////////////
 
+inline const UnboxedLayout&
+UnboxedArrayObject::layout() const
+{
+    return group()->unboxedLayout();
+}
+
 inline void
 UnboxedArrayObject::setLength(ExclusiveContext* cx, uint32_t length)
 {
     if (length > INT32_MAX) {
         // Track objects with overflowing lengths in type information.
         MarkObjectGroupFlags(cx, this, OBJECT_FLAG_LENGTH_OVERFLOW);
     }
 
@@ -261,24 +277,42 @@ GetAnyBoxedOrUnboxedInitializedLength(JS
 {
     if (obj->isNative())
         return obj->as<NativeObject>().getDenseInitializedLength();
     if (obj->is<UnboxedArrayObject>())
         return obj->as<UnboxedArrayObject>().initializedLength();
     return 0;
 }
 
+static inline size_t
+GetAnyBoxedOrUnboxedCapacity(JSObject* obj)
+{
+    if (obj->isNative())
+        return obj->as<NativeObject>().getDenseCapacity();
+    if (obj->is<UnboxedArrayObject>())
+        return obj->as<UnboxedArrayObject>().capacity();
+    return 0;
+}
+
 static inline Value
 GetAnyBoxedOrUnboxedDenseElement(JSObject* obj, size_t index)
 {
     if (obj->isNative())
         return obj->as<NativeObject>().getDenseElement(index);
     return obj->as<UnboxedArrayObject>().getElement(index);
 }
 
+static inline size_t
+GetAnyBoxedOrUnboxedArrayLength(JSObject* obj)
+{
+    if (obj->is<ArrayObject>())
+        return obj->as<ArrayObject>().length();
+    return obj->as<UnboxedArrayObject>().length();
+}
+
 static inline void
 SetAnyBoxedOrUnboxedArrayLength(JSContext* cx, JSObject* obj, size_t length)
 {
     if (obj->is<ArrayObject>()) {
         MOZ_ASSERT(length >= obj->as<ArrayObject>().length());
         obj->as<ArrayObject>().setLength(cx, length);
     } else {
         MOZ_ASSERT(length >= obj->as<UnboxedArrayObject>().length());
@@ -472,34 +506,36 @@ MoveBoxedOrUnboxedDenseElements(JSContex
     }
 
     return DenseElementResult::Success;
 }
 
 template <JSValueType Type>
 static inline DenseElementResult
 CopyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
-                                uint32_t srcStart, uint32_t length)
+                                uint32_t dstStart, uint32_t srcStart, uint32_t length)
 {
     MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(src));
     MOZ_ASSERT(HasBoxedOrUnboxedDenseElements<Type>(dst));
-    MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<Type>(dst) == 0);
+    MOZ_ASSERT(GetBoxedOrUnboxedInitializedLength<Type>(dst) == dstStart);
     MOZ_ASSERT(GetBoxedOrUnboxedCapacity<Type>(dst) >= length);
 
-    SetBoxedOrUnboxedInitializedLength<Type>(cx, dst, length);
+    SetBoxedOrUnboxedInitializedLength<Type>(cx, dst, dstStart + length);
 
     if (Type == JSVAL_TYPE_MAGIC) {
         const Value* vp = src->as<NativeObject>().getDenseElements() + srcStart;
-        dst->as<NativeObject>().initDenseElements(0, vp, length);
+        dst->as<NativeObject>().initDenseElements(dstStart, vp, length);
     } else {
         uint8_t* dstData = dst->as<UnboxedArrayObject>().elements();
         uint8_t* srcData = src->as<UnboxedArrayObject>().elements();
         size_t elementSize = UnboxedTypeSize(Type);
 
-        memcpy(dstData, srcData + srcStart * elementSize, length * elementSize);
+        memcpy(dstData + dstStart * elementSize,
+               srcData + srcStart * elementSize,
+               length * elementSize);
 
         // Add a post barrier if we might have copied a nursery pointer to dst.
         if (UnboxedTypeNeedsPostBarrier(Type) && !IsInsideNursery(dst))
             dst->runtimeFromMainThread()->gc.storeBuffer.putWholeCellFromMainThread(dst);
     }
 
     return DenseElementResult::Success;
 }
@@ -533,16 +569,28 @@ CallBoxedOrUnboxedSpecialization(F f, JS
         return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_STRING>();
     if (HasBoxedOrUnboxedDenseElements<JSVAL_TYPE_OBJECT>(obj))
         return f. DEPENDENT_TEMPLATE_HINT operator()<JSVAL_TYPE_OBJECT>();
     return DenseElementResult::Incomplete;
 }
 
 #undef DEPENDENT_TEMPLATE_HINT
 
+#define DefineBoxedOrUnboxedFunctor1(Signature, A)                      \
+struct Signature ## Functor {                                           \
+    A a;                                                                \
+    explicit Signature ## Functor(A a)                                  \
+      : a(a)                                                            \
+    {}                                                                  \
+    template <JSValueType Type>                                         \
+    DenseElementResult operator()() {                                   \
+        return Signature<Type>(a);                                      \
+    }                                                                   \
+}
+
 #define DefineBoxedOrUnboxedFunctor3(Signature, A, B, C)                \
 struct Signature ## Functor {                                           \
     A a; B b; C c;                                                      \
     Signature ## Functor(A a, B b, C c)                                 \
       : a(a), b(b), c(c)                                                \
     {}                                                                  \
     template <JSValueType Type>                                         \
     DenseElementResult operator()() {                                   \
@@ -592,16 +640,16 @@ SetOrExtendAnyBoxedOrUnboxedDenseElement
                                           ShouldUpdateTypes updateTypes);
 
 DenseElementResult
 MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
                                    uint32_t dstStart, uint32_t srcStart, uint32_t length);
 
 DenseElementResult
 CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
-                                   uint32_t srcStart, uint32_t length);
+                                   uint32_t dstStart, uint32_t srcStart, uint32_t length);
 
 void
 SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen);
 
 } // namespace js
 
 #endif // vm_UnboxedObject_inl_h
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -1276,17 +1276,17 @@ UnboxedArrayObject::shrinkElements(Exclu
     if (!newElements)
         return;
 
     elements_ = newElements;
     setCapacityIndex(newCapacityIndex);
 }
 
 bool
-UnboxedArrayObject::containsProperty(JSContext* cx, jsid id)
+UnboxedArrayObject::containsProperty(ExclusiveContext* cx, jsid id)
 {
     if (JSID_IS_INT(id) && uint32_t(JSID_TO_INT(id)) < initializedLength())
         return true;
     if (JSID_IS_ATOM(id) && JSID_TO_ATOM(id) == cx->names().length)
         return true;
     return false;
 }
 
@@ -1391,21 +1391,21 @@ UnboxedArrayObject::obj_setProperty(JSCo
             if (JSID_IS_INT(id)) {
                 if (obj->as<UnboxedArrayObject>().setElement(cx, JSID_TO_INT(id), v))
                     return result.succeed();
             } else {
                 uint32_t len;
                 if (!CanonicalizeArrayLengthValue(cx, v, &len))
                     return false;
                 UnboxedArrayObject* nobj = &obj->as<UnboxedArrayObject>();
-                nobj->setLength(cx, len);
                 if (len < nobj->initializedLength()) {
                     nobj->setInitializedLength(len);
                     nobj->shrinkElements(cx, len);
                 }
+                nobj->setLength(cx, len);
                 return result.succeed();
             }
 
             if (!convertToNative(cx, obj))
                 return false;
             return SetProperty(cx, obj, id, v, receiver, result);
         }
 
@@ -1434,16 +1434,25 @@ UnboxedArrayObject::obj_getOwnPropertyDe
     desc.object().set(nullptr);
     return true;
 }
 
 /* static */ bool
 UnboxedArrayObject::obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
                                        ObjectOpResult& result)
 {
+    if (obj->as<UnboxedArrayObject>().containsProperty(cx, id)) {
+        size_t initlen = obj->as<UnboxedArrayObject>().initializedLength();
+        if (JSID_IS_INT(id) && JSID_TO_INT(id) == int32_t(initlen - 1)) {
+            obj->as<UnboxedArrayObject>().setInitializedLength(initlen - 1);
+            obj->as<UnboxedArrayObject>().shrinkElements(cx, initlen - 1);
+            return result.succeed();
+        }
+    }
+
     if (!convertToNative(cx, obj))
         return false;
     return DeleteProperty(cx, obj, id, result);
 }
 
 /* static */ bool
 UnboxedArrayObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable)
 {
@@ -1954,24 +1963,24 @@ DefineBoxedOrUnboxedFunctor5(MoveBoxedOr
 DenseElementResult
 js::MoveAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* obj,
                                        uint32_t dstStart, uint32_t srcStart, uint32_t length)
 {
     MoveBoxedOrUnboxedDenseElementsFunctor functor(cx, obj, dstStart, srcStart, length);
     return CallBoxedOrUnboxedSpecialization(functor, obj);
 }
 
-DefineBoxedOrUnboxedFunctor5(CopyBoxedOrUnboxedDenseElements,
-                             JSContext*, JSObject*, JSObject*, uint32_t, uint32_t);
+DefineBoxedOrUnboxedFunctor6(CopyBoxedOrUnboxedDenseElements,
+                             JSContext*, JSObject*, JSObject*, uint32_t, uint32_t, uint32_t);
 
 DenseElementResult
 js::CopyAnyBoxedOrUnboxedDenseElements(JSContext* cx, JSObject* dst, JSObject* src,
-                                       uint32_t srcStart, uint32_t length)
+                                       uint32_t dstStart, uint32_t srcStart, uint32_t length)
 {
-    CopyBoxedOrUnboxedDenseElementsFunctor functor(cx, dst, src, srcStart, length);
+    CopyBoxedOrUnboxedDenseElementsFunctor functor(cx, dst, src, dstStart, srcStart, length);
     return CallBoxedOrUnboxedSpecialization(functor, dst);
 }
 
 DefineBoxedOrUnboxedFunctor3(SetBoxedOrUnboxedInitializedLength,
                              JSContext*, JSObject*, size_t);
 
 void
 js::SetAnyBoxedOrUnboxedInitializedLength(JSContext* cx, JSObject* obj, size_t initlen)
--- a/js/src/vm/UnboxedObject.h
+++ b/js/src/vm/UnboxedObject.h
@@ -243,19 +243,17 @@ class UnboxedPlainObject : public JSObje
                                              MutableHandle<JSPropertyDescriptor> desc);
 
     static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
                                    ObjectOpResult& result);
 
     static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties);
     static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable);
 
-    const UnboxedLayout& layout() const {
-        return group()->unboxedLayout();
-    }
+    inline const UnboxedLayout& layout() const;
 
     const UnboxedLayout& layoutDontCheckGeneration() const {
         return group()->unboxedLayoutDontCheckGeneration();
     }
 
     uint8_t* data() {
         return &data_[0];
     }
@@ -380,19 +378,17 @@ class UnboxedArrayObject : public JSObje
                                              MutableHandle<JSPropertyDescriptor> desc);
 
     static bool obj_deleteProperty(JSContext* cx, HandleObject obj, HandleId id,
                                    ObjectOpResult& result);
 
     static bool obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties);
     static bool obj_watch(JSContext* cx, HandleObject obj, HandleId id, HandleObject callable);
 
-    const UnboxedLayout& layout() const {
-        return group()->unboxedLayout();
-    }
+    inline const UnboxedLayout& layout() const;
 
     const UnboxedLayout& layoutDontCheckGeneration() const {
         return group()->unboxedLayoutDontCheckGeneration();
     }
 
     JSValueType elementType() const {
         return layoutDontCheckGeneration().elementType();
     }
@@ -434,17 +430,17 @@ class UnboxedArrayObject : public JSObje
     uint32_t capacityIndex() const {
         return (capacityIndexAndInitializedLength_ & CapacityMask) >> CapacityShift;
     }
 
     uint32_t capacity() const {
         return computeCapacity(capacityIndex(), length());
     }
 
-    bool containsProperty(JSContext* cx, jsid id);
+    bool containsProperty(ExclusiveContext* cx, jsid id);
 
     bool setElement(ExclusiveContext* cx, size_t index, const Value& v);
     bool initElement(ExclusiveContext* cx, size_t index, const Value& v);
     void initElementNoTypeChange(size_t index, const Value& v);
     Value getElement(size_t index);
 
     template <JSValueType Type> inline bool setElementSpecific(ExclusiveContext* cx, size_t index,
                                                                const Value& v);
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -22,16 +22,17 @@
 #include "nsIDebug2.h"
 #include "nsIDocShell.h"
 #include "amIAddonManager.h"
 #include "nsPIDOMWindow.h"
 #include "nsPrintfCString.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Services.h"
+#include "mozilla/dom/ScriptSettings.h"
 
 #include "nsContentUtils.h"
 #include "nsCCUncollectableMarker.h"
 #include "nsCycleCollectionNoteRootCallback.h"
 #include "nsCycleCollector.h"
 #include "nsScriptLoader.h"
 #include "jsfriendapi.h"
 #include "jsprf.h"
@@ -54,16 +55,17 @@
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
 
 using namespace mozilla;
 using namespace xpc;
 using namespace JS;
 using mozilla::dom::PerThreadAtomCache;
+using mozilla::dom::AutoEntryScript;
 
 /***************************************************************************/
 
 const char* const XPCJSRuntime::mStrings[] = {
     "constructor",          // IDX_CONSTRUCTOR
     "toString",             // IDX_TO_STRING
     "toSource",             // IDX_TO_SOURCE
     "lastResult",           // IDX_LAST_RESULT
@@ -1365,52 +1367,40 @@ XPCJSRuntime::GetWatchdogTimestamp(Watch
 
 void
 xpc::SimulateActivityCallback(bool aActive)
 {
     XPCJSRuntime::ActivityCallback(XPCJSRuntime::Get(), aActive);
 }
 
 // static
-JSContext*
-XPCJSRuntime::DefaultJSContextCallback(JSRuntime* rt)
+bool
+XPCJSRuntime::EnvironmentPreparer::invoke(HandleObject scope, js::ScriptEnvironmentPreparer::Closure& closure)
 {
-    MOZ_ASSERT(rt == Get()->Runtime());
-    return Get()->GetJSContextStack()->GetSafeJSContext();
+    MOZ_ASSERT(NS_IsMainThread());
+    nsIGlobalObject* global = NativeGlobal(scope);
+    NS_ENSURE_TRUE(global && global->GetGlobalJSObject(), false);
+    AutoEntryScript aes(global, "JS-engine-initiated execution");
+    aes.TakeOwnershipOfErrorReporting();
+    return closure(aes.cx());
 }
 
 // static
 void
 XPCJSRuntime::ActivityCallback(void* arg, bool active)
 {
     if (!active) {
         ProcessHangMonitor::ClearHang();
     }
 
     XPCJSRuntime* self = static_cast<XPCJSRuntime*>(arg);
     self->mWatchdogManager->RecordRuntimeActivity(active);
 }
 
 // static
-//
-// JS-CTypes creates and caches a JSContext that it uses when executing JS
-// callbacks. When we're notified that ctypes is about to call into some JS,
-// push the cx to maintain the integrity of the context stack.
-void
-XPCJSRuntime::CTypesActivityCallback(JSContext* cx, js::CTypesActivityType type)
-{
-  if (type == js::CTYPES_CALLBACK_BEGIN) {
-    if (!xpc::PushJSContextNoScriptContext(cx))
-      MOZ_CRASH();
-  } else if (type == js::CTYPES_CALLBACK_END) {
-    xpc::PopJSContextNoScriptContext();
-  }
-}
-
-// static
 bool
 XPCJSRuntime::InterruptCallback(JSContext* cx)
 {
     XPCJSRuntime* self = XPCJSRuntime::Get();
 
     // Normally we record mSlowScriptCheckpoint when we start to process an
     // event. However, we can run JS outside of event handlers. This code takes
     // care of that case.
@@ -3445,19 +3435,18 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
     JS_AddWeakPointerCallback(runtime, WeakPointerCallback, this);
     JS_SetWrapObjectCallbacks(runtime, &WrapObjectCallbacks);
     js::SetPreserveWrapperCallback(runtime, PreserveWrapper);
 #ifdef MOZ_ENABLE_PROFILER_SPS
     if (PseudoStack* stack = mozilla_get_pseudo_stack())
         stack->sampleRuntime(runtime);
 #endif
     JS_SetAccumulateTelemetryCallback(runtime, AccumulateTelemetryCallback);
-    js::SetDefaultJSContextCallback(runtime, DefaultJSContextCallback);
+    js::SetScriptEnvironmentPreparer(runtime, &mEnvironmentPreparer);
     js::SetActivityCallback(runtime, ActivityCallback, this);
-    js::SetCTypesActivityCallback(runtime, CTypesActivityCallback);
     JS_SetInterruptCallback(runtime, InterruptCallback);
 
     // The JS engine needs to keep the source code around in order to implement
     // Function.prototype.toSource(). It'd be nice to not have to do this for
     // chrome code and simply stub out requests for source on it. Life is not so
     // easy, unfortunately. Nobody relies on chrome toSource() working in core
     // browser code, but chrome tests use it. The worst offenders are addons,
     // which like to monkeypatch chrome functions by calling toSource() on them
--- a/js/xpconnect/src/XPCMaps.cpp
+++ b/js/xpconnect/src/XPCMaps.cpp
@@ -164,25 +164,17 @@ JSObject2WrappedJSMap::SizeOfWrappedJS(m
 
 /***************************************************************************/
 // implement Native2WrappedNativeMap...
 
 // static
 Native2WrappedNativeMap*
 Native2WrappedNativeMap::newMap(int length)
 {
-    Native2WrappedNativeMap* map = new Native2WrappedNativeMap(length);
-    if (map && map->mTable)
-        return map;
-    // Allocation of the map or the creation of its hash table has
-    // failed. This will cause a nullptr deref later when we attempt
-    // to use the map, so we abort immediately to provide a more
-    // useful crash stack.
-    NS_RUNTIMEABORT("Ran out of memory.");
-    return nullptr;
+    return new Native2WrappedNativeMap(length);
 }
 
 Native2WrappedNativeMap::Native2WrappedNativeMap(int length)
 {
     mTable = new PLDHashTable2(PL_DHashGetStubOps(), sizeof(Entry), length);
 }
 
 Native2WrappedNativeMap::~Native2WrappedNativeMap()
@@ -324,25 +316,17 @@ ClassInfo2NativeSetMap::ShallowSizeOfInc
 
 /***************************************************************************/
 // implement ClassInfo2WrappedNativeProtoMap...
 
 // static
 ClassInfo2WrappedNativeProtoMap*
 ClassInfo2WrappedNativeProtoMap::newMap(int length)
 {
-    ClassInfo2WrappedNativeProtoMap* map = new ClassInfo2WrappedNativeProtoMap(length);
-    if (map && map->mTable)
-        return map;
-    // Allocation of the map or the creation of its hash table has
-    // failed. This will cause a nullptr deref later when we attempt
-    // to use the map, so we abort immediately to provide a more
-    // useful crash stack.
-    NS_RUNTIMEABORT("Ran out of memory.");
-    return nullptr;
+    return new ClassInfo2WrappedNativeProtoMap(length);
 }
 
 ClassInfo2WrappedNativeProtoMap::ClassInfo2WrappedNativeProtoMap(int length)
 {
     mTable = new PLDHashTable2(PL_DHashGetStubOps(), sizeof(Entry), length);
 }
 
 ClassInfo2WrappedNativeProtoMap::~ClassInfo2WrappedNativeProtoMap()
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -55,16 +55,18 @@
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #include "nsICrashReporter.h"
 #endif
 
 using namespace mozilla;
 using namespace JS;
+using mozilla::dom::AutoJSAPI;
+using mozilla::dom::AutoEntryScript;
 
 class XPCShellDirProvider : public nsIDirectoryServiceProvider2
 {
 public:
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIDIRECTORYSERVICEPROVIDER
     NS_DECL_NSIDIRECTORYSERVICEPROVIDER2
 
@@ -98,17 +100,16 @@ static const char kXPConnectServiceContr
 #define EXITCODE_RUNTIME_ERROR 3
 #define EXITCODE_FILE_NOT_FOUND 4
 
 static FILE* gOutFile = nullptr;
 static FILE* gErrFile = nullptr;
 static FILE* gInFile = nullptr;
 
 static int gExitCode = 0;
-static bool gIgnoreReportedErrors = false;
 static bool gQuitting = false;
 static bool reportWarnings = true;
 static bool compileOnly = false;
 
 static JSPrincipals* gJSPrincipals = nullptr;
 static nsAutoString* gWorkingDirectory = nullptr;
 
 static bool
@@ -390,31 +391,16 @@ Quit(JSContext* cx, unsigned argc, jsval
     if (!ToInt32(cx, args.get(0), &gExitCode))
         return false;
 
     gQuitting = true;
 //    exit(0);
     return false;
 }
 
-// Provide script a way to disable the xpcshell error reporter, preventing
-// reported errors from being logged to the console and also from affecting the
-// exit code returned by the xpcshell binary.
-static bool
-IgnoreReportedErrors(JSContext* cx, unsigned argc, jsval* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    if (args.length() != 1 || !args[0].isBoolean()) {
-        JS_ReportError(cx, "Bad arguments");
-        return false;
-    }
-    gIgnoreReportedErrors = args[0].toBoolean();
-    return true;
-}
-
 static bool
 DumpXPC(JSContext* cx, unsigned argc, jsval* vp)
 {
     JS::CallArgs args = CallArgsFromVp(argc, vp);
 
     uint16_t depth = 2;
     if (args.length() > 0) {
         if (!JS::ToUint16(cx, args[0], &depth))
@@ -662,17 +648,16 @@ RegisterAppManifest(JSContext* cx, unsig
     return true;
 }
 
 static const JSFunctionSpec glob_functions[] = {
     JS_FS("print",           Print,          0,0),
     JS_FS("readline",        ReadLine,       1,0),
     JS_FS("load",            Load,           1,0),
     JS_FS("quit",            Quit,           0,0),
-    JS_FS("ignoreReportedErrors", IgnoreReportedErrors, 1,0),
     JS_FS("version",         Version,        1,0),
     JS_FS("build",           BuildDate,      0,0),
     JS_FS("dumpXPC",         DumpXPC,        1,0),
     JS_FS("dump",            Dump,           1,0),
     JS_FS("gc",              GC,             0,0),
 #ifdef JS_GC_ZEAL
     JS_FS("gczeal",          GCZeal,         1,0),
 #endif
@@ -825,146 +810,155 @@ static const JSErrorFormatString*
 my_GetErrorMessage(void* userRef, const unsigned errorNumber)
 {
     if (errorNumber == 0 || errorNumber >= JSShellErr_Limit)
         return nullptr;
 
     return &jsShell_ErrorFormatString[errorNumber];
 }
 
-static void
-ProcessFile(JSContext* cx, const char* filename, FILE* file, bool forceTTY)
+static bool
+ProcessLine(AutoJSAPI& jsapi, const char* buffer, int startline)
 {
+    JSContext* cx = jsapi.cx();
     JS::RootedScript script(cx);
     JS::RootedValue result(cx);
-    int lineno, startline;
-    bool ok, hitEOF;
-    char* bufp, buffer[4096];
-    JSString* str;
+    JS::CompileOptions options(cx);
+    options.setFileAndLine("typein", startline)
+           .setIsRunOnce(true);
+    if (!JS_CompileScript(cx, buffer, strlen(buffer), options, &script))
+        return false;
+    if (compileOnly)
+        return true;
+    if (!JS_ExecuteScript(cx, script, &result))
+        return false;
 
+    if (result.isUndefined())
+        return true;
+    RootedString str(cx);
+    if (!(str = ToString(cx, result)))
+        return false;
+    JSAutoByteString bytes;
+    if (!bytes.encodeLatin1(cx, str))
+        return false;
+
+    fprintf(gOutFile, "%s\n", bytes.ptr());
+    return true;
+}
+
+static bool
+ProcessFile(AutoJSAPI& jsapi, const char* filename, FILE* file, bool forceTTY)
+{
+    JSContext* cx = jsapi.cx();
     JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
     MOZ_ASSERT(global);
 
     if (forceTTY) {
         file = stdin;
-    } else if (!isatty(fileno(file)))
-    {
+    } else if (!isatty(fileno(file))) {
         /*
          * It's not interactive - just execute it.
          *
          * Support the UNIX #! shell hack; gobble the first line if it starts
          * with '#'.  TODO - this isn't quite compatible with sharp variables,
          * as a legal js program (using sharp variables) might start with '#'.
          * But that would require multi-character lookahead.
          */
         int ch = fgetc(file);
         if (ch == '#') {
             while ((ch = fgetc(file)) != EOF) {
                 if (ch == '\n' || ch == '\r')
                     break;
             }
         }
         ungetc(ch, file);
-        JS_BeginRequest(cx);
 
+        JS::RootedScript script(cx);
+        JS::RootedValue unused(cx);
         JS::CompileOptions options(cx);
         options.setUTF8(true)
                .setFileAndLine(filename, 1)
-               .setIsRunOnce(true);
-        if (JS::Compile(cx, options, file, &script) && !compileOnly)
-            (void)JS_ExecuteScript(cx, script, &result);
-        JS_EndRequest(cx);
-
-        return;
+               .setIsRunOnce(true)
+               .setNoScriptRval(true);
+        if (!JS::Compile(cx, options, file, &script))
+            return false;
+        return compileOnly || JS_ExecuteScript(cx, script, &unused);
     }
 
     /* It's an interactive filehandle; drop into read-eval-print loop. */
-    lineno = 1;
-    hitEOF = false;
+    int lineno = 1;
+    bool hitEOF = false;
     do {
-        bufp = buffer;
+        char buffer[4096];
+        char* bufp = buffer;
         *bufp = '\0';
 
         /*
          * Accumulate lines until we get a 'compilable unit' - one that either
          * generates an error (before running out of source) or that compiles
          * cleanly.  This should be whenever we get a complete statement that
          * coincides with the end of a line.
          */
-        startline = lineno;
+        int startline = lineno;
         do {
             if (!GetLine(cx, bufp, file, startline == lineno ? "js> " : "")) {
                 hitEOF = true;
                 break;
             }
             bufp += strlen(bufp);
             lineno++;
         } while (!JS_BufferIsCompilableUnit(cx, global, buffer, strlen(buffer)));
 
-        JS_BeginRequest(cx);
-        /* Clear any pending exception from previous failed compiles.  */
-        JS_ClearPendingException(cx);
-        JS::CompileOptions options(cx);
-        options.setFileAndLine("typein", startline)
-               .setIsRunOnce(true);
-        if (JS_CompileScript(cx, buffer, strlen(buffer), options, &script)) {
-            JSErrorReporter older;
-
-            if (!compileOnly) {
-                ok = JS_ExecuteScript(cx, script, &result);
-                if (ok && result != JSVAL_VOID) {
-                    /* Suppress error reports from JS::ToString(). */
-                    older = JS_SetErrorReporter(JS_GetRuntime(cx), nullptr);
-                    str = ToString(cx, result);
-                    JS_SetErrorReporter(JS_GetRuntime(cx), older);
-                    JSAutoByteString bytes;
-                    if (str && bytes.encodeLatin1(cx, str))
-                        fprintf(gOutFile, "%s\n", bytes.ptr());
-                    else
-                        ok = false;
-                }
-            }
-        }
-        JS_EndRequest(cx);
+        if (!ProcessLine(jsapi, buffer, startline))
+            jsapi.ClearException(); // Errors from interactive processing are squelched.
     } while (!hitEOF && !gQuitting);
 
     fprintf(gOutFile, "\n");
+    return true;
 }
 
-static void
-Process(JSContext* cx, const char* filename, bool forceTTY)
+static bool
+Process(AutoJSAPI& jsapi, const char* filename, bool forceTTY)
 {
     FILE* file;
 
     if (forceTTY || !filename || strcmp(filename, "-") == 0) {
         file = stdin;
     } else {
         file = fopen(filename, "r");
         if (!file) {
-            JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
+            JS_ReportErrorNumber(jsapi.cx(), my_GetErrorMessage, nullptr,
                                  JSSMSG_CANT_OPEN,
                                  filename, strerror(errno));
             gExitCode = EXITCODE_FILE_NOT_FOUND;
-            return;
+            return false;
         }
     }
 
-    ProcessFile(cx, filename, file, forceTTY);
+    bool ok = ProcessFile(jsapi, filename, file, forceTTY);
     if (file != stdin)
         fclose(file);
+    return ok;
 }
 
 static int
-usage(void)
+usage()
 {
     fprintf(gErrFile, "%s\n", JS_GetImplementationVersion());
     fprintf(gErrFile, "usage: xpcshell [-g gredir] [-a appdir] [-r manifest]... [-WwxiCSsmIp] [-v version] [-f scriptfile] [-e script] [scriptfile] [scriptarg...]\n");
     return 2;
 }
 
+static bool
+printUsageAndSetExitCode()
+{
+    gExitCode = usage();
+    return false;
+}
+
 static void
 ProcessArgsForCompartment(JSContext* cx, char** argv, int argc)
 {
     for (int i = 0; i < argc; i++) {
         if (argv[i][0] != '-' || argv[i][1] == '\0')
             break;
 
         switch (argv[i][1]) {
@@ -982,32 +976,36 @@ ProcessArgsForCompartment(JSContext* cx,
         case 'I':
             RuntimeOptionsRef(cx).toggleIon()
                                  .toggleAsmJS();
             break;
         }
     }
 }
 
-static int
-ProcessArgs(JSContext* cx, char** argv, int argc, XPCShellDirProvider* aDirProvider)
+static bool
+ProcessArgs(AutoJSAPI& jsapi, char** argv, int argc, XPCShellDirProvider* aDirProvider)
 {
+    JSContext* cx = jsapi.cx();
     const char rcfilename[] = "xpcshell.js";
     FILE* rcfile;
     int rootPosition;
     JS::Rooted<JSObject*> argsObj(cx);
     char* filename = nullptr;
     bool isInteractive = true;
     bool forceTTY = false;
 
     rcfile = fopen(rcfilename, "r");
     if (rcfile) {
         printf("[loading '%s'...]\n", rcfilename);
-        ProcessFile(cx, rcfilename, rcfile, false);
+        bool ok = ProcessFile(jsapi, rcfilename, rcfile, false);
         fclose(rcfile);
+        if (!ok) {
+            return false;
+        }
     }
 
     JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
 
     /*
      * Scan past all optional arguments so we can create the arguments object
      * before processing any -f options, which must interleave properly with
      * -v and -w options.  This requires two passes, and without getopt, we'll
@@ -1053,17 +1051,17 @@ ProcessArgs(JSContext* cx, char** argv, 
         if (argv[i][0] != '-' || argv[i][1] == '\0') {
             filename = argv[i++];
             isInteractive = false;
             break;
         }
         switch (argv[i][1]) {
         case 'v':
             if (++i == argc) {
-                return usage();
+                return printUsageAndSetExitCode();
             }
             JS_SetVersionForCompartment(js::GetContextCompartment(cx),
                                         JSVersion(atoi(argv[i])));
             break;
         case 'W':
             reportWarnings = false;
             break;
         case 'w':
@@ -1073,35 +1071,36 @@ ProcessArgs(JSContext* cx, char** argv, 
             break;
         case 'd':
             /* This used to try to turn on the debugger. */
             break;
         case 'm':
             break;
         case 'f':
             if (++i == argc) {
-                return usage();
+                return printUsageAndSetExitCode();
             }
-            Process(cx, argv[i], false);
+            if (!Process(jsapi, argv[i], false))
+                return false;
             /*
              * XXX: js -f foo.js should interpret foo.js and then
              * drop into interactive mode, but that breaks test
              * harness. Just execute foo.js for now.
              */
             isInteractive = false;
             break;
         case 'i':
             isInteractive = forceTTY = true;
             break;
         case 'e':
         {
             RootedValue rval(cx);
 
             if (++i == argc) {
-                return usage();
+                return printUsageAndSetExitCode();
             }
 
             JS::CompileOptions opts(cx);
             opts.setFileAndLine("-e", 1);
             JS::Evaluate(cx, opts, argv[i], strlen(argv[i]), &rval);
 
             isInteractive = false;
             break;
@@ -1117,30 +1116,29 @@ ProcessArgs(JSContext* cx, char** argv, 
             break;
         case 'p':
         {
           // plugins path
           char* pluginPath = argv[++i];
           nsCOMPtr<nsIFile> pluginsDir;
           if (NS_FAILED(XRE_GetFileFromPath(pluginPath, getter_AddRefs(pluginsDir)))) {
               fprintf(gErrFile, "Couldn't use given plugins dir.\n");
-              return usage();
+              return printUsageAndSetExitCode();
           }
           aDirProvider->SetPluginDir(pluginsDir);
           break;
         }
         default:
-            return usage();
+            return printUsageAndSetExitCode();
         }
     }
 
     if (filename || isInteractive)
-        Process(cx, filename, forceTTY);
-
-    return gExitCode;
+        return Process(jsapi, filename, forceTTY);
+    return true;
 }
 
 /***************************************************************************/
 
 // #define TEST_InitClassesWithNewWrappedGlobal
 
 #ifdef TEST_InitClassesWithNewWrappedGlobal
 // XXX hacky test code...
@@ -1206,29 +1204,16 @@ nsXPCFunctionThisTranslator::TranslateTh
 {
     nsCOMPtr<nsISupports> temp = aInitialThis;
     temp.forget(_retval);
     return NS_OK;
 }
 
 #endif
 
-static void
-XPCShellErrorReporter(JSContext* cx, const char* message, JSErrorReport* rep)
-{
-    if (gIgnoreReportedErrors)
-        return;
-
-    if (!JSREPORT_IS_WARNING(rep->flags))
-        gExitCode = EXITCODE_RUNTIME_ERROR;
-
-    // Delegate to the system error reporter for heavy lifting.
-    xpc::SystemErrorReporter(cx, message, rep);
-}
-
 static bool
 GetCurrentWorkingDirectory(nsAString& workingDirectory)
 {
 #if !defined(XP_WIN) && !defined(XP_UNIX)
     //XXX: your platform should really implement this
     return false;
 #elif XP_WIN
     DWORD requiredLength = GetCurrentDirectoryW(0, nullptr);
@@ -1263,17 +1248,17 @@ GetCurrentWorkingDirectory(nsAString& wo
 
 static JSSecurityCallbacks shellSecurityCallbacks;
 
 int
 XRE_XPCShellMain(int argc, char** argv, char** envp)
 {
     JSRuntime* rt;
     JSContext* cx;
-    int result;
+    int result = 0;
     nsresult rv;
 
     gErrFile = stderr;
     gOutFile = stdout;
     gInFile = stdin;
 
     NS_LogInit();
 
@@ -1428,19 +1413,17 @@ XRE_XPCShellMain(int argc, char** argv, 
         }
 
         // Override the default XPConnect interrupt callback. We could store the
         // old one and restore it before shutting down, but there's not really a
         // reason to bother.
         sScriptedInterruptCallback.init(rt, UndefinedValue());
         JS_SetInterruptCallback(rt, XPCShellInterruptCallback);
 
-        JS_SetErrorReporter(rt, XPCShellErrorReporter);
-
-        dom::AutoJSAPI jsapi;
+        AutoJSAPI jsapi;
         jsapi.Init();
         cx = jsapi.cx();
 
         argc--;
         argv++;
         ProcessArgsForCompartment(cx, argv, argc);
 
         nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
@@ -1541,24 +1524,38 @@ XRE_XPCShellMain(int argc, char** argv, 
             if (GetCurrentWorkingDirectory(workingDirectory))
                 gWorkingDirectory = &workingDirectory;
 
             JS_DefineProperty(cx, glob, "__LOCATION__", JS::UndefinedHandleValue,
                               JSPROP_SHARED,
                               GetLocationProperty,
                               nullptr);
 
-            // We are almost certainly going to run script here, so we need an
-            // AutoEntryScript. This is Gecko-specific and not in any spec.
-            dom::AutoEntryScript aes(backstagePass,
-                                     "xpcshell argument processing");
-            result = ProcessArgs(aes.cx(), argv, argc, &dirprovider);
+            {
+                // We are almost certainly going to run script here, so we need an
+                // AutoEntryScript. This is Gecko-specific and not in any spec.
+                AutoEntryScript aes(backstagePass, "xpcshell argument processing");
+
+                // If an exception is thrown, we'll set our return code
+                // appropriately, and then let the AutoJSAPI destructor report
+                // the error to the console.
+                aes.TakeOwnershipOfErrorReporting();
+                if (!ProcessArgs(aes, argv, argc, &dirprovider)) {
+                    if (gExitCode) {
+                        result = gExitCode;
+                    } else if (gQuitting) {
+                        result = 0;
+                    } else {
+                        result = EXITCODE_RUNTIME_ERROR;
+                    }
+                }
+            }
 
             JS_DropPrincipals(rt, gJSPrincipals);
-            JS_SetAllNonReservedSlotsToUndefined(aes.cx(), glob);
+            JS_SetAllNonReservedSlotsToUndefined(cx, glob);
             JS_GC(rt);
         }
         JS_GC(rt);
     } // this scopes the nsCOMPtrs
 
     if (!XRE_ShutdownTestShell())
         NS_ERROR("problem shutting down testshell");
 
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -944,17 +944,17 @@ nsXPConnect::OnProcessNextEvent(nsIThrea
     // Record this event.
     mEventDepth++;
 
     // Start the slow script timer.
     mRuntime->OnProcessNextEvent();
 
     // Push a null JSContext so that we don't see any script during
     // event processing.
-    bool ok = PushJSContextNoScriptContext(nullptr);
+    bool ok = PushNullJSContext();
     NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPConnect::AfterProcessNextEvent(nsIThreadInternal* aThread,
                                    uint32_t aRecursionDepth,
                                    bool aEventWasProcessed)
@@ -970,17 +970,17 @@ nsXPConnect::AfterProcessNextEvent(nsITh
     // Call cycle collector occasionally.
     MOZ_ASSERT(NS_IsMainThread());
     nsJSContext::MaybePokeCC();
 
     nsContentUtils::PerformMainThreadMicroTaskCheckpoint();
 
     Promise::PerformMicroTaskCheckpoint();
 
-    PopJSContextNoScriptContext();
+    PopNullJSContext();
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPConnect::OnDispatchedEvent(nsIThreadInternal* aThread)
 {
     NS_NOTREACHED("Why tell us?");
@@ -1050,25 +1050,25 @@ JSContext*
 nsXPConnect::GetSafeJSContext()
 {
     return GetRuntime()->GetJSContextStack()->GetSafeJSContext();
 }
 
 namespace xpc {
 
 bool
-PushJSContextNoScriptContext(JSContext* aCx)
+PushNullJSContext()
 {
-    MOZ_ASSERT_IF(aCx, !GetScriptContextFromJSContext(aCx));
-    return XPCJSRuntime::Get()->GetJSContextStack()->Push(aCx);
+    return XPCJSRuntime::Get()->GetJSContextStack()->Push(nullptr);
 }
 
 void
-PopJSContextNoScriptContext()
+PopNullJSContext()
 {
+    MOZ_ASSERT(XPCJSRuntime::Get()->GetJSContextStack()->Peek() == nullptr);
     XPCJSRuntime::Get()->GetJSContextStack()->Pop();
 }
 
 } // namespace xpc
 
 nsIPrincipal*
 nsXPConnect::GetPrincipal(JSObject* obj, bool allowShortCircuit) const
 {
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -600,20 +600,22 @@ public:
     ShortLivedStringBuffer<nsString> mScratchStrings;
     ShortLivedStringBuffer<nsCString> mScratchCStrings;
 
     void AddGCCallback(xpcGCCallback cb);
     void RemoveGCCallback(xpcGCCallback cb);
     void AddContextCallback(xpcContextCallback cb);
     void RemoveContextCallback(xpcContextCallback cb);
 
-    static JSContext* DefaultJSContextCallback(JSRuntime* rt);
+    struct EnvironmentPreparer : public js::ScriptEnvironmentPreparer {
+        bool invoke(JS::HandleObject scope, Closure& closure) override;
+    };
+    EnvironmentPreparer mEnvironmentPreparer;
+
     static void ActivityCallback(void* arg, bool active);
-    static void CTypesActivityCallback(JSContext* cx,
-                                       js::CTypesActivityType type);
     static bool InterruptCallback(JSContext* cx);
 
     size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
     AutoMarkingPtr**  GetAutoRootsAdr() {return &mAutoRoots;}
 
     JSObject* UnprivilegedJunkScope() { return mUnprivilegedJunkScope; }
     JSObject* PrivilegedJunkScope() { return mPrivilegedJunkScope; }
@@ -2837,24 +2839,18 @@ struct XPCJSContextInfo {
     {}
     JSContext* cx;
 
     // Whether the frame chain was saved
     bool savedFrameChain;
 };
 
 namespace xpc {
-
-// These functions are used in a few places where a callback model makes it
-// impossible to push a JSContext using one of our stack-scoped classes. We
-// depend on those stack-scoped classes to maintain nsIScriptContext
-// invariants, so these functions may only be used of the context is not
-// associated with an nsJSContext/nsIScriptContext.
-bool PushJSContextNoScriptContext(JSContext* aCx);
-void PopJSContextNoScriptContext();
+bool PushNullJSContext();
+void PopNullJSContext();
 
 } /* namespace xpc */
 
 namespace mozilla {
 namespace dom {
 namespace danger {
 class AutoCxPusher;
 }
@@ -2885,18 +2881,18 @@ public:
     JSContext* GetSafeJSContext();
     bool HasJSContext(JSContext* cx);
 
     const InfallibleTArray<XPCJSContextInfo>* GetStack()
     { return &mStack; }
 
 private:
     friend class mozilla::dom::danger::AutoCxPusher;
-    friend bool xpc::PushJSContextNoScriptContext(JSContext* aCx);
-    friend void xpc::PopJSContextNoScriptContext();
+    friend bool xpc::PushNullJSContext();
+    friend void xpc::PopNullJSContext();
 
     // We make these private so that stack manipulation can only happen
     // through one of the above friends.
     JSContext* Pop();
     bool Push(JSContext* cx);
 
     AutoInfallibleTArray<XPCJSContextInfo, 16> mStack;
     XPCJSRuntime* mRuntime;
--- a/js/xpconnect/tests/unit/test_bug809674.js
+++ b/js/xpconnect/tests/unit/test_bug809674.js
@@ -20,16 +20,12 @@ function test_property_throws(contractid
   var o = Cc[contractid].createInstance(Ci["nsIXPCTestBug809674"]);