Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 03 Jun 2015 14:00:51 +0200
changeset 269622 3f10ceab96e727c22dac31d7556b7ba8aab9553d
parent 269621 788b0589c1ac79ed5075626ae52f492134e17f82 (current diff)
parent 269514 6c612d7adbf565fdcf899250793a30718c735776 (diff)
child 269623 e56c2ee5836e1725361e0b73c08834be7965fd9d
push id2540
push userwcosta@mozilla.com
push dateWed, 03 Jun 2015 20:55:41 +0000
milestone41.0a1
Merge mozilla-central to fx-team
intl/uconv/ucvcn/gbkuniq2b.uf
--- 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="9b7ed13e0dee26b9f16ae5fbc076fa8bd588b256"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="477b5672811ed970a7476fe6f67dba546a302dce"/>
   <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="15a220c3e411f9606e4059e4c9fd0c57a4290c13"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c487ecd9afbcea1d507b9e4dfd09b3a8787fa65"/>
   <!-- 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="9b7ed13e0dee26b9f16ae5fbc076fa8bd588b256"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="477b5672811ed970a7476fe6f67dba546a302dce"/>
   <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="15a220c3e411f9606e4059e4c9fd0c57a4290c13"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c487ecd9afbcea1d507b9e4dfd09b3a8787fa65"/>
   <!-- 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="9b7ed13e0dee26b9f16ae5fbc076fa8bd588b256"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="477b5672811ed970a7476fe6f67dba546a302dce"/>
   <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="aac9cc4bb94cf720baf8f7ee419b4d76ac86b1ac"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="10b3daf0093db94c64e78a72ac43a93b68976087"/>
   <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="9b7ed13e0dee26b9f16ae5fbc076fa8bd588b256"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="477b5672811ed970a7476fe6f67dba546a302dce"/>
   <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="15a220c3e411f9606e4059e4c9fd0c57a4290c13"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c487ecd9afbcea1d507b9e4dfd09b3a8787fa65"/>
   <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="9b7ed13e0dee26b9f16ae5fbc076fa8bd588b256"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="477b5672811ed970a7476fe6f67dba546a302dce"/>
   <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="15a220c3e411f9606e4059e4c9fd0c57a4290c13"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c487ecd9afbcea1d507b9e4dfd09b3a8787fa65"/>
   <!-- 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="9b7ed13e0dee26b9f16ae5fbc076fa8bd588b256"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="477b5672811ed970a7476fe6f67dba546a302dce"/>
   <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="15a220c3e411f9606e4059e4c9fd0c57a4290c13"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c487ecd9afbcea1d507b9e4dfd09b3a8787fa65"/>
   <!-- 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="9b7ed13e0dee26b9f16ae5fbc076fa8bd588b256"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="477b5672811ed970a7476fe6f67dba546a302dce"/>
   <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="aac9cc4bb94cf720baf8f7ee419b4d76ac86b1ac"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="10b3daf0093db94c64e78a72ac43a93b68976087"/>
   <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="9b7ed13e0dee26b9f16ae5fbc076fa8bd588b256"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="477b5672811ed970a7476fe6f67dba546a302dce"/>
   <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="15a220c3e411f9606e4059e4c9fd0c57a4290c13"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c487ecd9afbcea1d507b9e4dfd09b3a8787fa65"/>
   <!-- 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": "9b7ed13e0dee26b9f16ae5fbc076fa8bd588b256", 
+        "git_revision": "477b5672811ed970a7476fe6f67dba546a302dce", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "d5e509fd9697316bcbc267413a30478dcd4dbca9", 
+    "revision": "5bab67b888d1491d1718cf1a7349bcef3114cbab", 
     "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="9b7ed13e0dee26b9f16ae5fbc076fa8bd588b256"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="477b5672811ed970a7476fe6f67dba546a302dce"/>
   <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="15a220c3e411f9606e4059e4c9fd0c57a4290c13"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c487ecd9afbcea1d507b9e4dfd09b3a8787fa65"/>
   <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="9b7ed13e0dee26b9f16ae5fbc076fa8bd588b256"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="477b5672811ed970a7476fe6f67dba546a302dce"/>
   <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="15a220c3e411f9606e4059e4c9fd0c57a4290c13"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5c487ecd9afbcea1d507b9e4dfd09b3a8787fa65"/>
   <!-- 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/browser/themes/tab-svgs.mozbuild
+++ b/browser/themes/tab-svgs.mozbuild
@@ -1,16 +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/.
 
-script = TOPSRCDIR + '/browser/themes/preprocess-tab-svgs.py'
-input = [TOPSRCDIR + '/browser/themes/shared/tab-selected.svg']
+script = '/browser/themes/preprocess-tab-svgs.py'
+input = ['/browser/themes/shared/tab-selected.svg']
 
 # Context variables can't be used inside functions, so hack around that.
 generated_files = GENERATED_FILES
 
 def generate_svg(svg_name, script_function):
     global generated_files
     generated_files += [svg_name]
     svg = generated_files[svg_name]
--- a/config/external/nspr/Makefile.in
+++ b/config/external/nspr/Makefile.in
@@ -19,19 +19,31 @@ ifdef MOZ_FOLD_LIBS
 EXTRA_MAKE_FLAGS := SHARED_LIBRARY= IMPORT_LIBRARY= SHARED_LIB_PDB=
 
 # Work around libVersionPoint conflict between all three libraries.
 # See bug #838566.
 EXTRA_MAKE_FLAGS += XP_DEFINE=-DlibVersionPoint='libVersionPoint$$(LIBRARY_NAME)'
 else
 # nspr's make export compiles and links everything, but linking can't happen
 # during export on platforms where nspr is linked against mozcrt/mozglue.
-export:: EXTRA_MAKE_FLAGS := SHARED_LIBRARY= IMPORT_LIBRARY= SHARED_LIB_PDB=
+export:: EXTRA_MAKE_FLAGS += SHARED_LIBRARY= IMPORT_LIBRARY= SHARED_LIB_PDB=
 endif
 
+MOZ_BUILDID := $(shell cat $(DEPTH)/config/buildid)
+
+# The NSPR build system uses build-time generated dates for public API
+# exposed data structures. Use the buildid as forced date, to avoid
+# having to deal with what changing NSPR itself might mean.
+
+# SH_DATE is a date with the format "%Y-%m-%d %T"
+EXTRA_MAKE_FLAGS += SH_DATE="$(shell $(PYTHON) -c 'd = "$(MOZ_BUILDID)"; print d[0:4]+"-"+d[4:6]+"-"+d[6:8]+" "+d[8:10]+":"+d[10:12]+":"+d[12:14]')"
+
+# SH_NOW is a date as a unix timestamp in ┬Áseconds
+EXTRA_MAKE_FLAGS += SH_NOW="$(shell $(PYTHON) -c 'import time, calendar; print calendar.timegm(time.strptime("$(MOZ_BUILDID)", "%Y%m%d%H%M%S"))')000000"
+
 clean distclean export::
 	$(MAKE) -C $(DEPTH)/nsprpub $@ $(EXTRA_MAKE_FLAGS)
 
 target::
 # nspr's libs and install rule re-export headers, and that can race with other
 # compilations, so use a separate directory here. The headers are exported
 # during export anyways.
 	$(MAKE) -C $(DEPTH)/nsprpub libs $(EXTRA_MAKE_FLAGS) dist_includedir=$(ABS_DIST)/nspr-include
--- a/dom/base/DOMException.cpp
+++ b/dom/base/DOMException.cpp
@@ -719,10 +719,22 @@ DOMException::Create(nsresult aRv)
   nsCString message;
   uint16_t code;
   NSResultToNameAndMessage(aRv, name, message, &code);
   nsRefPtr<DOMException> inst =
     new DOMException(aRv, message, name, code);
   return inst.forget();
 }
 
+/* static */already_AddRefed<DOMException>
+DOMException::Create(nsresult aRv, const nsCString& aMessage)
+{
+  nsCString name;
+  nsCString message;
+  uint16_t code;
+  NSResultToNameAndMessage(aRv, name, message, &code);
+  nsRefPtr<DOMException> inst =
+    new DOMException(aRv, aMessage, name, code);
+  return inst.forget();
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/DOMException.h
+++ b/dom/base/DOMException.h
@@ -152,16 +152,19 @@ public:
 
   // Intentionally shadow the nsXPCException version.
   void GetMessageMoz(nsString& retval);
   void GetName(nsString& retval);
 
   static already_AddRefed<DOMException>
   Create(nsresult aRv);
 
+  static already_AddRefed<DOMException>
+  Create(nsresult aRv, const nsCString& aMessage);
+
 protected:
 
   virtual ~DOMException() {}
 
   nsCString mName;
   nsCString mMessage;
 
   uint16_t mCode;
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -112,16 +112,17 @@
 #include "mozilla/Hal.h"
 #endif
 #include "mozilla/dom/ContentChild.h"
 
 #include "mozilla/dom/FeatureList.h"
 
 #ifdef MOZ_EME
 #include "mozilla/EMEUtils.h"
+#include "mozilla/DetailedPromise.h"
 #endif
 
 #ifdef MOZ_WIDGET_GONK
 #include <cutils/properties.h>
 #endif
 
 namespace mozilla {
 namespace dom {
@@ -2740,17 +2741,17 @@ Navigator::GetUserAgent(nsPIDOMWindow* a
 
 #ifdef MOZ_EME
 already_AddRefed<Promise>
 Navigator::RequestMediaKeySystemAccess(const nsAString& aKeySystem,
                                        const Optional<Sequence<MediaKeySystemOptions>>& aOptions,
                                        ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
-  nsRefPtr<Promise> promise = Promise::Create(go, aRv);
+  nsRefPtr<DetailedPromise> promise = DetailedPromise::Create(go, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   if (!mMediaKeySystemAccessManager) {
     mMediaKeySystemAccessManager = new MediaKeySystemAccessManager(mWindow);
   }
 
--- a/dom/base/nsContentList.cpp
+++ b/dom/base/nsContentList.cpp
@@ -148,17 +148,17 @@ NS_IMPL_RELEASE_INHERITED(nsSimpleConten
 
 JSObject*
 nsSimpleContentList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
 {
   return NodeListBinding::Wrap(cx, this, aGivenProto);
 }
 
 // Hashtable for storing nsContentLists
-static PLDHashTable2* gContentListHashTable;
+static PLDHashTable* gContentListHashTable;
 
 #define RECENTLY_USED_CONTENT_LIST_CACHE_SIZE 31
 static nsContentList*
   sRecentlyUsedContentLists[RECENTLY_USED_CONTENT_LIST_CACHE_SIZE] = {};
 
 static MOZ_ALWAYS_INLINE uint32_t
 RecentlyUsedCacheIndex(const nsContentListKey& aKey)
 {
@@ -212,17 +212,17 @@ NS_GetContentList(nsINode* aRootNode,
     ContentListHashtableMatchEntry,
     PL_DHashMoveEntryStub,
     PL_DHashClearEntryStub
   };
 
   // Initialize the hashtable if needed.
   if (!gContentListHashTable) {
     gContentListHashTable =
-      new PLDHashTable2(&hash_table_ops, sizeof(ContentListHashEntry));
+      new PLDHashTable(&hash_table_ops, sizeof(ContentListHashEntry));
   }
 
   ContentListHashEntry *entry = nullptr;
   // First we look in our hashtable.  Then we create a content list if needed
   entry = static_cast<ContentListHashEntry *>
     (PL_DHashTableAdd(gContentListHashTable, &hashKey, fallible));
   if (entry)
     list = entry->mContentList;
@@ -265,17 +265,17 @@ nsCacheableFuncStringNodeList::WrapObjec
 
 JSObject*
 nsCacheableFuncStringHTMLCollection::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLCollectionBinding::Wrap(cx, this, aGivenProto);
 }
 
 // Hashtable for storing nsCacheableFuncStringContentList
-static PLDHashTable2* gFuncStringContentListHashTable;
+static PLDHashTable* gFuncStringContentListHashTable;
 
 struct FuncStringContentListHashEntry : public PLDHashEntryHdr
 {
   nsCacheableFuncStringContentList* mContentList;
 };
 
 static PLDHashNumber
 FuncStringContentListHashtableHashKey(PLDHashTable *table, const void *key)
@@ -316,17 +316,17 @@ GetFuncStringContentList(nsINode* aRootN
     FuncStringContentListHashtableMatchEntry,
     PL_DHashMoveEntryStub,
     PL_DHashClearEntryStub
   };
 
   // Initialize the hashtable if needed.
   if (!gFuncStringContentListHashTable) {
     gFuncStringContentListHashTable =
-      new PLDHashTable2(&hash_table_ops, sizeof(FuncStringContentListHashEntry));
+      new PLDHashTable(&hash_table_ops, sizeof(FuncStringContentListHashEntry));
   }
 
   FuncStringContentListHashEntry *entry = nullptr;
   // First we look in our hashtable.  Then we create a content list if needed
   if (gFuncStringContentListHashTable) {
     nsFuncStringCacheKey hashKey(aRootNode, aFunc, aString);
 
     entry = static_cast<FuncStringContentListHashEntry *>
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -331,17 +331,17 @@ static const nsAttrValue::EnumTable kAut
   { 0 }
 };
 
 namespace {
 
 static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
 static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
 
-static PLDHashTable2* sEventListenerManagersHash;
+static PLDHashTable* sEventListenerManagersHash;
 
 class DOMEventListenerManagersHashReporter final : public nsIMemoryReporter
 {
   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
 
   ~DOMEventListenerManagersHashReporter() {}
 
 public:
@@ -493,17 +493,17 @@ nsContentUtils::Init()
       PL_DHashVoidPtrKeyStub,
       PL_DHashMatchEntryStub,
       PL_DHashMoveEntryStub,
       EventListenerManagerHashClearEntry,
       EventListenerManagerHashInitEntry
     };
 
     sEventListenerManagersHash =
-      new PLDHashTable2(&hash_table_ops, sizeof(EventListenerManagerMapEntry));
+      new PLDHashTable(&hash_table_ops, sizeof(EventListenerManagerMapEntry));
 
     RegisterStrongMemoryReporter(new DOMEventListenerManagersHashReporter());
   }
 
   sBlockedScriptRunners = new nsTArray< nsCOMPtr<nsIRunnable> >;
 
   Preferences::AddBoolVarCache(&sAllowXULXBL_for_file,
                                "dom.allow_XUL_XBL_for_file");
@@ -3334,17 +3334,17 @@ nsContentUtils::LogSimpleConsoleError(co
       console->LogMessage(scriptError);
     }
   }
 }
 
 /* static */ nsresult
 nsContentUtils::ReportToConsole(uint32_t aErrorFlags,
                                 const nsACString& aCategory,
-                                nsIDocument* aDocument,
+                                const nsIDocument* aDocument,
                                 PropertiesFile aFile,
                                 const char *aMessageName,
                                 const char16_t **aParams,
                                 uint32_t aParamsLength,
                                 nsIURI* aURI,
                                 const nsAFlatString& aSourceLine,
                                 uint32_t aLineNumber,
                                 uint32_t aColumnNumber)
@@ -3369,17 +3369,17 @@ nsContentUtils::ReportToConsole(uint32_t
                                      aLineNumber, aColumnNumber);
 }
 
 
 /* static */ nsresult
 nsContentUtils::ReportToConsoleNonLocalized(const nsAString& aErrorText,
                                             uint32_t aErrorFlags,
                                             const nsACString& aCategory,
-                                            nsIDocument* aDocument,
+                                            const nsIDocument* aDocument,
                                             nsIURI* aURI,
                                             const nsAFlatString& aSourceLine,
                                             uint32_t aLineNumber,
                                             uint32_t aColumnNumber)
 {
   uint64_t innerWindowID = 0;
   if (aDocument) {
     if (!aURI) {
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -770,17 +770,17 @@ public:
               containing error.
    *   @param [aColumnNumber=0] (Optional) Column number within resource
               containing error.
               If aURI is null, then aDocument->GetDocumentURI() is used.
    */
   static nsresult ReportToConsoleNonLocalized(const nsAString& aErrorText,
                                               uint32_t aErrorFlags,
                                               const nsACString& aCategory,
-                                              nsIDocument* aDocument,
+                                              const nsIDocument* aDocument,
                                               nsIURI* aURI = nullptr,
                                               const nsAFlatString& aSourceLine
                                                 = EmptyString(),
                                               uint32_t aLineNumber = 0,
                                               uint32_t aColumnNumber = 0);
 
   /**
    * Report a localized error message to the error console.
@@ -815,17 +815,17 @@ public:
     eCOMMON_DIALOG_PROPERTIES,
     eMATHML_PROPERTIES,
     eSECURITY_PROPERTIES,
     eNECKO_PROPERTIES,
     PropertiesFile_COUNT
   };
   static nsresult ReportToConsole(uint32_t aErrorFlags,
                                   const nsACString& aCategory,
-                                  nsIDocument* aDocument,
+                                  const nsIDocument* aDocument,
                                   PropertiesFile aFile,
                                   const char *aMessageName,
                                   const char16_t **aParams = nullptr,
                                   uint32_t aParamsLength = 0,
                                   nsIURI* aURI = nullptr,
                                   const nsAFlatString& aSourceLine
                                     = EmptyString(),
                                   uint32_t aLineNumber = 0,
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -2025,17 +2025,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
     cb.NoteXPCOMChild(tmp->mFrameRequestCallbacks[i].mCallback.GetISupports());
   }
 
   // Traverse animation components
   if (tmp->mAnimationController) {
     tmp->mAnimationController->Traverse(&cb);
   }
 
-  if (tmp->mSubDocuments && tmp->mSubDocuments->IsInitialized()) {
+  if (tmp->mSubDocuments) {
     PL_DHashTableEnumerate(tmp->mSubDocuments, SubDocTraverser, &cb);
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader)
 
   // We own only the items in mDOMMediaQueryLists that have listeners;
   // this reference is managed by their AddListener and RemoveListener
   // methods.
@@ -3981,17 +3981,17 @@ nsDocument::SetSubDocumentFor(Element* a
       {
         PL_DHashVoidPtrKeyStub,
         PL_DHashMatchEntryStub,
         PL_DHashMoveEntryStub,
         SubDocClearEntry,
         SubDocInitEntry
       };
 
-      mSubDocuments = new PLDHashTable2(&hash_table_ops, sizeof(SubDocMapEntry));
+      mSubDocuments = new PLDHashTable(&hash_table_ops, sizeof(SubDocMapEntry));
     }
 
     // Add a mapping to the hash table
     SubDocMapEntry *entry = static_cast<SubDocMapEntry*>
       (PL_DHashTableAdd(mSubDocuments, aElement, fallible));
 
     if (!entry) {
       return NS_ERROR_OUT_OF_MEMORY;
@@ -7528,19 +7528,18 @@ nsIDocument::SetDir(const nsAString& aDi
 NS_IMETHODIMP
 nsDocument::GetInputEncoding(nsAString& aInputEncoding)
 {
   nsIDocument::GetInputEncoding(aInputEncoding);
   return NS_OK;
 }
 
 void
-nsIDocument::GetInputEncoding(nsAString& aInputEncoding)
-{
-  // Not const function, because WarnOnceAbout is not a const method
+nsIDocument::GetInputEncoding(nsAString& aInputEncoding) const
+{
   WarnOnceAbout(eInputEncoding);
   if (mHaveInputEncoding) {
     return GetCharacterSet(aInputEncoding);
   }
 
   SetDOMStringToNull(aInputEncoding);
 }
 
@@ -10417,53 +10416,55 @@ static const char* kDeprecationWarnings[
 #define DOCUMENT_WARNING(_op) #_op "Warning",
 static const char* kDocumentWarnings[] = {
 #include "nsDocumentWarningList.h"
   nullptr
 };
 #undef DOCUMENT_WARNING
 
 bool
-nsIDocument::HasWarnedAbout(DeprecatedOperations aOperation)
+nsIDocument::HasWarnedAbout(DeprecatedOperations aOperation) const
 {
   static_assert(eDeprecatedOperationCount <= 64,
                 "Too many deprecated operations");
   return mDeprecationWarnedAbout & (1ull << aOperation);
 }
 
 void
 nsIDocument::WarnOnceAbout(DeprecatedOperations aOperation,
-                           bool asError /* = false */)
-{
+                           bool asError /* = false */) const
+{
+  MOZ_ASSERT(NS_IsMainThread());
   if (HasWarnedAbout(aOperation)) {
     return;
   }
   mDeprecationWarnedAbout |= (1ull << aOperation);
   uint32_t flags = asError ? nsIScriptError::errorFlag
                            : nsIScriptError::warningFlag;
   nsContentUtils::ReportToConsole(flags,
                                   NS_LITERAL_CSTRING("DOM Core"), this,
                                   nsContentUtils::eDOM_PROPERTIES,
                                   kDeprecationWarnings[aOperation]);
 }
 
 bool
-nsIDocument::HasWarnedAbout(DocumentWarnings aWarning)
+nsIDocument::HasWarnedAbout(DocumentWarnings aWarning) const
 {
   static_assert(eDocumentWarningCount <= 64,
                 "Too many document warnings");
   return mDocWarningWarnedAbout & (1ull << aWarning);
 }
 
 void
 nsIDocument::WarnOnceAbout(DocumentWarnings aWarning,
                            bool asError /* = false */,
                            const char16_t **aParams /* = nullptr */,
-                           uint32_t aParamsLength /* = 0 */)
-{
+                           uint32_t aParamsLength /* = 0 */) const
+{
+  MOZ_ASSERT(NS_IsMainThread());
   if (HasWarnedAbout(aWarning)) {
     return;
   }
   mDocWarningWarnedAbout |= (1ull << aWarning);
   uint32_t flags = asError ? nsIScriptError::errorFlag
                            : nsIScriptError::warningFlag;
   nsContentUtils::ReportToConsole(flags,
                                   NS_LITERAL_CSTRING("DOM Core"), this,
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -1515,17 +1515,17 @@ protected:
   virtual ~nsDocument();
 
   void EnsureOnloadBlocker();
 
   void NotifyStyleSheetApplicableStateChanged();
 
   nsTArray<nsIObserver*> mCharSetObservers;
 
-  PLDHashTable2 *mSubDocuments;
+  PLDHashTable *mSubDocuments;
 
   // Array of owning references to all children
   nsAttrAndChildArray mChildren;
 
   // Pointer to our parser if we're currently in the process of being
   // parsed into.
   nsCOMPtr<nsIParser> mParser;
 
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -1044,17 +1044,17 @@ public:
   {
     nsPIDOMWindow *window = GetWindow();
     return window ? window->WindowID() : 0;
   }
 
   /**
    * Return the inner window ID.
    */
-  uint64_t InnerWindowID()
+  uint64_t InnerWindowID() const
   {
     nsPIDOMWindow *window = GetInnerWindow();
     return window ? window->WindowID() : 0;
   }
 
   /**
    * Get the script loader for this document
    */ 
@@ -2161,30 +2161,31 @@ public:
   void FlushPendingLinkUpdates();
 
 #define DEPRECATED_OPERATION(_op) e##_op,
   enum DeprecatedOperations {
 #include "nsDeprecatedOperationList.h"
     eDeprecatedOperationCount
   };
 #undef DEPRECATED_OPERATION
-  bool HasWarnedAbout(DeprecatedOperations aOperation);
-  void WarnOnceAbout(DeprecatedOperations aOperation, bool asError = false);
+  bool HasWarnedAbout(DeprecatedOperations aOperation) const;
+  void WarnOnceAbout(DeprecatedOperations aOperation,
+                     bool asError = false) const;
 
 #define DOCUMENT_WARNING(_op) e##_op,
   enum DocumentWarnings {
 #include "nsDocumentWarningList.h"
     eDocumentWarningCount
   };
 #undef DOCUMENT_WARNING
-  bool HasWarnedAbout(DocumentWarnings aWarning);
+  bool HasWarnedAbout(DocumentWarnings aWarning) const;
   void WarnOnceAbout(DocumentWarnings aWarning,
                      bool asError = false,
                      const char16_t **aParams = nullptr,
-                     uint32_t aParamsLength = 0);
+                     uint32_t aParamsLength = 0) const;
 
   virtual void PostVisibilityUpdateEvent() = 0;
   
   bool IsSyntheticDocument() const { return mIsSyntheticDocument; }
 
   void SetNeedLayoutFlush() {
     mNeedLayoutFlush = true;
     if (mDisplayDocument) {
@@ -2374,17 +2375,17 @@ public:
   already_AddRefed<mozilla::dom::CDATASection>
     CreateCDATASection(const nsAString& aData, mozilla::ErrorResult& rv);
   already_AddRefed<mozilla::dom::Attr>
     CreateAttribute(const nsAString& aName, mozilla::ErrorResult& rv);
   already_AddRefed<mozilla::dom::Attr>
     CreateAttributeNS(const nsAString& aNamespaceURI,
                       const nsAString& aQualifiedName,
                       mozilla::ErrorResult& rv);
-  void GetInputEncoding(nsAString& aInputEncoding);
+  void GetInputEncoding(nsAString& aInputEncoding) const;
   already_AddRefed<nsLocation> GetLocation() const;
   void GetReferrer(nsAString& aReferrer) const;
   void GetLastModified(nsAString& aLastModified) const;
   void GetReadyState(nsAString& aReadyState) const;
   // Not const because otherwise the compiler can't figure out whether to call
   // this GetTitle or the nsAString version from non-const methods, since
   // neither is an exact match.
   virtual void GetTitle(nsString& aTitle) = 0;
@@ -2419,26 +2420,26 @@ public:
   void MozExitPointerLock()
   {
     UnlockPointer(this);
   }
   bool Hidden() const
   {
     return mVisibilityState != mozilla::dom::VisibilityState::Visible;
   }
-  bool MozHidden() // Not const because of WarnOnceAbout
+  bool MozHidden() const
   {
     WarnOnceAbout(ePrefixedVisibilityAPI);
     return Hidden();
   }
-  mozilla::dom::VisibilityState VisibilityState()
+  mozilla::dom::VisibilityState VisibilityState() const
   {
     return mVisibilityState;
   }
-  mozilla::dom::VisibilityState MozVisibilityState()
+  mozilla::dom::VisibilityState MozVisibilityState() const
   {
     WarnOnceAbout(ePrefixedVisibilityAPI);
     return VisibilityState();
   }
   virtual mozilla::dom::StyleSheetList* StyleSheets() = 0;
   void GetSelectedStyleSheetSet(nsAString& aSheetSet);
   virtual void SetSelectedStyleSheetSet(const nsAString& aSheetSet) = 0;
   virtual void GetLastStyleSheetSet(nsString& aSheetSet) = 0;
@@ -2541,18 +2542,18 @@ public:
   }
 
   // FontFaceSource
   mozilla::dom::FontFaceSet* GetFonts(mozilla::ErrorResult& aRv);
 
   bool DidFireDOMContentLoaded() const { return mDidFireDOMContentLoaded; }
 
 private:
-  uint64_t mDeprecationWarnedAbout;
-  uint64_t mDocWarningWarnedAbout;
+  mutable uint64_t mDeprecationWarnedAbout;
+  mutable uint64_t mDocWarningWarnedAbout;
   SelectorCache mSelectorCache;
 
 protected:
   ~nsIDocument();
   nsPropertyTable* GetExtraPropertyTable(uint16_t aCategory);
 
   // Never ever call this. Only call GetWindow!
   virtual nsPIDOMWindow *GetWindowInternal() const = 0;
--- a/dom/base/nsImageLoadingContent.cpp
+++ b/dom/base/nsImageLoadingContent.cpp
@@ -297,24 +297,52 @@ nsImageLoadingContent::OnLoadComplete(im
   }
 
   nsCOMPtr<nsINode> thisNode = do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   nsSVGEffects::InvalidateDirectRenderingObservers(thisNode->AsElement());
 
   return NS_OK;
 }
 
+static bool
+ImageIsAnimated(imgIRequest* aRequest)
+{
+  if (!aRequest) {
+    return false;
+  }
+
+  nsCOMPtr<imgIContainer> image;
+  if (NS_SUCCEEDED(aRequest->GetImage(getter_AddRefs(image)))) {
+    bool isAnimated = false;
+    nsresult rv = image->GetAnimated(&isAnimated);
+    if (NS_SUCCEEDED(rv) && isAnimated) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 void
 nsImageLoadingContent::OnUnlockedDraw()
 {
   if (mVisibleCount > 0) {
     // We should already be marked as visible, there is nothing more we can do.
     return;
   }
 
+  // It's OK for non-animated images to wait until the next image visibility
+  // update to become locked. (And that's preferable, since in the case of
+  // scrolling it keeps memory usage minimal.) For animated images, though, we
+  // want to mark them visible right away so we can call
+  // IncrementAnimationConsumers() on them and they'll start animating.
+  if (!ImageIsAnimated(mCurrentRequest) && !ImageIsAnimated(mPendingRequest)) {
+    return;
+  }
+
   nsPresContext* presContext = GetFramePresContext();
   if (!presContext)
     return;
 
   nsIPresShell* presShell = presContext->PresShell();
   if (!presShell)
     return;
 
--- a/dom/base/nsPropertyTable.cpp
+++ b/dom/base/nsPropertyTable.cpp
@@ -52,17 +52,17 @@ public:
   bool Equals(nsIAtom *aPropertyName)
   {
     return mName == aPropertyName;
   }
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
 
   nsCOMPtr<nsIAtom>  mName;           // property name
-  PLDHashTable2      mObjectValueMap; // map of object/value pairs
+  PLDHashTable       mObjectValueMap; // map of object/value pairs
   NSPropertyDtorFunc mDtorFunc;       // property specific value dtor function
   void*              mDtorData;       // pointer to pass to dtor
   bool               mTransfer;       // whether to transfer in
                                       // TransferOrDeleteAllPropertiesFor
   
   PropertyList*      mNext;
 };
 
@@ -211,21 +211,16 @@ nsPropertyTable::SetPropertyInternal(nsP
         aTransfer != propertyList->mTransfer) {
       NS_WARNING("Destructor/data mismatch while setting property");
       return NS_ERROR_INVALID_ARG;
     }
 
   } else {
     propertyList = new PropertyList(aPropertyName, aPropDtorFunc,
                                     aPropDtorData, aTransfer);
-    if (!propertyList || !propertyList->mObjectValueMap.IsInitialized()) {
-      delete propertyList;
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-
     propertyList->mNext = mPropertyList;
     mPropertyList = propertyList;
   }
 
   // The current property value (if there is one) is replaced and the current
   // value is destroyed
   nsresult result = NS_OK;
   PropertyListMapEntry *entry = static_cast<PropertyListMapEntry*>
--- a/dom/base/nsScriptNameSpaceManager.h
+++ b/dom/base/nsScriptNameSpaceManager.h
@@ -229,13 +229,13 @@ private:
   nsresult OperateCategoryEntryHash(nsICategoryManager* aCategoryManager,
                                     const char* aCategory,
                                     nsISupports* aEntry,
                                     bool aRemove);
 
   nsGlobalNameStruct* LookupNameInternal(const nsAString& aName,
                                          const char16_t **aClassName = nullptr);
 
-  PLDHashTable2 mGlobalNames;
-  PLDHashTable2 mNavigatorNames;
+  PLDHashTable mGlobalNames;
+  PLDHashTable mNavigatorNames;
 };
 
 #endif /* nsScriptNameSpaceManager_h__ */
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -10452,25 +10452,23 @@ class CGDOMJSProxyHandler_defineProperty
                 """,
                 callSetter=CGProxyIndexedSetter(self.descriptor).define())
         elif self.descriptor.supportsIndexedProperties():
             # We allow untrusted content to prevent Xrays from setting a
             # property if that property is an indexed property and we have no
             # indexed setter.  That's how the object would normally behave if
             # you tried to set the property on it.  That means we don't need to
             # do anything special for Xrays here.
-            set += fill(
+            set += dedent(
                 """
                 if (IsArrayIndex(GetArrayIndexFromId(cx, id))) {
-                  return js::IsInNonStrictPropertySet(cx)
-                         ? opresult.succeed()
-                         : ThrowErrorMessage(cx, MSG_NO_INDEXED_SETTER, "${name}");
+                  *defined = true;
+                  return opresult.failNoIndexedSetter();
                 }
-                """,
-                name=self.descriptor.name)
+                """)
 
         namedSetter = self.descriptor.operations['NamedSetter']
         if namedSetter:
             if self.descriptor.hasUnforgeableMembers:
                 raise TypeError("Can't handle a named setter on an interface "
                                 "that has unforgeables.  Figure out how that "
                                 "should work!")
             if self.descriptor.operations['NamedCreator'] is not namedSetter:
@@ -10493,23 +10491,21 @@ class CGDOMJSProxyHandler_defineProperty
             # means we don't need to do anything special for Xrays here.
             if self.descriptor.supportsNamedProperties():
                 set += fill(
                     """
                     bool found = false;
                     $*{presenceChecker}
 
                     if (found) {
-                      return js::IsInNonStrictPropertySet(cx)
-                             ? opresult.succeed()
-                             : ThrowErrorMessage(cx, MSG_NO_NAMED_SETTER, "${name}");
+                      *defined = true;
+                      return opresult.failNoNamedSetter();
                     }
                     """,
-                    presenceChecker=CGProxyNamedPresenceChecker(self.descriptor, foundVar="found").define(),
-                    name=self.descriptor.name)
+                    presenceChecker=CGProxyNamedPresenceChecker(self.descriptor, foundVar="found").define())
             set += ("return mozilla::dom::DOMProxyHandler::defineProperty(%s);\n" %
                     ", ".join(a.name for a in self.args))
         return set
 
 
 class CGDOMJSProxyHandler_delete(ClassMethod):
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'cx'),
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -29,18 +29,16 @@ MSG_DEF(MSG_METHOD_THIS_UNWRAPPING_DENIE
 MSG_DEF(MSG_GETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE, 2, JSEXN_TYPEERR, "'{0}' getter called on an object that does not implement interface {1}.")
 MSG_DEF(MSG_GETTER_THIS_UNWRAPPING_DENIED, 1, JSEXN_TYPEERR, "Permission to call '{0}' getter denied.")
 MSG_DEF(MSG_SETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE, 2, JSEXN_TYPEERR, "'{0}' setter called on an object that does not implement interface {1}.")
 MSG_DEF(MSG_SETTER_THIS_UNWRAPPING_DENIED, 1, JSEXN_TYPEERR, "Permission to call '{0}' setter denied.")
 MSG_DEF(MSG_THIS_DOES_NOT_IMPLEMENT_INTERFACE, 1, JSEXN_TYPEERR, "\"this\" object does not implement interface {0}.")
 MSG_DEF(MSG_NOT_IN_UNION, 2, JSEXN_TYPEERR, "{0} could not be converted to any of: {1}.")
 MSG_DEF(MSG_ILLEGAL_CONSTRUCTOR, 0, JSEXN_TYPEERR, "Illegal constructor.")
 MSG_DEF(MSG_CONSTRUCTOR_WITHOUT_NEW, 1, JSEXN_TYPEERR, "Constructor {0} requires 'new'")
-MSG_DEF(MSG_NO_INDEXED_SETTER, 1, JSEXN_TYPEERR, "{0} doesn't have an indexed property setter.")
-MSG_DEF(MSG_NO_NAMED_SETTER, 1, JSEXN_TYPEERR, "{0} doesn't have a named property setter.")
 MSG_DEF(MSG_ENFORCE_RANGE_NON_FINITE, 1, JSEXN_TYPEERR, "Non-finite value is out of range for {0}.")
 MSG_DEF(MSG_ENFORCE_RANGE_OUT_OF_RANGE, 1, JSEXN_TYPEERR, "Value is out of range for {0}.")
 MSG_DEF(MSG_NOT_SEQUENCE, 1, JSEXN_TYPEERR, "{0} can't be converted to a sequence.")
 MSG_DEF(MSG_NOT_DICTIONARY, 1, JSEXN_TYPEERR, "{0} can't be converted to a dictionary.")
 MSG_DEF(MSG_OVERLOAD_RESOLUTION_FAILED, 3, JSEXN_TYPEERR, "Argument {0} is not valid for any of the {1}-argument overloads of {2}.")
 MSG_DEF(MSG_GLOBAL_NOT_NATIVE, 0, JSEXN_TYPEERR, "Global is not a native object.")
 MSG_DEF(MSG_ENCODING_NOT_SUPPORTED, 1, JSEXN_RANGEERR, "The given encoding '{0}' is not supported.")
 MSG_DEF(MSG_DOM_ENCODING_NOT_UTF, 0, JSEXN_RANGEERR, "The encoding must be utf-8, utf-16, or utf-16be.")
--- a/dom/encoding/moz.build
+++ b/dom/encoding/moz.build
@@ -20,17 +20,17 @@ UNIFIED_SOURCES += [
 
 FAIL_ON_WARNINGS = True
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/intl/locale',
 ]
 
-props2arrays = TOPSRCDIR + '/intl/locale/props2arrays.py'
+props2arrays = '/intl/locale/props2arrays.py'
 prefixes = (
     'domainsfallbacks',
     'encodingsgroups',
     'labelsencodings',
     'localesfallbacks',
     'nonparticipatingdomains',
 )
 
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -4501,45 +4501,48 @@ HTMLMediaElement::SetMediaKeys(mozilla::
   }
 
   nsCOMPtr<nsIGlobalObject> global =
     do_QueryInterface(OwnerDoc()->GetInnerWindow());
   if (!global) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
-  nsRefPtr<Promise> promise = Promise::Create(global, aRv);
+  nsRefPtr<DetailedPromise> promise = DetailedPromise::Create(global, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
   if (mMediaKeys == aMediaKeys) {
     promise->MaybeResolve(JS::UndefinedHandleValue);
     return promise.forget();
   }
   if (aMediaKeys && aMediaKeys->IsBoundToMediaElement()) {
-    promise->MaybeReject(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR,
+                         NS_LITERAL_CSTRING("MediaKeys object is already bound to another HTMLMediaElement"));
     return promise.forget();
   }
   if (mMediaKeys) {
     // Existing MediaKeys object. Shut it down.
     mMediaKeys->Shutdown();
     mMediaKeys = nullptr;
   }
   if (mDecoder &&
       !mMediaSource &&
       Preferences::GetBool("media.eme.mse-only", true)) {
     ShutdownDecoder();
-    promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+                         NS_LITERAL_CSTRING("EME not supported on non-MSE streams"));
     return promise.forget();
   }
 
   mMediaKeys = aMediaKeys;
   if (mMediaKeys) {
     if (NS_FAILED(mMediaKeys->Bind(this))) {
-      promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+      promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                           NS_LITERAL_CSTRING("Failed to bind MediaKeys object to HTMLMediaElement"));
       mMediaKeys = nullptr;
       return promise.forget();
     }
     if (mDecoder) {
       mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
     }
   }
   promise->MaybeResolve(JS::UndefinedHandleValue);
--- a/dom/media/eme/CDMCallbackProxy.cpp
+++ b/dom/media/eme/CDMCallbackProxy.cpp
@@ -101,29 +101,29 @@ class RejectPromiseTask : public nsRunna
 public:
   RejectPromiseTask(CDMProxy* aProxy,
                     uint32_t aPromiseId,
                     nsresult aException,
                     const nsCString& aMessage)
     : mProxy(aProxy)
     , mPid(aPromiseId)
     , mException(aException)
-    , mMsg(NS_ConvertUTF8toUTF16(aMessage))
+    , mMsg(aMessage)
   {
   }
 
   NS_IMETHOD Run() {
     mProxy->OnRejectPromise(mPid, mException, mMsg);
     return NS_OK;
   }
 
   nsRefPtr<CDMProxy> mProxy;
   dom::PromiseId mPid;
   nsresult mException;
-  nsString mMsg;
+  nsCString mMsg;
 };
 
 
 void
 CDMCallbackProxy::RejectPromise(uint32_t aPromiseId,
                                 nsresult aException,
                                 const nsCString& aMessage)
 {
--- a/dom/media/eme/CDMProxy.cpp
+++ b/dom/media/eme/CDMProxy.cpp
@@ -51,22 +51,24 @@ CDMProxy::Init(PromiseId aPromiseId,
           NS_ConvertUTF16toUTF8(aTopLevelOrigin).get(),
           (aInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"));
 
   nsCString pluginVersion;
   if (!mGMPThread) {
     nsCOMPtr<mozIGeckoMediaPluginService> mps =
       do_GetService("@mozilla.org/gecko-media-plugin-service;1");
     if (!mps) {
-      RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+      RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                    NS_LITERAL_CSTRING("Couldn't get MediaPluginService in CDMProxy::Init"));
       return;
     }
     mps->GetThread(getter_AddRefs(mGMPThread));
     if (!mGMPThread) {
-      RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+      RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                    NS_LITERAL_CSTRING("Couldn't get GMP thread CDMProxy::Init"));
       return;
     }
   }
 
   nsAutoPtr<InitData> data(new InitData());
   data->mPromiseId = aPromiseId;
   data->mOrigin = aOrigin;
   data->mTopLevelOrigin = aTopLevelOrigin;
@@ -85,21 +87,27 @@ CDMProxy::IsOnGMPThread()
   return NS_GetCurrentThread() == mGMPThread;
 }
 #endif
 
 void
 CDMProxy::gmp_InitDone(GMPDecryptorProxy* aCDM, nsAutoPtr<InitData>&& aData)
 {
   EME_LOG("CDMProxy::gmp_InitDone");
-  if (!aCDM || mShutdownCalled) {
+  if (mShutdownCalled) {
     if (aCDM) {
       aCDM->Close();
     }
-    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("CDMProxy was shut down before init could complete"));
+    return;
+  }
+  if (!aCDM) {
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("GetGMPDecryptor failed to return a CDM"));
     return;
   }
 
   mCDM = aCDM;
   mCallback = new CDMCallbackProxy(this);
   mCDM->Init(mCallback);
   nsCOMPtr<nsIRunnable> task(
     NS_NewRunnableMethodWithArg<uint32_t>(this,
@@ -151,85 +159,91 @@ private:
 void
 CDMProxy::gmp_Init(nsAutoPtr<InitData>&& aData)
 {
   MOZ_ASSERT(IsOnGMPThread());
 
   nsCOMPtr<mozIGeckoMediaPluginService> mps =
     do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   if (!mps) {
-    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Couldn't get MediaPluginService in CDMProxy::gmp_Init"));
     return;
   }
 
   // Make a copy before we transfer ownership of aData to the
   // gmp_InitGetGMPDecryptorCallback.
   InitData data(*aData);
   UniquePtr<GetNodeIdCallback> callback(
     new gmp_InitGetGMPDecryptorCallback(this, Move(aData)));
   nsresult rv = mps->GetNodeId(data.mOrigin,
                                data.mTopLevelOrigin,
                                data.mInPrivateBrowsing,
                                Move(callback));
   if (NS_FAILED(rv)) {
-    RejectPromise(data.mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(data.mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Call to GetNodeId() failed early"));
   }
 }
 
 void
 CDMProxy::gmp_InitGetGMPDecryptor(nsresult aResult,
                                   const nsACString& aNodeId,
                                   nsAutoPtr<InitData>&& aData)
 {
   uint32_t promiseID = aData->mPromiseId;
   if (NS_FAILED(aResult)) {
-    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("GetNodeId() called back, but with a failure result"));
     return;
   }
 
   mNodeId = aNodeId;
   MOZ_ASSERT(!GetNodeId().IsEmpty());
 
   nsCOMPtr<mozIGeckoMediaPluginService> mps =
     do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   if (!mps) {
-    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Couldn't get MediaPluginService in CDMProxy::gmp_InitGetGMPDecryptor"));
     return;
   }
 
   EME_LOG("CDMProxy::gmp_Init (%s, %s) %s NodeId=%s",
           NS_ConvertUTF16toUTF8(aData->mOrigin).get(),
           NS_ConvertUTF16toUTF8(aData->mTopLevelOrigin).get(),
           (aData->mInPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"),
           GetNodeId().get());
 
   nsTArray<nsCString> tags;
   tags.AppendElement(NS_ConvertUTF16toUTF8(mKeySystem));
 
   UniquePtr<GetGMPDecryptorCallback> callback(new gmp_InitDoneCallback(this,
                                                                        Move(aData)));
   nsresult rv = mps->GetGMPDecryptor(&tags, GetNodeId(), Move(callback));
   if (NS_FAILED(rv)) {
-    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Call to GetGMPDecryptor() failed early"));
   }
 }
 
 void
 CDMProxy::OnCDMCreated(uint32_t aPromiseId)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
   }
   MOZ_ASSERT(!GetNodeId().IsEmpty());
   if (mCDM) {
     mKeys->OnCDMCreated(aPromiseId, GetNodeId(), mCDM->GetPluginId());
   } else {
     // No CDM? Just reject the promise.
-    mKeys->RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    mKeys->RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                         NS_LITERAL_CSTRING("Null CDM in OnCDMCreated()"));
   }
 }
 
 void
 CDMProxy::CreateSession(uint32_t aCreateSessionToken,
                         dom::SessionType aSessionType,
                         PromiseId aPromiseId,
                         const nsAString& aInitDataType,
@@ -259,17 +273,18 @@ ToGMPSessionType(dom::SessionType aSessi
   };
 };
 
 void
 CDMProxy::gmp_CreateSession(nsAutoPtr<CreateSessionData> aData)
 {
   MOZ_ASSERT(IsOnGMPThread());
   if (!mCDM) {
-    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Null CDM in gmp_CreateSession"));
     return;
   }
   mCDM->CreateSession(aData->mCreateSessionToken,
                       aData->mPromiseId,
                       aData->mInitDataType,
                       aData->mInitData,
                       ToGMPSessionType(aData->mSessionType));
 }
@@ -290,17 +305,18 @@ CDMProxy::LoadSession(PromiseId aPromise
 }
 
 void
 CDMProxy::gmp_LoadSession(nsAutoPtr<SessionOpData> aData)
 {
   MOZ_ASSERT(IsOnGMPThread());
 
   if (!mCDM) {
-    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Null CDM in gmp_LoadSession"));
     return;
   }
   mCDM->LoadSession(aData->mPromiseId, aData->mSessionId);
 }
 
 void
 CDMProxy::SetServerCertificate(PromiseId aPromiseId,
                                nsTArray<uint8_t>& aCert)
@@ -316,17 +332,18 @@ CDMProxy::SetServerCertificate(PromiseId
   mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
 void
 CDMProxy::gmp_SetServerCertificate(nsAutoPtr<SetServerCertificateData> aData)
 {
   MOZ_ASSERT(IsOnGMPThread());
   if (!mCDM) {
-    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Null CDM in gmp_SetServerCertificate"));
     return;
   }
   mCDM->SetServerCertificate(aData->mPromiseId, aData->mCert);
 }
 
 void
 CDMProxy::UpdateSession(const nsAString& aSessionId,
                         PromiseId aPromiseId,
@@ -345,17 +362,18 @@ CDMProxy::UpdateSession(const nsAString&
   mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
 void
 CDMProxy::gmp_UpdateSession(nsAutoPtr<UpdateSessionData> aData)
 {
   MOZ_ASSERT(IsOnGMPThread());
   if (!mCDM) {
-    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Null CDM in gmp_UpdateSession"));
     return;
   }
   mCDM->UpdateSession(aData->mPromiseId,
                       aData->mSessionId,
                       aData->mResponse);
 }
 
 void
@@ -373,17 +391,18 @@ CDMProxy::CloseSession(const nsAString& 
   mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
 void
 CDMProxy::gmp_CloseSession(nsAutoPtr<SessionOpData> aData)
 {
   MOZ_ASSERT(IsOnGMPThread());
   if (!mCDM) {
-    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Null CDM in gmp_CloseSession"));
     return;
   }
   mCDM->CloseSession(aData->mPromiseId, aData->mSessionId);
 }
 
 void
 CDMProxy::RemoveSession(const nsAString& aSessionId,
                         PromiseId aPromiseId)
@@ -399,17 +418,18 @@ CDMProxy::RemoveSession(const nsAString&
   mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
 }
 
 void
 CDMProxy::gmp_RemoveSession(nsAutoPtr<SessionOpData> aData)
 {
   MOZ_ASSERT(IsOnGMPThread());
   if (!mCDM) {
-    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
+    RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Null CDM in gmp_RemoveSession"));
     return;
   }
   mCDM->RemoveSession(aData->mPromiseId, aData->mSessionId);
 }
 
 void
 CDMProxy::Shutdown()
 {
@@ -438,24 +458,26 @@ CDMProxy::gmp_Shutdown()
 
   if (mCDM) {
     mCDM->Close();
     mCDM = nullptr;
   }
 }
 
 void
-CDMProxy::RejectPromise(PromiseId aId, nsresult aCode)
+CDMProxy::RejectPromise(PromiseId aId, nsresult aCode,
+                        const nsCString& aReason)
 {
   if (NS_IsMainThread()) {
     if (!mKeys.IsNull()) {
-      mKeys->RejectPromise(aId, aCode);
+      mKeys->RejectPromise(aId, aCode, aReason);
     }
   } else {
-    nsCOMPtr<nsIRunnable> task(new RejectPromiseTask(this, aId, aCode));
+    nsCOMPtr<nsIRunnable> task(new RejectPromiseTask(this, aId, aCode,
+                                                     aReason));
     NS_DispatchToMainThread(task);
   }
 }
 
 void
 CDMProxy::ResolvePromise(PromiseId aId)
 {
   if (NS_IsMainThread()) {
@@ -592,21 +614,20 @@ CDMProxy::OnSessionError(const nsAString
     session->DispatchKeyError(aSystemCode);
   }
   LogToConsole(aMsg);
 }
 
 void
 CDMProxy::OnRejectPromise(uint32_t aPromiseId,
                           nsresult aDOMException,
-                          const nsAString& aMsg)
+                          const nsCString& aMsg)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  RejectPromise(aPromiseId, aDOMException);
-  LogToConsole(aMsg);
+  RejectPromise(aPromiseId, aDOMException, aMsg);
 }
 
 const nsString&
 CDMProxy::KeySystem() const
 {
   return mKeySystem;
 }
 
--- a/dom/media/eme/CDMProxy.h
+++ b/dom/media/eme/CDMProxy.h
@@ -133,26 +133,27 @@ public:
   void OnSessionError(const nsAString& aSessionId,
                       nsresult aException,
                       uint32_t aSystemCode,
                       const nsAString& aMsg);
 
   // Main thread only.
   void OnRejectPromise(uint32_t aPromiseId,
                        nsresult aDOMException,
-                       const nsAString& aMsg);
+                       const nsCString& aMsg);
 
   // Threadsafe.
   void Decrypt(MediaRawData* aSample,
                DecryptionClient* aSink,
                MediaTaskQueue* aTaskQueue);
 
   // Reject promise with DOMException corresponding to aExceptionCode.
   // Can be called from any thread.
-  void RejectPromise(PromiseId aId, nsresult aExceptionCode);
+  void RejectPromise(PromiseId aId, nsresult aExceptionCode,
+                     const nsCString& aReason);
 
   // Resolves promise with "undefined".
   // Can be called from any thread.
   void ResolvePromise(PromiseId aId);
 
   // Threadsafe.
   const nsString& KeySystem() const;
 
@@ -264,30 +265,33 @@ private:
   };
   // GMP thread only.
   void gmp_Decrypt(nsRefPtr<DecryptJob> aJob);
 
   class RejectPromiseTask : public nsRunnable {
   public:
     RejectPromiseTask(CDMProxy* aProxy,
                       PromiseId aId,
-                      nsresult aCode)
+                      nsresult aCode,
+                      const nsCString& aReason)
       : mProxy(aProxy)
       , mId(aId)
       , mCode(aCode)
+      , mReason(aReason)
     {
     }
     NS_METHOD Run() {
-      mProxy->RejectPromise(mId, mCode);
+      mProxy->RejectPromise(mId, mCode, mReason);
       return NS_OK;
     }
   private:
     nsRefPtr<CDMProxy> mProxy;
     PromiseId mId;
     nsresult mCode;
+    nsCString mReason;
   };
 
   ~CDMProxy();
 
   // Helper to enforce that a raw pointer is only accessed on the main thread.
   template<class Type>
   class MainThreadOnlyRawPtr {
   public:
new file mode 100644
--- /dev/null
+++ b/dom/media/eme/DetailedPromise.cpp
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DetailedPromise.h"
+#include "mozilla/dom/DOMException.h"
+
+namespace mozilla {
+namespace dom {
+
+DetailedPromise::DetailedPromise(nsIGlobalObject* aGlobal)
+  : Promise(aGlobal)
+  , mResponded(false)
+{
+}
+
+DetailedPromise::~DetailedPromise()
+{
+  MOZ_ASSERT(mResponded == IsPending());
+}
+
+static void
+LogToConsole(const nsAString& aMsg)
+{
+  nsCOMPtr<nsIConsoleService> console(
+    do_GetService("@mozilla.org/consoleservice;1"));
+  if (!console) {
+    NS_WARNING("Failed to log message to console.");
+    return;
+  }
+  nsAutoString msg(aMsg);
+  console->LogStringMessage(msg.get());
+}
+
+void
+DetailedPromise::MaybeReject(nsresult aArg, const nsCString& aReason)
+{
+  mResponded = true;
+
+  LogToConsole(NS_ConvertUTF8toUTF16(aReason));
+
+  nsRefPtr<DOMException> exception =
+    DOMException::Create(aArg, aReason);
+  Promise::MaybeRejectBrokenly(exception);
+}
+
+void
+DetailedPromise::MaybeReject(ErrorResult&, const nsCString& aReason)
+{
+  NS_NOTREACHED("nsresult expected in MaybeReject()");
+}
+
+/* static */ already_AddRefed<DetailedPromise>
+DetailedPromise::Create(nsIGlobalObject* aGlobal, ErrorResult& aRv)
+{
+  nsRefPtr<DetailedPromise> promise = new DetailedPromise(aGlobal);
+  promise->CreateWrapper(aRv);
+  return aRv.Failed() ? nullptr : promise.forget();
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/dom/media/eme/DetailedPromise.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __DetailedPromise_h__
+#define __DetailedPromise_h__
+
+#include "mozilla/dom/Promise.h"
+
+namespace mozilla {
+namespace dom {
+
+/*
+ * This is pretty horrible; bug 1160445.
+ * Extend Promise to add custom DOMException messages on rejection.
+ * Get rid of this once we've ironed out EME errors in the wild.
+ */
+class DetailedPromise : public Promise
+{
+public:
+  static already_AddRefed<DetailedPromise>
+  Create(nsIGlobalObject* aGlobal, ErrorResult& aRv);
+
+  template <typename T>
+  void MaybeResolve(const T& aArg)
+  {
+    mResponded = true;
+    Promise::MaybeResolve<T>(aArg);
+  }
+
+  void MaybeReject(nsresult aArg) = delete;
+  void MaybeReject(nsresult aArg, const nsCString& aReason);
+
+  void MaybeReject(ErrorResult& aArg) = delete;
+  void MaybeReject(ErrorResult&, const nsCString& aReason);
+
+private:
+  explicit DetailedPromise(nsIGlobalObject* aGlobal);
+  virtual ~DetailedPromise();
+
+  bool mResponded;
+};
+
+}
+}
+
+#endif // __DetailedPromise_h__
--- a/dom/media/eme/MediaKeySession.cpp
+++ b/dom/media/eme/MediaKeySession.cpp
@@ -164,34 +164,36 @@ MediaKeySession::KeyStatuses() const
   return mKeyStatusMap;
 }
 
 already_AddRefed<Promise>
 MediaKeySession::GenerateRequest(const nsAString& aInitDataType,
                                  const ArrayBufferViewOrArrayBuffer& aInitData,
                                  ErrorResult& aRv)
 {
-  nsRefPtr<Promise> promise(MakePromise(aRv));
+  nsRefPtr<DetailedPromise> promise(MakePromise(aRv));
   if (aRv.Failed()) {
     return nullptr;
   }
 
   if (!mUninitialized) {
     EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, uninitialized",
             this, NS_ConvertUTF16toUTF8(mSessionId).get());
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
+                         NS_LITERAL_CSTRING("Session is already initialized in MediaKeySession.generateRequest()"));
     return promise.forget();
   }
 
   mUninitialized = false;
 
   nsTArray<uint8_t> data;
   if (aInitDataType.IsEmpty() ||
       !CopyArrayBufferViewOrArrayBufferData(aInitData, data)) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
+                         NS_LITERAL_CSTRING("Bad arguments to MediaKeySession.generateRequest()"));
     EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, "
             "invalid initData or initDataType",
       this, NS_ConvertUTF16toUTF8(mSessionId).get());
     return promise.forget();
   }
 
   // Convert initData to base64 for easier logging.
   // Note: UpdateSession() Move()s the data out of the array, so we have
@@ -219,30 +221,32 @@ MediaKeySession::GenerateRequest(const n
           base64InitData.get());
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 MediaKeySession::Load(const nsAString& aSessionId, ErrorResult& aRv)
 {
-  nsRefPtr<Promise> promise(MakePromise(aRv));
+  nsRefPtr<DetailedPromise> promise(MakePromise(aRv));
   if (aRv.Failed()) {
     return nullptr;
   }
 
   if (aSessionId.IsEmpty()) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
+                         NS_LITERAL_CSTRING("Trying to load a session with empty session ID"));
     // "The sessionId parameter is empty."
     EME_LOG("MediaKeySession[%p,''] Load() failed, no sessionId", this);
     return promise.forget();
   }
 
   if (!mUninitialized) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
+                         NS_LITERAL_CSTRING("Session is already initialized in MediaKeySession.load()"));
     EME_LOG("MediaKeySession[%p,'%s'] Load() failed, uninitialized",
       this, NS_ConvertUTF16toUTF8(aSessionId).get());
     return promise.forget();
   }
 
   mUninitialized = false;
 
   // We now know the sessionId being loaded into this session. Remove the
@@ -260,25 +264,31 @@ MediaKeySession::Load(const nsAString& a
     this, NS_ConvertUTF16toUTF8(mSessionId).get(), pid);
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 MediaKeySession::Update(const ArrayBufferViewOrArrayBuffer& aResponse, ErrorResult& aRv)
 {
-  nsRefPtr<Promise> promise(MakePromise(aRv));
+  nsRefPtr<DetailedPromise> promise(MakePromise(aRv));
   if (aRv.Failed()) {
     return nullptr;
   }
   nsTArray<uint8_t> data;
-  if (IsClosed() ||
-      !mKeys->GetCDMProxy() ||
-      !CopyArrayBufferViewOrArrayBufferData(aResponse, data)) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+  if (IsClosed() || !mKeys->GetCDMProxy()) {
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                         NS_LITERAL_CSTRING("Session is closed or was not properly initialized"));
+    EME_LOG("MediaKeySession[%p,'%s'] Update() failed, session is closed or was not properly initialised.",
+            this, NS_ConvertUTF16toUTF8(mSessionId).get());
+    return promise.forget();
+  }
+  if (!CopyArrayBufferViewOrArrayBufferData(aResponse, data)) {
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
+                         NS_LITERAL_CSTRING("Invalid response buffer"));
     EME_LOG("MediaKeySession[%p,'%s'] Update() failed, invalid response buffer",
             this, NS_ConvertUTF16toUTF8(mSessionId).get());
     return promise.forget();
   }
 
 
   // Convert response to base64 for easier logging.
   // Note: UpdateSession() Move()s the data out of the array, so we have
@@ -305,17 +315,17 @@ MediaKeySession::Update(const ArrayBuffe
            base64Response.get());
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 MediaKeySession::Close(ErrorResult& aRv)
 {
-  nsRefPtr<Promise> promise(MakePromise(aRv));
+  nsRefPtr<DetailedPromise> promise(MakePromise(aRv));
   if (aRv.Failed()) {
     return nullptr;
   }
   if (IsClosed() || !mKeys->GetCDMProxy()) {
     EME_LOG("MediaKeySession[%p,'%s'] Close() already closed",
             this, NS_ConvertUTF16toUTF8(mSessionId).get());
     promise->MaybeResolve(JS::UndefinedHandleValue);
     return promise.forget();
@@ -347,29 +357,31 @@ bool
 MediaKeySession::IsClosed() const
 {
   return mIsClosed;
 }
 
 already_AddRefed<Promise>
 MediaKeySession::Remove(ErrorResult& aRv)
 {
-  nsRefPtr<Promise> promise(MakePromise(aRv));
+  nsRefPtr<DetailedPromise> promise(MakePromise(aRv));
   if (aRv.Failed()) {
     return nullptr;
   }
   if (mSessionType != SessionType::Persistent) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
+                         NS_LITERAL_CSTRING("Calling MediaKeySession.remove() on non-persistent session"));
     // "The operation is not supported on session type sessions."
     EME_LOG("MediaKeySession[%p,'%s'] Remove() failed, sesion not persisrtent.",
             this, NS_ConvertUTF16toUTF8(mSessionId).get());
     return promise.forget();
   }
   if (IsClosed() || !mKeys->GetCDMProxy()) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                         NS_LITERAL_CSTRING("MediaKeySesison.remove() called but session is not active"));
     // "The session is closed."
     EME_LOG("MediaKeySession[%p,'%s'] Remove() failed, already session closed.",
             this, NS_ConvertUTF16toUTF8(mSessionId).get());
     return promise.forget();
   }
   PromiseId pid = mKeys->StorePromise(promise);
   mKeys->GetCDMProxy()->RemoveSession(mSessionId, pid);
   EME_LOG("MediaKeySession[%p,'%s'] Remove() sent to CDM, promiseId=%d.",
@@ -429,22 +441,22 @@ MediaKeySession::DispatchKeyStatusesChan
 }
 
 uint32_t
 MediaKeySession::Token() const
 {
   return mToken;
 }
 
-already_AddRefed<Promise>
+already_AddRefed<DetailedPromise>
 MediaKeySession::MakePromise(ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
   if (!global) {
     NS_WARNING("Passed non-global to MediaKeys ctor!");
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
-  return Promise::Create(global, aRv);
+  return DetailedPromise::Create(global, aRv);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/eme/MediaKeySession.h
+++ b/dom/media/eme/MediaKeySession.h
@@ -92,19 +92,19 @@ public:
 
   // Process-unique identifier.
   uint32_t Token() const;
 
 private:
   ~MediaKeySession();
 
   void UpdateKeyStatusMap();
-  already_AddRefed<Promise> MakePromise(ErrorResult& aRv);
+  already_AddRefed<DetailedPromise> MakePromise(ErrorResult& aRv);
 
-  nsRefPtr<Promise> mClosed;
+  nsRefPtr<DetailedPromise> mClosed;
 
   nsRefPtr<MediaKeyError> mMediaKeyError;
   nsRefPtr<MediaKeys> mKeys;
   const nsString mKeySystem;
   nsString mSessionId;
   const SessionType mSessionType;
   const uint32_t mToken;
   bool mIsClosed;
--- a/dom/media/eme/MediaKeySystemAccessManager.cpp
+++ b/dom/media/eme/MediaKeySystemAccessManager.cpp
@@ -4,16 +4,17 @@
 
 #include "MediaKeySystemAccessManager.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/EMEUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
+#include "mozilla/DetailedPromise.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaKeySystemAccessManager)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
 NS_INTERFACE_MAP_END
@@ -21,17 +22,17 @@ NS_INTERFACE_MAP_END
 NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaKeySystemAccessManager)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaKeySystemAccessManager)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(MediaKeySystemAccessManager)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaKeySystemAccessManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
   for (size_t i = 0; i < tmp->mRequests.Length(); i++) {
-    tmp->mRequests[i].RejectPromise();
+    tmp->mRequests[i].RejectPromise(NS_LITERAL_CSTRING("Promise still outstanding at MediaKeySystemAccessManager GC"));
     tmp->mRequests[i].CancelTimer();
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequests[i].mPromise)
   }
   tmp->mRequests.Clear();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MediaKeySystemAccessManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
@@ -47,56 +48,59 @@ MediaKeySystemAccessManager::MediaKeySys
 }
 
 MediaKeySystemAccessManager::~MediaKeySystemAccessManager()
 {
   Shutdown();
 }
 
 void
-MediaKeySystemAccessManager::Request(Promise* aPromise,
+MediaKeySystemAccessManager::Request(DetailedPromise* aPromise,
                                      const nsAString& aKeySystem,
                                      const Optional<Sequence<MediaKeySystemOptions>>& aOptions)
 {
   if (aKeySystem.IsEmpty() || (aOptions.WasPassed() && aOptions.Value().IsEmpty())) {
-    aPromise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    aPromise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
+                          NS_LITERAL_CSTRING("Invalid keysystem type or invalid options sequence"));
     return;
   }
   Sequence<MediaKeySystemOptions> optionsNotPassed;
   const auto& options = aOptions.WasPassed() ? aOptions.Value() : optionsNotPassed;
   Request(aPromise, aKeySystem, options, RequestType::Initial);
 }
 
 void
-MediaKeySystemAccessManager::Request(Promise* aPromise,
+MediaKeySystemAccessManager::Request(DetailedPromise* aPromise,
                                      const nsAString& aKeySystem,
                                      const Sequence<MediaKeySystemOptions>& aOptions,
                                      RequestType aType)
 {
   EME_LOG("MediaKeySystemAccessManager::Request %s", NS_ConvertUTF16toUTF8(aKeySystem).get());
   if (!Preferences::GetBool("media.eme.enabled", false)) {
     // EME disabled by user, send notification to chrome so UI can
     // inform user.
     MediaKeySystemAccess::NotifyObservers(mWindow,
                                           aKeySystem,
                                           MediaKeySystemStatus::Api_disabled);
-    aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+                          NS_LITERAL_CSTRING("EME has been preffed off"));
     return;
   }
 
   // Parse keysystem, split it out into keySystem prefix, and version suffix.
   nsAutoString keySystem;
   int32_t minCdmVersion = NO_CDM_VERSION;
   if (!ParseKeySystem(aKeySystem,
                       keySystem,
                       minCdmVersion)) {
     // Invalid keySystem string, or unsupported keySystem. Send notification
     // to chrome to show a failure notice.
     MediaKeySystemAccess::NotifyObservers(mWindow, aKeySystem, MediaKeySystemStatus::Cdm_not_supported);
-    aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+                          NS_LITERAL_CSTRING("Key system string is invalid, or key system is unsupported"));
     return;
   }
 
   MediaKeySystemStatus status = MediaKeySystemAccess::GetKeySystemStatus(keySystem, minCdmVersion);
   if ((status == MediaKeySystemStatus::Cdm_not_installed ||
        status == MediaKeySystemStatus::Cdm_insufficient_version) &&
       keySystem.EqualsLiteral("com.adobe.primetime")) {
     // These are cases which could be resolved by downloading a new(er) CDM.
@@ -112,42 +116,48 @@ MediaKeySystemAccessManager::Request(Pro
       // Note: If we're re-trying, we don't re-send the notificaiton,
       // as chrome is already displaying the "we can't play, updating"
       // notification.
       MediaKeySystemAccess::NotifyObservers(mWindow, keySystem, status);
     } else {
       // We waited or can't wait for an update and we still can't service
       // the request. Give up. Chrome will still be showing a "I can't play,
       // updating" notification.
-      aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+      aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+                            NS_LITERAL_CSTRING("Gave up while waiting for a CDM update"));
     }
     return;
   }
   if (status != MediaKeySystemStatus::Available) {
     if (status != MediaKeySystemStatus::Error) {
       // Failed due to user disabling something, send a notification to
       // chrome, so we can show some UI to explain how the user can rectify
       // the situation.
       MediaKeySystemAccess::NotifyObservers(mWindow, keySystem, status);
+      aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+                            NS_LITERAL_CSTRING("The key system has been disabled by the user"));
+      return;
     }
-    aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    aPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                          NS_LITERAL_CSTRING("GetKeySystemAccess failed"));
     return;
   }
 
   if (aOptions.IsEmpty() ||
       MediaKeySystemAccess::IsSupported(keySystem, aOptions)) {
     nsRefPtr<MediaKeySystemAccess> access(new MediaKeySystemAccess(mWindow, keySystem));
     aPromise->MaybeResolve(access);
     return;
   }
 
-  aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+  aPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR,
+                        NS_LITERAL_CSTRING("Key system is not supported"));
 }
 
-MediaKeySystemAccessManager::PendingRequest::PendingRequest(Promise* aPromise,
+MediaKeySystemAccessManager::PendingRequest::PendingRequest(DetailedPromise* aPromise,
                                                             const nsAString& aKeySystem,
                                                             const Sequence<MediaKeySystemOptions>& aOptions,
                                                             nsITimer* aTimer)
   : mPromise(aPromise)
   , mKeySystem(aKeySystem)
   , mOptions(aOptions)
   , mTimer(aTimer)
 {
@@ -172,25 +182,25 @@ void
 MediaKeySystemAccessManager::PendingRequest::CancelTimer()
 {
   if (mTimer) {
     mTimer->Cancel();
   }
 }
 
 void
-MediaKeySystemAccessManager::PendingRequest::RejectPromise()
+MediaKeySystemAccessManager::PendingRequest::RejectPromise(const nsCString& aReason)
 {
   if (mPromise) {
-    mPromise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    mPromise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR, aReason);
   }
 }
 
 bool
-MediaKeySystemAccessManager::AwaitInstall(Promise* aPromise,
+MediaKeySystemAccessManager::AwaitInstall(DetailedPromise* aPromise,
                                           const nsAString& aKeySystem,
                                           const Sequence<MediaKeySystemOptions>& aOptions)
 {
   EME_LOG("MediaKeySystemAccessManager::AwaitInstall %s", NS_ConvertUTF16toUTF8(aKeySystem).get());
 
   if (!EnsureObserversAdded()) {
     NS_WARNING("Failed to add pref observer");
     return false;
@@ -260,17 +270,17 @@ MediaKeySystemAccessManager::EnsureObser
 void
 MediaKeySystemAccessManager::Shutdown()
 {
   EME_LOG("MediaKeySystemAccessManager::Shutdown");
   nsTArray<PendingRequest> requests(Move(mRequests));
   for (PendingRequest& request : requests) {
     // Cancel all requests; we're shutting down.
     request.CancelTimer();
-    request.RejectPromise();
+    request.RejectPromise(NS_LITERAL_CSTRING("Promise still outstanding at MediaKeySystemAccessManager shutdown"));
   }
   if (mAddedObservers) {
     nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
     if (obsService) {
       obsService->RemoveObserver(this, "gmp-path-added");
       mAddedObservers = false;
     }
   }
--- a/dom/media/eme/MediaKeySystemAccessManager.h
+++ b/dom/media/eme/MediaKeySystemAccessManager.h
@@ -9,65 +9,67 @@
 #include "nsIObserver.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsISupportsImpl.h"
 #include "nsITimer.h"
 
 namespace mozilla {
 namespace dom {
 
+class DetailedPromise;
+
 class MediaKeySystemAccessManager final : public nsIObserver
 {
 public:
 
   explicit MediaKeySystemAccessManager(nsPIDOMWindow* aWindow);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(MediaKeySystemAccessManager, nsIObserver)
   NS_DECL_NSIOBSERVER
 
-  void Request(Promise* aPromise,
+  void Request(DetailedPromise* aPromise,
                const nsAString& aKeySystem,
                const Optional<Sequence<MediaKeySystemOptions>>& aOptions);
 
   void Shutdown();
 
   struct PendingRequest {
-    PendingRequest(Promise* aPromise,
+    PendingRequest(DetailedPromise* aPromise,
       const nsAString& aKeySystem,
       const Sequence<MediaKeySystemOptions>& aOptions,
       nsITimer* aTimer);
     PendingRequest(const PendingRequest& aOther);
     ~PendingRequest();
     void CancelTimer();
-    void RejectPromise();
+    void RejectPromise(const nsCString& aReason);
 
-    nsRefPtr<Promise> mPromise;
+    nsRefPtr<DetailedPromise> mPromise;
     const nsString mKeySystem;
     const Sequence<MediaKeySystemOptions> mOptions;
     nsCOMPtr<nsITimer> mTimer;
   };
 
 private:
 
   enum RequestType {
     Initial,
     Subsequent
   };
 
-  void Request(Promise* aPromise,
+  void Request(DetailedPromise* aPromise,
                const nsAString& aKeySystem,
                const Sequence<MediaKeySystemOptions>& aOptions,
                RequestType aType);
 
   ~MediaKeySystemAccessManager();
 
   bool EnsureObserversAdded();
 
-  bool AwaitInstall(Promise* aPromise,
+  bool AwaitInstall(DetailedPromise* aPromise,
                     const nsAString& aKeySystem,
                     const Sequence<MediaKeySystemOptions>& aOptions);
 
   void RetryRequest(PendingRequest& aRequest);
 
   nsTArray<PendingRequest> mRequests;
 
   nsCOMPtr<nsPIDOMWindow> mWindow;
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -55,20 +55,21 @@ MediaKeys::MediaKeys(nsPIDOMWindow* aPar
   , mCreatePromiseId(0)
 {
   EME_LOG("MediaKeys[%p] constructed keySystem=%s",
           this, NS_ConvertUTF16toUTF8(mKeySystem).get());
 }
 
 static PLDHashOperator
 RejectPromises(const uint32_t& aKey,
-               nsRefPtr<dom::Promise>& aPromise,
+               nsRefPtr<dom::DetailedPromise>& aPromise,
                void* aClosure)
 {
-  aPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+  aPromise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                        NS_LITERAL_CSTRING("Promise still outstanding at MediaKeys shutdown"));
   ((MediaKeys*)aClosure)->Release();
   return PL_DHASH_NEXT;
 }
 
 MediaKeys::~MediaKeys()
 {
   Shutdown();
   EME_LOG("MediaKeys[%p] destroyed", this);
@@ -140,101 +141,105 @@ MediaKeys::WrapObject(JSContext* aCx, JS
 }
 
 void
 MediaKeys::GetKeySystem(nsString& retval) const
 {
   retval = mKeySystem;
 }
 
-already_AddRefed<Promise>
+already_AddRefed<DetailedPromise>
 MediaKeys::SetServerCertificate(const ArrayBufferViewOrArrayBuffer& aCert, ErrorResult& aRv)
 {
-  nsRefPtr<Promise> promise(MakePromise(aRv));
+  nsRefPtr<DetailedPromise>
+    promise(MakePromise(aRv));
   if (aRv.Failed()) {
     return nullptr;
   }
 
   if (!mProxy) {
     NS_WARNING("Tried to use a MediaKeys without a CDM");
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                         NS_LITERAL_CSTRING("Null CDM in MediaKeys.setServerCertificate()"));
     return promise.forget();
   }
 
   nsTArray<uint8_t> data;
   if (!CopyArrayBufferViewOrArrayBufferData(aCert, data)) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
+                         NS_LITERAL_CSTRING("Invalid argument to MediaKeys.setServerCertificate()"));
     return promise.forget();
   }
 
   mProxy->SetServerCertificate(StorePromise(promise), data);
   return promise.forget();
 }
 
-already_AddRefed<Promise>
+already_AddRefed<DetailedPromise>
 MediaKeys::MakePromise(ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
   if (!global) {
     NS_WARNING("Passed non-global to MediaKeys ctor!");
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
-  return Promise::Create(global, aRv);
+  return DetailedPromise::Create(global, aRv);
 }
 
 PromiseId
-MediaKeys::StorePromise(Promise* aPromise)
+MediaKeys::StorePromise(DetailedPromise* aPromise)
 {
   static uint32_t sEMEPromiseCount = 1;
   MOZ_ASSERT(aPromise);
   uint32_t id = sEMEPromiseCount++;
 
   EME_LOG("MediaKeys[%p]::StorePromise() id=%d", this, id);
 
   // Keep MediaKeys alive for the lifetime of its promises. Any still-pending
   // promises are rejected in Shutdown().
   AddRef();
 
   mPromises.Put(id, aPromise);
   return id;
 }
 
-already_AddRefed<Promise>
+already_AddRefed<DetailedPromise>
 MediaKeys::RetrievePromise(PromiseId aId)
 {
   if (!mPromises.Contains(aId)) {
     NS_WARNING(nsPrintfCString("Tried to retrieve a non-existent promise id=%d", aId).get());
     return nullptr;
   }
-  nsRefPtr<Promise> promise;
+  nsRefPtr<DetailedPromise> promise;
   mPromises.Remove(aId, getter_AddRefs(promise));
   Release();
   return promise.forget();
 }
 
 void
-MediaKeys::RejectPromise(PromiseId aId, nsresult aExceptionCode)
+MediaKeys::RejectPromise(PromiseId aId, nsresult aExceptionCode,
+                         const nsCString& aReason)
 {
   EME_LOG("MediaKeys[%p]::RejectPromise(%d, 0x%x)", this, aId, aExceptionCode);
 
-  nsRefPtr<Promise> promise(RetrievePromise(aId));
+  nsRefPtr<DetailedPromise> promise(RetrievePromise(aId));
   if (!promise) {
     return;
   }
   if (mPendingSessions.Contains(aId)) {
     // This promise could be a createSession or loadSession promise,
     // so we might have a pending session waiting to be resolved into
     // the promise on success. We've been directed to reject to promise,
     // so we can throw away the corresponding session object.
     mPendingSessions.Remove(aId);
   }
 
   MOZ_ASSERT(NS_FAILED(aExceptionCode));
-  promise->MaybeReject(aExceptionCode);
+  promise->MaybeReject(aExceptionCode, aReason);
 
   if (mCreatePromiseId == aId) {
     // Note: This will probably destroy the MediaKeys object!
     Release();
   }
 }
 
 void
@@ -259,98 +264,102 @@ MediaKeys::OnSessionIdReady(MediaKeySess
   mKeySessions.Put(aSession->GetSessionId(), aSession);
 }
 
 void
 MediaKeys::ResolvePromise(PromiseId aId)
 {
   EME_LOG("MediaKeys[%p]::ResolvePromise(%d)", this, aId);
 
-  nsRefPtr<Promise> promise(RetrievePromise(aId));
+  nsRefPtr<DetailedPromise> promise(RetrievePromise(aId));
   if (!promise) {
     return;
   }
   if (mPendingSessions.Contains(aId)) {
     // We should only resolve LoadSession calls via this path,
     // not CreateSession() promises.
     nsRefPtr<MediaKeySession> session;
     if (!mPendingSessions.Get(aId, getter_AddRefs(session)) ||
         !session ||
         session->GetSessionId().IsEmpty()) {
       NS_WARNING("Received activation for non-existent session!");
-      promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+      promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR,
+                           NS_LITERAL_CSTRING("CDM LoadSession() returned a different session ID than requested"));
       mPendingSessions.Remove(aId);
       return;
     }
     mPendingSessions.Remove(aId);
     mKeySessions.Put(session->GetSessionId(), session);
     promise->MaybeResolve(session);
   } else {
     promise->MaybeResolve(JS::UndefinedHandleValue);
   }
 }
 
-already_AddRefed<Promise>
+already_AddRefed<DetailedPromise>
 MediaKeys::Init(ErrorResult& aRv)
 {
-  nsRefPtr<Promise> promise(MakePromise(aRv));
+  nsRefPtr<DetailedPromise>
+    promise(MakePromise(aRv));
   if (aRv.Failed()) {
     return nullptr;
   }
 
   mProxy = new CDMProxy(this, mKeySystem);
 
   // Determine principal (at creation time) of the MediaKeys object.
   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(GetParentObject());
   if (!sop) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                         NS_LITERAL_CSTRING("Couldn't get script principal in MediaKeys::Init"));
     return promise.forget();
   }
   mPrincipal = sop->GetPrincipal();
 
   // Determine principal of the "top-level" window; the principal of the
   // page that will display in the URL bar.
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(GetParentObject());
   if (!window) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                         NS_LITERAL_CSTRING("Couldn't get top-level window in MediaKeys::Init"));
     return promise.forget();
   }
   nsCOMPtr<nsIDOMWindow> topWindow;
   window->GetTop(getter_AddRefs(topWindow));
   nsCOMPtr<nsPIDOMWindow> top = do_QueryInterface(topWindow);
   if (!top || !top->GetExtantDoc()) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                         NS_LITERAL_CSTRING("Couldn't get document in MediaKeys::Init"));
     return promise.forget();
   }
 
   mTopLevelPrincipal = top->GetExtantDoc()->NodePrincipal();
 
   if (!mPrincipal || !mTopLevelPrincipal) {
     NS_WARNING("Failed to get principals when creating MediaKeys");
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                         NS_LITERAL_CSTRING("Couldn't get principal(s) in MediaKeys::Init"));
     return promise.forget();
   }
 
   nsAutoString origin;
   nsresult rv = nsContentUtils::GetUTFOrigin(mPrincipal, origin);
   if (NS_FAILED(rv)) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                         NS_LITERAL_CSTRING("Couldn't get principal origin string in MediaKeys::Init"));
     return promise.forget();
   }
   nsAutoString topLevelOrigin;
   rv = nsContentUtils::GetUTFOrigin(mTopLevelPrincipal, topLevelOrigin);
   if (NS_FAILED(rv)) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR,
+                         NS_LITERAL_CSTRING("Couldn't get top-level principal origin string in MediaKeys::Init"));
     return promise.forget();
   }
 
-  if (!window) {
-    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return promise.forget();
-  }
   nsIDocument* doc = window->GetExtantDoc();
   const bool inPrivateBrowsing = nsContentUtils::IsInPrivateBrowsing(doc);
 
   EME_LOG("MediaKeys[%p]::Create() (%s, %s), %s",
           this,
           NS_ConvertUTF16toUTF8(origin).get(),
           NS_ConvertUTF16toUTF8(topLevelOrigin).get(),
           (inPrivateBrowsing ? "PrivateBrowsing" : "NonPrivateBrowsing"));
@@ -449,17 +458,17 @@ private:
   uint32_t mPluginId;
   nsWeakPtr mParentWindowWeakPtr;
   nsWeakPtr mDocumentWeakPtr;
 };
 
 void
 MediaKeys::OnCDMCreated(PromiseId aId, const nsACString& aNodeId, const uint32_t aPluginId)
 {
-  nsRefPtr<Promise> promise(RetrievePromise(aId));
+  nsRefPtr<DetailedPromise> promise(RetrievePromise(aId));
   if (!promise) {
     return;
   }
   mNodeId = aNodeId;
   nsRefPtr<MediaKeys> keys(this);
   EME_LOG("MediaKeys[%p]::OnCDMCreated() resolve promise id=%d", this, aId);
   promise->MaybeResolve(keys);
   if (mCreatePromiseId == aId) {
@@ -518,17 +527,17 @@ MediaKeys::CreateSession(JSContext* aCx,
   mPendingSessions.Put(session->Token(), session);
 
   return session.forget();
 }
 
 void
 MediaKeys::OnSessionLoaded(PromiseId aId, bool aSuccess)
 {
-  nsRefPtr<Promise> promise(RetrievePromise(aId));
+  nsRefPtr<DetailedPromise> promise(RetrievePromise(aId));
   if (!promise) {
     return;
   }
   EME_LOG("MediaKeys[%p]::OnSessionLoaded() resolve promise id=%d", this, aId);
 
   promise->MaybeResolve(aSuccess);
 }
 
--- a/dom/media/eme/MediaKeys.h
+++ b/dom/media/eme/MediaKeys.h
@@ -13,29 +13,30 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/RefPtr.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsRefPtrHashtable.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/MediaKeysBinding.h"
 #include "mozIGeckoMediaPluginService.h"
+#include "mozilla/DetailedPromise.h"
 
 namespace mozilla {
 
 class CDMProxy;
 
 namespace dom {
 
 class ArrayBufferViewOrArrayBuffer;
 class MediaKeySession;
 class HTMLMediaElement;
 
 typedef nsRefPtrHashtable<nsStringHashKey, MediaKeySession> KeySessionHashMap;
-typedef nsRefPtrHashtable<nsUint32HashKey, dom::Promise> PromiseHashMap;
+typedef nsRefPtrHashtable<nsUint32HashKey, dom::DetailedPromise> PromiseHashMap;
 typedef nsRefPtrHashtable<nsUint32HashKey, MediaKeySession> PendingKeySessionsHashMap;
 typedef uint32_t PromiseId;
 
 // Helper function to extract data coming in from JS in an
 // (ArrayBuffer or ArrayBufferView) IDL typed function argument.
 bool
 CopyArrayBufferViewOrArrayBufferData(const ArrayBufferViewOrArrayBuffer& aBufferOrView,
                                      nsTArray<uint8_t>& aOutData);
@@ -48,35 +49,36 @@ class MediaKeys final : public nsISuppor
   ~MediaKeys();
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaKeys)
 
   MediaKeys(nsPIDOMWindow* aParentWindow, const nsAString& aKeySystem);
 
-  already_AddRefed<Promise> Init(ErrorResult& aRv);
+  already_AddRefed<DetailedPromise> Init(ErrorResult& aRv);
 
   nsPIDOMWindow* GetParentObject() const;
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   nsresult Bind(HTMLMediaElement* aElement);
 
   // Javascript: readonly attribute DOMString keySystem;
   void GetKeySystem(nsString& retval) const;
 
   // JavaScript: MediaKeys.createSession()
   already_AddRefed<MediaKeySession> CreateSession(JSContext* aCx,
                                                   SessionType aSessionType,
                                                   ErrorResult& aRv);
 
   // JavaScript: MediaKeys.SetServerCertificate()
-  already_AddRefed<Promise> SetServerCertificate(const ArrayBufferViewOrArrayBuffer& aServerCertificate,
-                                                 ErrorResult& aRv);
+  already_AddRefed<DetailedPromise>
+    SetServerCertificate(const ArrayBufferViewOrArrayBuffer& aServerCertificate,
+                         ErrorResult& aRv);
 
   already_AddRefed<MediaKeySession> GetSession(const nsAString& aSessionId);
 
   // Removes and returns MediaKeySession from the set of sessions awaiting
   // their sessionId to be assigned.
   already_AddRefed<MediaKeySession> GetPendingSession(uint32_t aToken);
 
   // Called once a Init() operation succeeds.
@@ -92,24 +94,25 @@ public:
   void OnSessionLoaded(PromiseId aId, bool aSuccess);
 
   // Called once a session has closed.
   void OnSessionClosed(MediaKeySession* aSession);
 
   CDMProxy* GetCDMProxy() { return mProxy; }
 
   // Makes a new promise, or nullptr on failure.
-  already_AddRefed<Promise> MakePromise(ErrorResult& aRv);
+  already_AddRefed<DetailedPromise> MakePromise(ErrorResult& aRv);
   // Stores promise in mPromises, returning an ID that can be used to retrieve
   // it later. The ID is passed to the CDM, so that it can signal specific
   // promises to be resolved.
-  PromiseId StorePromise(Promise* aPromise);
+  PromiseId StorePromise(DetailedPromise* aPromise);
 
   // Reject promise with DOMException corresponding to aExceptionCode.
-  void RejectPromise(PromiseId aId, nsresult aExceptionCode);
+  void RejectPromise(PromiseId aId, nsresult aExceptionCode,
+                     const nsCString& aReason);
   // Resolves promise with "undefined".
   void ResolvePromise(PromiseId aId);
 
   const nsCString& GetNodeId() const;
 
   void Shutdown();
 
   // Called by CDMProxy when CDM crashes or shuts down. It is different from
@@ -119,17 +122,17 @@ public:
   // Returns true if this MediaKeys has been bound to a media element.
   bool IsBoundToMediaElement() const;
 
 private:
 
   bool IsInPrivateBrowsing();
 
   // Removes promise from mPromises, and returns it.
-  already_AddRefed<Promise> RetrievePromise(PromiseId aId);
+  already_AddRefed<DetailedPromise> RetrievePromise(PromiseId aId);
 
   // Owning ref to proxy. The proxy has a weak reference back to the MediaKeys,
   // and the MediaKeys destructor clears the proxy's reference to the MediaKeys.
   nsRefPtr<CDMProxy> mProxy;
 
   nsRefPtr<HTMLMediaElement> mElement;
 
   nsCOMPtr<nsPIDOMWindow> mParent;
--- a/dom/media/eme/moz.build
+++ b/dom/media/eme/moz.build
@@ -14,23 +14,25 @@ EXPORTS.mozilla.dom += [
     'MediaKeySystemAccess.h',
     'MediaKeySystemAccessManager.h',
 ]
 
 EXPORTS.mozilla += [
     'CDMCallbackProxy.h',
     'CDMCaps.h',
     'CDMProxy.h',
-    'EMEUtils.h'
+    'DetailedPromise.h',
+    'EMEUtils.h',
 ]
 
 UNIFIED_SOURCES += [
     'CDMCallbackProxy.cpp',
     'CDMCaps.cpp',
     'CDMProxy.cpp',
+    'DetailedPromise.cpp',
     'EMEUtils.cpp',
     'MediaEncryptedEvent.cpp',
     'MediaKeyError.cpp',
     'MediaKeyMessageEvent.cpp',
     'MediaKeys.cpp',
     'MediaKeySession.cpp',
     'MediaKeyStatusMap.cpp',
     'MediaKeySystemAccess.cpp',
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -347,24 +347,26 @@ skip-if = (toolkit == 'android' && proce
 [test_bug495300.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_bug654550.html]
 [test_bug686942.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_bug726904.html]
 [test_bug874897.html]
 [test_bug879717.html]
+tags=capturestream
 skip-if = os == 'win' && !debug # bug 1140675
 [test_bug883173.html]
 [test_bug895091.html]
 [test_bug895305.html]
 [test_bug919265.html]
 [test_bug957847.html]
 [test_bug1018933.html]
 [test_bug1113600.html]
+tags=capturestream
 [test_can_play_type.html]
 [test_can_play_type_mpeg.html]
 skip-if = buildapp == 'b2g' || (toolkit == 'android' && processor == 'x86') # bug 1021675 #x86 only bug 914439
 [test_can_play_type_no_ogg.html]
 [test_can_play_type_ogg.html]
 [test_chaining.html]
 [test_clone_media_element.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
@@ -397,17 +399,17 @@ skip-if = toolkit == 'android' # bug 104
 # in future however, so I'm not removing the test, just disabling it.
 [test_eme_persistent_sessions.html]
 skip-if = toolkit == 'android' # bug 1043403
 [test_eme_playback.html]
 skip-if = toolkit == 'android' # bug 1043403
 [test_eme_requestKeySystemAccess.html]
 skip-if = toolkit == 'android' # bug 1043403
 [test_eme_stream_capture_blocked.html]
-tags=msg
+tags=msg capturestream
 skip-if = toolkit == 'android' || (os == 'win' && !debug) # bug 1043403, bug 1140675
 [test_empty_resource.html]
 [test_error_in_video_document.html]
 skip-if = toolkit == 'android' || (os == 'win' && !debug) || (os == 'mac' && !debug) # bug 608634
 [test_error_on_404.html]
 [test_fastSeek.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_fastSeek-forwards.html]
@@ -427,55 +429,55 @@ skip-if = (toolkit == 'android' && proce
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_media_selection.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_media_sniffer.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_mediarecorder_avoid_recursion.html]
 tags=msg
 [test_mediarecorder_creation.html]
-tags=msg
+tags=msg capturestream
 [test_mediarecorder_creation_fail.html]
 tags=msg
 [test_mediarecorder_getencodeddata.html]
 tags=msg
 [test_mediarecorder_record_4ch_audiocontext.html]
 tags=msg
 [test_mediarecorder_record_audiocontext.html]
 tags=msg
 [test_mediarecorder_record_audiocontext_mlk.html]
 tags=msg
 [test_mediarecorder_record_audionode.html]
 tags=msg
 [test_mediarecorder_record_gum_video_timeslice.html]
 tags=msg
 skip-if = buildapp == 'b2g' || toolkit == 'android' # mimetype check, bug 969289
 [test_mediarecorder_record_immediate_stop.html]
-tags=msg
+tags=msg capturestream
 [test_mediarecorder_record_no_timeslice.html]
-tags=msg
+tags=msg capturestream
 skip-if = toolkit == 'gonk' && debug
 [test_mediarecorder_record_nosrc.html]
-tags=msg
+tags=msg capturestream
 [test_mediarecorder_record_session.html]
-tags=msg
+tags=msg capturestream
 [test_mediarecorder_record_startstopstart.html]
 tags=msg
 skip-if = toolkit == 'gonk' && debug
 [test_mediarecorder_record_stopms.html]
 tags=msg
 [test_mediarecorder_record_timeslice.html]
-tags=msg
+tags=msg capturestream
 skip-if = toolkit == 'gonk' && debug
 [test_mediarecorder_reload_crash.html]
-tags=msg
+tags=msg capturestream
 [test_mediarecorder_unsupported_src.html]
 tags=msg
 [test_mediarecorder_record_getdata_afterstart.html]
-tags=msg
+tags=msg capturestream
 skip-if = toolkit == 'gonk' && debug
 [test_mediatrack_consuming_mediaresource.html]
 [test_mediatrack_consuming_mediastream.html]
 tags=msg
 [test_mediatrack_events.html]
 skip-if = toolkit == 'gonk' && debug # bug 1065924
 [test_mediatrack_parsing_ogg.html]
 [test_mediatrack_replay_from_end.html]
@@ -518,16 +520,17 @@ skip-if = toolkit == 'gonk' || (toolkit 
 [test_readyState.html]
 [test_referer.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
 [test_replay_metadata.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_reset_events_async.html]
 [test_reset_src.html]
 [test_video_dimensions.html]
+tags=capturestream
 [test_resume.html]
 skip-if = true # bug 1021673
 [test_seek_out_of_range.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_seek-1.html]
 skip-if = android_version == '10' # bug 1059116
 [test_seek-2.html]
 [test_seek-3.html]
@@ -548,37 +551,37 @@ skip-if = (toolkit == 'android' && proce
 [test_seekLies.html]
 [test_source.html]
 [test_source_media.html]
 [test_source_null.html]
 [test_source_write.html]
 [test_standalone.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_streams_autoplay.html]
-tags=msg
+tags=msg capturestream
 [test_streams_element_capture.html]
 #x86 only bug 914439, b2g desktop bug 752796
 skip-if = (toolkit == 'android' && processor == 'x86') || (buildapp == 'b2g' && toolkit != 'gonk')
-tags=msg
+tags=msg capturestream
 [test_streams_element_capture_createObjectURL.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
-tags=msg
+tags=msg capturestream
 [test_streams_element_capture_playback.html]
-tags=msg
+tags=msg capturestream
 [test_streams_element_capture_reset.html]
-tags=msg
+tags=msg capturestream
 [test_streams_gc.html]
 skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1096270
-tags=msg
+tags=msg capturestream
 [test_streams_individual_pause.html]
 tags=msg
 [test_streams_srcObject.html]
-tags=msg
+tags=msg capturestream
 [test_streams_tracks.html]
-tags=msg
+tags=msg capturestream
 [test_texttrack.html]
 [test_texttrackcue.html]
 [test_texttracklist.html]
 [test_texttrackregion.html]
 [test_timeupdate_small_files.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_trackelementevent.html]
 [test_trackevent.html]
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -100,16 +100,17 @@ skip-if = toolkit == 'gonk' || buildapp 
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_bug834153.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_bug1013809.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g emulator seems to be too slow (Bug 1016498 and 1008080)
 [test_peerConnection_bug1042791.html]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet' || os == 'android' # bug 1043403 # Bug 1141029 Mulet parity with B2G Desktop for TC
 [test_peerConnection_capturedVideo.html]
+tags=capturestream
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_captureStream_canvas_2d.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_captureStream_canvas_webgl.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_close.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_errorCallbacks.html]
--- a/dom/media/webaudio/test/mochitest.ini
+++ b/dom/media/webaudio/test/mochitest.ini
@@ -44,16 +44,17 @@ skip-if = (toolkit == 'android' && (proc
 [test_audioBufferSourceNodeNullBuffer.html]
 [test_audioBufferSourceNodeOffset.html]
 skip-if = (toolkit == 'gonk') || (toolkit == 'android') || debug #bug 906752
 [test_audioBufferSourceNodePassThrough.html]
 [test_audioBufferSourceNodeRate.html]
 [test_AudioContext.html]
 skip-if = android_version == '10' # bug 1138462
 [test_audioContextSuspendResumeClose.html]
+tags=capturestream
 [test_audioDestinationNode.html]
 [test_AudioListener.html]
 [test_audioParamExponentialRamp.html]
 [test_audioParamGain.html]
 [test_audioParamLinearRamp.html]
 [test_audioParamSetCurveAtTime.html]
 [test_audioParamSetCurveAtTimeZeroDuration.html]
 [test_audioParamSetTargetAtTime.html]
@@ -75,16 +76,17 @@ skip-if = android_version == '10' # bug 
 [test_bug867174.html]
 [test_bug867203.html]
 [test_bug875221.html]
 [test_bug875402.html]
 [test_bug894150.html]
 [test_bug956489.html]
 [test_bug964376.html]
 [test_bug966247.html]
+tags=capturestream
 [test_bug972678.html]
 [test_bug1056032.html]
 skip-if = toolkit == 'android' # bug 1056706
 [test_channelMergerNode.html]
 [test_channelMergerNodeWithVolume.html]
 [test_channelSplitterNode.html]
 [test_channelSplitterNodeWithVolume.html]
 skip-if = (android_version == '18' && debug) # bug 1158417
@@ -111,25 +113,30 @@ skip-if = toolkit == 'android' # bug 105
 [test_dynamicsCompressorNode.html]
 [test_dynamicsCompressorNodePassThrough.html]
 [test_gainNode.html]
 [test_gainNodeInLoop.html]
 [test_gainNodePassThrough.html]
 [test_maxChannelCount.html]
 [test_mediaDecoding.html]
 [test_mediaElementAudioSourceNode.html]
+tags=capturestream
 [test_mediaElementAudioSourceNodePassThrough.html]
+tags=capturestream
 skip-if = toolkit == 'android' # bug 1145816
 [test_mediaElementAudioSourceNodeCrossOrigin.html]
+tags=capturestream
 skip-if = toolkit == 'android' # bug 1145816
 [test_mediaStreamAudioDestinationNode.html]
 [test_mediaStreamAudioSourceNode.html]
 [test_mediaStreamAudioSourceNodeCrossOrigin.html]
+tags=capturestream
 [test_mediaStreamAudioSourceNodePassThrough.html]
 [test_mediaStreamAudioSourceNodeResampling.html]
+tags=capturestream
 [test_mixingRules.html]
 skip-if = android_version == '10' || android_version == '18' # bug 1091965
 [test_mozaudiochannel.html]
 # Android: bug 1061675; OSX 10.6: bug 1097721
 skip-if = (toolkit == 'gonk' && !debug) || android_version == '10' || android_version == '18' || (os == 'mac' && os_version == '10.6')
 [test_nodeToParamConnection.html]
 [test_OfflineAudioContext.html]
 [test_offlineDestinationChannelCountLess.html]
--- a/dom/media/webspeech/recognition/test/mochitest.ini
+++ b/dom/media/webspeech/recognition/test/mochitest.ini
@@ -6,16 +6,17 @@ support-files =
   hello.ogg^headers^
   silence.ogg
   silence.ogg^headers^
 
 [test_abort.html]
 skip-if = toolkit == 'android' || toolkit == 'gonk' # bug 1037287
 [test_audio_capture_error.html]
 [test_call_start_from_end_handler.html]
+tags=capturestream
 skip-if = (android_version == '18' && debug) # bug 967606
 [test_nested_eventloop.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s # b2g(showmodaldialog)
 [test_preference_enable.html]
 [test_recognition_service_error.html]
 skip-if = buildapp == 'b2g' # b2g(timed out)
 [test_success_without_recognition_service.html]
 [test_timeout.html]
--- a/dom/network/TCPSocket.js
+++ b/dom/network/TCPSocket.js
@@ -685,24 +685,26 @@ TCPSocket.prototype = {
 
   send: function ts_send(data, byteOffset, byteLength) {
     if (this._readyState !== kOPEN) {
       throw new Error("Socket not open.");
     }
 
     if (this._binaryType === "arraybuffer") {
       byteLength = byteLength || data.byteLength;
+    } else {
+      data = data.toString();
+      byteLength = data.length;
     }
 
     if (this._inChild) {
       this._socketBridge.sendSend(data, byteOffset, byteLength, ++this._trackingNumber);
     }
 
-    let length = this._binaryType === "arraybuffer" ? byteLength : data.length;
-    let newBufferedAmount = this.bufferedAmount + length;
+    let newBufferedAmount = this.bufferedAmount + byteLength;
     let bufferFull = newBufferedAmount >= BUFFER_SIZE;
 
     if (bufferFull) {
       // If we buffered more than some arbitrary amount of data,
       // (65535 right now) we should tell the caller so they can
       // wait until ondrain is called if they so desire. Once all the
       // buffered data has been written to the socket, ondrain is
       // called.
@@ -717,32 +719,32 @@ TCPSocket.prototype = {
     }
 
     let new_stream;
     if (this._binaryType === "arraybuffer") {
       new_stream = new ArrayBufferInputStream();
       new_stream.setData(data, byteOffset, byteLength);
     } else {
       new_stream = new StringInputStream();
-      new_stream.setData(data, length);
+      new_stream.setData(data, byteLength);
     }
 
     if (this._waitingForStartTLS) {
       // When we are waiting for starttls, new_stream is added to pendingData
       // and will be appended to multiplexStream after tls had been set up.
       this._pendingDataAfterStartTLS.push(new_stream);
     } else {
       this._multiplexStream.appendStream(new_stream);
     }
 
     this._ensureCopying();
 
 #ifdef MOZ_WIDGET_GONK
     // Collect transmitted amount for network statistics.
-    this._txBytes += length;
+    this._txBytes += byteLength;
     this._saveNetworkStats(false);
 #endif
 
     return !bufferFull;
   },
 
   suspend: function ts_suspend() {
     if (this._inChild) {
--- a/dom/network/tests/test_tcpsocket_client_and_server_basics.js
+++ b/dom/network/tests/test_tcpsocket_client_and_server_basics.js
@@ -341,16 +341,40 @@ function* test_basics() {
     bigUint8Array.length);
   assertUint8ArraysEqual(serverReceived, bigUint8Array,
                          'server received/client sent');
   // And a close.
   is((yield serverQueue.waitForEvent()).type, 'close',
      'The drain event should fire after a large send that returned true.');
 
 
+  // -- Re-establish connection
+  connectedPromise = waitForConnection(listeningServer);
+  clientSocket = TCPSocket.open('127.0.0.1', serverPort,
+                                { binaryType: 'string' });
+  clientQueue = listenForEventsOnSocket(clientSocket, 'client');
+  is((yield clientQueue.waitForEvent()).type, 'open', 'got open event');
+
+  connectedResult = yield connectedPromise;
+  // destructuring assignment is not yet ES6 compliant, must manually unpack
+  serverSocket = connectedResult.socket;
+  serverQueue = connectedResult.queue;
+
+  // -- Attempt to send non-string data.
+  is(clientSocket.send(bigUint8Array), true,
+     'Client sending a large non-string should only send a small string.');
+  clientSocket.close();
+  // The server will get its data
+  serverReceived = yield serverQueue.waitForDataWithAtLeastLength(
+    bigUint8Array.toString().length);
+  // Then we'll get a close
+  is((yield clientQueue.waitForEvent()).type, 'close',
+     'The close event should fire after the drain event.');
+
+
   // -- Close the listening server (and try to connect)
   // We want to verify that the server actually closes / stops listening when
   // we tell it to.
   listeningServer.close();
 
   // - try and connect, get an error
   clientSocket = TCPSocket.open('127.0.0.1', serverPort,
                                 { binaryType: 'arraybuffer' });
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -78,17 +78,17 @@ typedef js::HashMap<nsJSObjWrapperKey,
                     js::SystemAllocPolicy> JSObjWrapperTable;
 static JSObjWrapperTable sJSObjWrappers;
 
 // Whether it's safe to iterate sJSObjWrappers.  Set to true when sJSObjWrappers
 // has been initialized and is not currently being enumerated.
 static bool sJSObjWrappersAccessible = false;
 
 // Hash of NPObject wrappers that wrap NPObjects as JSObjects.
-static PLDHashTable2* sNPObjWrappers;
+static PLDHashTable* sNPObjWrappers;
 
 // Global wrapper count. This includes JSObject wrappers *and*
 // NPObject wrappers. When this count goes to zero, there are no more
 // wrappers and we can kill off hash tables etc.
 static int32_t sWrapperCount;
 
 // The runtime service used to register/unregister GC callbacks.
 nsCOMPtr<nsIJSRuntimeService> sCallbackRuntime;
@@ -403,17 +403,17 @@ CreateNPObjWrapperTable()
 {
   MOZ_ASSERT(!sNPObjWrappers);
 
   if (!RegisterGCCallbacks()) {
     return false;
   }
 
   sNPObjWrappers =
-    new PLDHashTable2(PL_DHashGetStubOps(), sizeof(NPObjWrapperHashEntry));
+    new PLDHashTable(PL_DHashGetStubOps(), sizeof(NPObjWrapperHashEntry));
   return true;
 }
 
 static void
 DestroyNPObjWrapperTable()
 {
   MOZ_ASSERT(sNPObjWrappers->EntryCount() == 0);
 
@@ -1965,17 +1965,17 @@ NPObjWrapperPluginDestroyedCallback(PLDH
                                     uint32_t number, void *arg)
 {
   NPObjWrapperHashEntry *entry = (NPObjWrapperHashEntry *)hdr;
   NppAndCx *nppcx = reinterpret_cast<NppAndCx *>(arg);
 
   if (entry->mNpp == nppcx->npp) {
     // HACK: temporarily hide the hash we're enumerating so that invalidate()
     // and deallocate() don't touch it.
-    PLDHashTable2 *tmp = static_cast<PLDHashTable2*>(table);
+    PLDHashTable *tmp = static_cast<PLDHashTable*>(table);
     sNPObjWrappers = nullptr;
 
     NPObject *npobj = entry->mNPObj;
 
     if (npobj->_class && npobj->_class->invalidate) {
       npobj->_class->invalidate(npobj);
     }
 
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -5,16 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/Promise.h"
 
 #include "jsfriendapi.h"
 #include "js/Debug.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMError.h"
+#include "mozilla/dom/DOMException.h"
 #include "mozilla/dom/OwningNonNull.h"
 #include "mozilla/dom/PromiseBinding.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/MediaStreamError.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/Preferences.h"
 #include "PromiseCallback.h"
@@ -1690,16 +1691,20 @@ PromiseWorkerProxy::CleanUp(JSContext* a
 }
 
 // Specializations of MaybeRejectBrokenly we actually support.
 template<>
 void Promise::MaybeRejectBrokenly(const nsRefPtr<DOMError>& aArg) {
   MaybeSomething(aArg, &Promise::MaybeReject);
 }
 template<>
+void Promise::MaybeRejectBrokenly(const nsRefPtr<DOMException>& aArg) {
+  MaybeSomething(aArg, &Promise::MaybeReject);
+}
+template<>
 void Promise::MaybeRejectBrokenly(const nsAString& aArg) {
   MaybeSomething(aArg, &Promise::MaybeReject);
 }
 
 uint64_t
 Promise::GetID() {
   if (mID != 0) {
     return mID;
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -785,16 +785,21 @@ public:
     callback->UpdateFailed(aError);
     FailCommon(NS_ERROR_DOM_JS_EXCEPTION);
   }
 
   // Public so our error handling code can continue with a successful worker.
   void
   ContinueInstall()
   {
+    // mRegistration will be null if we have already Fail()ed.
+    if (!mRegistration) {
+      return;
+    }
+
     // Even if we are canceled, ensure integrity of mSetOfScopesBeingUpdated
     // first.
     nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     MOZ_ASSERT(swm->mSetOfScopesBeingUpdated.Contains(mRegistration->mScope));
     swm->mSetOfScopesBeingUpdated.Remove(mRegistration->mScope);
     // This is effectively the end of Step 4.3 of the [[Update]] algorithm.
     // The invocation of [[Install]] is not part of the atomic block.
 
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -101,16 +101,17 @@ support-files =
   app-protocol/*
   force_refresh_worker.js
   sw_clients/refresher.html
   sw_clients/refresher_compressed.html
   sw_clients/refresher_compressed.html^headers^
   sw_clients/refresher_cached.html
   sw_clients/refresher_cached_compressed.html
   sw_clients/refresher_cached_compressed.html^headers^
+  strict_mode_error.js
 
 [test_unregister.html]
 [test_installation_simple.html]
 [test_fetch_event.html]
 [test_https_fetch.html]
 [test_https_fetch_cloned_response.html]
 [test_https_synth_fetch_from_cached_sw.html]
 [test_match_all.html]
@@ -141,8 +142,9 @@ support-files =
 [test_claim.html]
 [test_periodic_https_update.html]
 [test_sanitize.html]
 [test_sanitize_domain.html]
 [test_service_worker_allowed.html]
 [test_app_protocol.html]
 [test_claim_fetch.html]
 [test_force_refresh.html]
+[test_strict_mode_error.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/strict_mode_error.js
@@ -0,0 +1,4 @@
+function f() {
+  return 1;
+  return 2;
+}
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_strict_mode_error.html
@@ -0,0 +1,39 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1170550 - test registration of service worker scripts with a strict mode error</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+  function runTest() {
+    navigator.serviceWorker
+      .register("strict_mode_error.js", {scope: "strict_mode_error"})
+      .catch(() => {
+        ok(true, "Registration failed as expected");
+        SimpleTest.finish();
+      });
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  onload = function() {
+    SpecialPowers.pushPrefEnv({"set": [
+      ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+      ["dom.serviceWorkers.enabled", true],
+      ["dom.serviceWorkers.testing.enabled", true],
+    ]}, runTest);
+  };
+</script>
+</pre>
+</body>
+</html>
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -760,17 +760,17 @@ XULDocument::AddBroadcastListenerFor(Ele
         PL_DHashVoidPtrKeyStub,
         PL_DHashMatchEntryStub,
         PL_DHashMoveEntryStub,
         ClearBroadcasterMapEntry,
         nullptr
     };
 
     if (! mBroadcasterMap) {
-        mBroadcasterMap = new PLDHashTable2(&gOps, sizeof(BroadcasterMapEntry));
+        mBroadcasterMap = new PLDHashTable(&gOps, sizeof(BroadcasterMapEntry));
     }
 
     BroadcasterMapEntry* entry =
         static_cast<BroadcasterMapEntry*>
                    (PL_DHashTableSearch(mBroadcasterMap, &aBroadcaster));
 
     if (!entry) {
         entry = static_cast<BroadcasterMapEntry*>
--- a/dom/xul/XULDocument.h
+++ b/dom/xul/XULDocument.h
@@ -714,17 +714,17 @@ protected:
         NS_DECL_NSIREQUESTOBSERVER
     };
 
     friend class ParserObserver;
 
     /**
      * A map from a broadcaster element to a list of listener elements.
      */
-    PLDHashTable2* mBroadcasterMap;
+    PLDHashTable* mBroadcasterMap;
 
     nsAutoPtr<nsInterfaceHashtable<nsURIHashKey,nsIObserver> > mOverlayLoadObservers;
     nsAutoPtr<nsInterfaceHashtable<nsURIHashKey,nsIObserver> > mPendingOverlayLoadNotifications;
 
     bool mInitialLayoutComplete;
 
     class nsDelayedBroadcastUpdate
     {
--- a/dom/xul/templates/nsContentSupportMap.cpp
+++ b/dom/xul/templates/nsContentSupportMap.cpp
@@ -1,24 +1,18 @@
 /* -*- 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 "nsContentSupportMap.h"
 #include "nsXULElement.h"
 
-nsresult
+void
 nsContentSupportMap::Remove(nsIContent* aElement)
 {
-    if (!mMap.IsInitialized())
-        return NS_ERROR_NOT_INITIALIZED;
-
     nsIContent* child = aElement;
     do {
         PL_DHashTableRemove(&mMap, child);
         child = child->GetNextNode(aElement);
     } while(child);
-
-    return NS_OK;
 }
 
-
--- a/dom/xul/templates/nsContentSupportMap.h
+++ b/dom/xul/templates/nsContentSupportMap.h
@@ -42,22 +42,22 @@ public:
         if (!hdr)
             return false;
 
         Entry* entry = static_cast<Entry*>(hdr);
         *aMatch = entry->mMatch;
         return true;
     }
 
-    nsresult Remove(nsIContent* aElement);
+    void Remove(nsIContent* aElement);
 
     void Clear() { mMap.Clear(); }
 
 protected:
-    PLDHashTable2 mMap;
+    PLDHashTable mMap;
 
     struct Entry : public PLDHashEntryHdr {
         nsIContent*      mContent;
         nsTemplateMatch* mMatch;
     };
 };
 
 #endif
--- a/dom/xul/templates/nsTemplateMap.h
+++ b/dom/xul/templates/nsTemplateMap.h
@@ -11,17 +11,17 @@
 
 class nsTemplateMap {
 protected:
     struct Entry : public PLDHashEntryHdr {
         nsIContent*     mContent;
         nsIContent*     mTemplate;
     };
 
-    PLDHashTable2 mTable;
+    PLDHashTable mTable;
 
 public:
     nsTemplateMap() : mTable(PL_DHashGetStubOps(), sizeof(Entry)) { }
 
     ~nsTemplateMap() { }
 
     void
     Put(nsIContent* aContent, nsIContent* aTemplate) {
--- a/embedding/components/commandhandler/nsCommandParams.h
+++ b/embedding/components/commandhandler/nsCommandParams.h
@@ -119,14 +119,14 @@ protected:
   static bool HashMatchEntry(PLDHashTable* aTable,
                              const PLDHashEntryHdr* aEntry, const void* aKey);
 
   static void HashMoveEntry(PLDHashTable* aTable, const PLDHashEntryHdr* aFrom,
                             PLDHashEntryHdr* aTo);
 
   static void HashClearEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry);
 
-  PLDHashTable2 mValuesHash;
+  PLDHashTable mValuesHash;
 
   static const PLDHashTableOps sHashOps;
 };
 
 #endif // nsCommandParams_h__
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -729,16 +729,17 @@ struct ParamTraits<mozilla::layers::Fram
     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.mIsLayersIdRoot);
+    WriteParam(aMsg, aParam.mUsesContainerScrolling);
     WriteParam(aMsg, aParam.GetContentDescription());
   }
 
   static bool ReadContentDescription(const Message* aMsg, void** aIter, paramType* aResult)
   {
     nsCString str;
     if (!ReadParam(aMsg, aIter, &str)) {
       return false;
@@ -773,16 +774,17 @@ struct ParamTraits<mozilla::layers::Fram
             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) &&
             ReadParam(aMsg, aIter, &aResult->mIsLayersIdRoot) &&
+            ReadParam(aMsg, aIter, &aResult->mUsesContainerScrolling) &&
             ReadContentDescription(aMsg, aIter, aResult));
   }
 };
 
 template<>
 struct ParamTraits<mozilla::layers::TextureFactoryIdentifier>
 {
   typedef mozilla::layers::TextureFactoryIdentifier paramType;
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -9,16 +9,17 @@
 #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 "gfxPrefs.h"                   // for LayoutUseContainersForRootFrames
 #include "nsString.h"
 
 namespace IPC {
 template <typename T> struct ParamTraits;
 } // namespace IPC
 
 namespace mozilla {
 namespace layers {
@@ -63,16 +64,17 @@ public:
     , mPresShellId(-1)
     , mViewport(0, 0, 0, 0)
     , mExtraResolution()
     , mBackgroundColor(0, 0, 0, 0)
     , mLineScrollAmount(0, 0)
     , mPageScrollAmount(0, 0)
     , mAllowVerticalScrollWithWheel(false)
     , mIsLayersIdRoot(false)
+    , mUsesContainerScrolling(false)
   {
   }
 
   // Default copy ctor and operator= are fine
 
   bool operator==(const FrameMetrics& aOther) const
   {
     return mCompositionBounds.IsEqualEdges(aOther.mCompositionBounds) &&
@@ -97,17 +99,18 @@ public:
            mScrollGeneration == aOther.mScrollGeneration &&
            mExtraResolution == aOther.mExtraResolution &&
            mBackgroundColor == aOther.mBackgroundColor &&
            mDoSmoothScroll == aOther.mDoSmoothScroll &&
            mLineScrollAmount == aOther.mLineScrollAmount &&
            mPageScrollAmount == aOther.mPageScrollAmount &&
            mAllowVerticalScrollWithWheel == aOther.mAllowVerticalScrollWithWheel &&
            mClipRect == aOther.mClipRect &&
-           mIsLayersIdRoot == aOther.mIsLayersIdRoot;
+           mIsLayersIdRoot == aOther.mIsLayersIdRoot &&
+		   mUsesContainerScrolling == aOther.mUsesContainerScrolling;
   }
   bool operator!=(const FrameMetrics& aOther) const
   {
     return !operator==(aOther);
   }
 
   bool IsDefault() const
   {
@@ -529,16 +532,24 @@ public:
 
   void SetIsLayersIdRoot(bool aValue) {
     mIsLayersIdRoot = aValue;
   }
   bool IsLayersIdRoot() const {
     return mIsLayersIdRoot;
   }
 
+  void SetUsesContainerScrolling(bool aValue) {
+    MOZ_ASSERT_IF(aValue, gfxPrefs::LayoutUseContainersForRootFrames());
+    mUsesContainerScrolling = aValue;
+  }
+  bool UsesContainerScrolling() const {
+    return mUsesContainerScrolling;
+  }
+
 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
@@ -707,16 +718,20 @@ private:
 
   // The clip rect to use when compositing a layer with this FrameMetrics.
   Maybe<ParentLayerIntRect> mClipRect;
 
   // Whether these framemetrics are for the root scroll frame (root element if
   // we don't have a root scroll frame) for its layers id.
   bool mIsLayersIdRoot;
 
+  // True if scrolling using containers, false otherwise. This can be removed
+  // when containerful scrolling is eliminated.
+  bool mUsesContainerScrolling;
+
   // 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/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -924,16 +924,37 @@ Layer::GetVisibleRegionRelativeToRootLay
     // positioning code.
     offset += currentLayerOffset;
   }
 
   *aLayerOffset = nsIntPoint(offset.x, offset.y);
   return true;
 }
 
+Maybe<ParentLayerIntRect>
+Layer::GetCombinedClipRect() const
+{
+  Maybe<ParentLayerIntRect> clip = GetClipRect();
+
+  for (size_t i = 0; i < mFrameMetrics.Length(); i++) {
+    if (!mFrameMetrics[i].HasClipRect()) {
+      continue;
+    }
+
+    const ParentLayerIntRect& other = mFrameMetrics[i].ClipRect();
+    if (clip) {
+      clip = Some(clip.value().Intersect(other));
+    } else {
+      clip = Some(other);
+    }
+  }
+
+  return clip;
+}
+
 ContainerLayer::ContainerLayer(LayerManager* aManager, void* aImplData)
   : Layer(aManager, aImplData),
     mFirstChild(nullptr),
     mLastChild(nullptr),
     mPreXScale(1.0f),
     mPreYScale(1.0f),
     mInheritedXScale(1.0f),
     mInheritedYScale(1.0f),
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -1250,16 +1250,22 @@ public:
   const LayerRect& GetStickyScrollRangeOuter() { return mStickyPositionData->mOuter; }
   const LayerRect& GetStickyScrollRangeInner() { return mStickyPositionData->mInner; }
   FrameMetrics::ViewID GetScrollbarTargetContainerId() { return mScrollbarTargetId; }
   ScrollDirection GetScrollbarDirection() { return mScrollbarDirection; }
   float GetScrollbarThumbRatio() { return mScrollbarThumbRatio; }
   bool IsScrollbarContainer() { return mIsScrollbarContainer; }
   Layer* GetMaskLayer() const { return mMaskLayer; }
 
+  /*
+   * Get the combined clip rect of the Layer clip and all clips on FrameMetrics.
+   * This is intended for use in Layout. The compositor needs to apply async
+   * transforms to find the combined clip.
+   */
+  Maybe<ParentLayerIntRect> GetCombinedClipRect() const;
 
   /**
    * Retrieve the root level visible region for |this| taking into account
    * clipping applied to parent layers of |this| as well as subtracting
    * visible regions of higher siblings of this layer and each ancestor.
    *
    * Note translation values for offsets of visible regions and accumulated
    * aLayerOffset are integer rounded using Point's RoundedToInt.
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -2898,16 +2898,17 @@ void AsyncPanZoomController::NotifyLayer
     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());
     mFrameMetrics.SetIsLayersIdRoot(aLayerMetrics.IsLayersIdRoot());
+    mFrameMetrics.SetUsesContainerScrolling(aLayerMetrics.UsesContainerScrolling());
 
     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/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -575,31 +575,40 @@ AsyncCompositionManager::ApplyAsyncConte
   }
 
   Matrix4x4 oldTransform = aLayer->GetTransform();
 
   Matrix4x4 combinedAsyncTransformWithoutOverscroll;
   Matrix4x4 combinedAsyncTransform;
   bool hasAsyncTransform = false;
   LayerMargin fixedLayerMargins(0, 0, 0, 0);
-  Maybe<ParentLayerIntRect> clipRect = aLayer->AsLayerComposite()->GetShadowClipRect();
+
+  // Each layer has multiple clips. Its local clip, which must move with async
+  // transforms, and its scrollframe clips, which are the clips between each
+  // scrollframe and its ancestor scrollframe. Scrollframe clips include the
+  // composition bounds and any other clips induced by layout.
+  //
+  // The final clip for the layer is the intersection of these clips.
+  Maybe<ParentLayerIntRect> asyncClip = aLayer->GetClipRect();
 
   for (uint32_t i = 0; i < aLayer->GetFrameMetricsCount(); i++) {
     AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController(i);
     if (!controller) {
       continue;
     }
 
     hasAsyncTransform = true;
 
     ViewTransform asyncTransformWithoutOverscroll;
     ParentLayerPoint scrollOffset;
     controller->SampleContentTransformForFrame(&asyncTransformWithoutOverscroll,
                                                scrollOffset);
     Matrix4x4 overscrollTransform = controller->GetOverscrollTransform();
+    Matrix4x4 asyncTransform =
+      Matrix4x4(asyncTransformWithoutOverscroll) * overscrollTransform;
 
     if (!aLayer->IsScrollInfoLayer()) {
       controller->MarkAsyncTransformAppliedToContent();
     }
 
     const FrameMetrics& metrics = aLayer->GetFrameMetrics(i);
     ScreenPoint offset(0, 0);
     // TODO: When we enable APZ on Fennec, we'll need to call SyncFrameMetrics here.
@@ -618,54 +627,44 @@ 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()));
+    // Transform the current local clip by this APZC's async transform. If we're
+    // using containerful scrolling, then the clip is not part of the scrolled
+    // frame and should not be transformed.
+    if (asyncClip && !metrics.UsesContainerScrolling()) {
+      asyncClip = Some(TransformTo<ParentLayerPixel>(asyncTransform, *asyncClip));
+    }
+
+    // Combine the local clip with the ancestor scrollframe clip. This is not
+    // included in the async transform above, since the ancestor clip should not
+    // move with this APZC.
+    if (metrics.HasClipRect()) {
+      ParentLayerIntRect clip = metrics.ClipRect();
+      if (asyncClip) {
+        asyncClip = Some(clip.Intersect(*asyncClip));
       } else {
-        clipRect = Some(metrics.ClipRect());
+        asyncClip = Some(clip);
       }
     }
 
     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
-      // bounds of the scroll frames *below* it.
-      // Therefore, if we have multiple scroll frames associated with this
-      // layer, the clip rect needs to be adjusted for the async transforms of
-      // the scroll frames other than the bottom-most one.
-      // To make this adjustment, we start with the Layout-provided clip rect,
-      // and at each level other than the bottom, transform it by the async
-      // transform at that level, and then re-intersect it with the composition
-      // bounds at that level.
-      ParentLayerRect transformed = TransformTo<ParentLayerPixel>(
-        (Matrix4x4(asyncTransformWithoutOverscroll) * overscrollTransform),
-        ParentLayerRect(*clipRect));
-      clipRect = Some(RoundedOut(transformed.Intersect(metrics.GetCompositionBounds())));
-    }
+    combinedAsyncTransform *= asyncTransform;
   }
 
   if (hasAsyncTransform) {
-    if (clipRect) {
-      aLayer->AsLayerComposite()->SetShadowClipRect(clipRect);
+    if (asyncClip) {
+      aLayer->AsLayerComposite()->SetShadowClipRect(asyncClip);
     }
+
     // Apply the APZ transform on top of GetLocalTransform() here (rather than
     // GetTransform()) in case the OMTA code in SampleAnimations already set a
     // shadow transform; in that case we want to apply ours on top of that one
     // rather than clobber it.
     SetShadowTransform(aLayer,
         aLayer->GetLocalTransform() * AdjustForClip(combinedAsyncTransform, aLayer));
 
     const FrameMetrics& bottom = LayerMetricsWrapper::BottommostScrollableMetrics(aLayer);
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -612,55 +612,56 @@ FT2FontFamily::AddFacesToFontList(Infall
  * find their attributes), leading to significantly quicker startup.
  */
 
 #define CACHE_KEY "font.cached-list"
 
 class FontNameCache {
 public:
     FontNameCache()
-        : mWriteNeeded(false)
+        : mMap(&mOps, sizeof(FNCMapEntry), 0)
+        , mWriteNeeded(false)
     {
+        // HACK ALERT: it's weird to assign |mOps| after we passed a pointer to
+        // it to |mMap|'s constructor. A more normal approach here would be to
+        // have a static |sOps| member. Unfortunately, this mysteriously but
+        // consistently makes Fennec start-up slower, so we take this
+        // unorthodox approach instead. It's safe because PLDHashTable's
+        // constructor doesn't dereference the pointer; it just makes a copy of
+        // it.
         mOps = (PLDHashTableOps) {
             StringHash,
             HashMatchEntry,
             MoveEntry,
             PL_DHashClearEntryStub,
             nullptr
         };
 
-        PL_DHashTableInit(&mMap, &mOps, sizeof(FNCMapEntry), 0);
-
         MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default,
                    "StartupCacheFontNameCache should only be used in chrome "
                    "process");
         mCache = mozilla::scache::StartupCache::GetSingleton();
 
         Init();
     }
 
     ~FontNameCache()
     {
-        if (!mMap.IsInitialized()) {
-            return;
-        }
         if (!mWriteNeeded || !mCache) {
-            PL_DHashTableFinish(&mMap);
             return;
         }
 
         nsAutoCString buf;
         PL_DHashTableEnumerate(&mMap, WriteOutMap, &buf);
-        PL_DHashTableFinish(&mMap);
         mCache->PutBuffer(CACHE_KEY, buf.get(), buf.Length() + 1);
     }
 
     void Init()
     {
-        if (!mMap.IsInitialized() || !mCache) {
+        if (!mCache) {
             return;
         }
         uint32_t size;
         char* buf;
         if (NS_FAILED(mCache->GetBuffer(CACHE_KEY, &buf, &size))) {
             return;
         }
 
@@ -705,19 +706,16 @@ public:
         // Should we use free() or delete[] here? See bug 684700.
         free(buf);
     }
 
     virtual void
     GetInfoForFile(const nsCString& aFileName, nsCString& aFaceList,
                    uint32_t *aTimestamp, uint32_t *aFilesize)
     {
-        if (!mMap.IsInitialized()) {
-            return;
-        }
         FNCMapEntry *entry =
             static_cast<FNCMapEntry*>(PL_DHashTableSearch(&mMap,
                                                           aFileName.get()));
         if (entry) {
             *aTimestamp = entry->mTimestamp;
             *aFilesize = entry->mFilesize;
             aFaceList.Assign(entry->mFaces);
             // this entry does correspond to an existing file
@@ -726,19 +724,16 @@ public:
             entry->mFileExists = true;
         }
     }
 
     virtual void
     CacheFileInfo(const nsCString& aFileName, const nsCString& aFaceList,
                   uint32_t aTimestamp, uint32_t aFilesize)
     {
-        if (!mMap.IsInitialized()) {
-            return;
-        }
         FNCMapEntry* entry = static_cast<FNCMapEntry*>
             (PL_DHashTableAdd(&mMap, aFileName.get(), fallible));
         if (entry) {
             entry->mFilename.Assign(aFileName);
             entry->mTimestamp = aTimestamp;
             entry->mFilesize = aFilesize;
             entry->mFaces.Assign(aFaceList);
             entry->mFileExists = true;
--- a/gfx/thebes/gfxPlatformMac.cpp
+++ b/gfx/thebes/gfxPlatformMac.cpp
@@ -571,17 +571,26 @@ static CVReturn VsyncCallback(CVDisplayL
 {
   // Executed on OS X hardware vsync thread
   OSXVsyncSource::OSXDisplay* display = (OSXVsyncSource::OSXDisplay*) aDisplayLinkContext;
   int64_t nextVsyncTimestamp = aOutputTime->hostTime;
   mozilla::TimeStamp nextVsync = mozilla::TimeStamp::FromSystemTime(nextVsyncTimestamp);
 
   mozilla::TimeStamp previousVsync = display->mPreviousTimestamp;
   display->mPreviousTimestamp = nextVsync;
-  MOZ_ASSERT(TimeStamp::Now() > previousVsync);
+  mozilla::TimeStamp now = TimeStamp::Now();
+  MOZ_ASSERT(nextVsync > previousVsync);
+
+  // Bug 1158321 - The VsyncCallback can sometimes execute before the reported
+  // vsync time. In those cases, normalize the timestamp to Now() as sending
+  // timestamps in the future has undefined behavior. See the comment above
+  // OSXDisplay::mPreviousTimestamp
+  if (now < previousVsync) {
+    previousVsync = now;
+  }
 
   display->NotifyVsync(previousVsync);
   return kCVReturnSuccess;
 }
 
 already_AddRefed<mozilla::gfx::VsyncSource>
 gfxPlatformMac::CreateHardwareVsyncSource()
 {
--- a/image/SurfaceCache.cpp
+++ b/image/SurfaceCache.cpp
@@ -388,16 +388,17 @@ public:
                    uint32_t aSurfaceCacheSize)
     : mExpirationTracker(aSurfaceCacheExpirationTimeMS)
     , mMemoryPressureObserver(new MemoryPressureObserver)
     , mMutex("SurfaceCache")
     , mDiscardFactor(aSurfaceCacheDiscardFactor)
     , mMaxCost(aSurfaceCacheSize)
     , mAvailableCost(aSurfaceCacheSize)
     , mLockedCost(0)
+    , mOverflowCount(0)
   {
     nsCOMPtr<nsIObserverService> os = services::GetObserverService();
     if (os) {
       os->AddObserver(mMemoryPressureObserver, "memory-pressure", false);
     }
   }
 
 private:
@@ -425,16 +426,17 @@ public:
     // If this is a duplicate surface, refuse to replace the original.
     if (MOZ_UNLIKELY(Lookup(aImageKey, aSurfaceKey))) {
       return InsertOutcome::FAILURE_ALREADY_PRESENT;
     }
 
     // If this is bigger than we can hold after discarding everything we can,
     // refuse to cache it.
     if (MOZ_UNLIKELY(!CanHoldAfterDiscarding(aCost))) {
+      mOverflowCount++;
       return InsertOutcome::FAILURE;
     }
 
     // Remove elements in order of cost until we can fit this in the cache. Note
     // that locked surfaces aren't in mCosts, so we never remove them here.
     while (aCost > mAvailableCost) {
       MOZ_ASSERT(!mCosts.IsEmpty(),
                  "Removed everything and it still won't fit");
@@ -792,16 +794,24 @@ public:
 
     rv = MOZ_COLLECT_REPORT("imagelib-surface-cache-estimated-locked",
                             KIND_OTHER, UNITS_BYTES,
                             mLockedCost,
                             "Estimated memory used by locked surfaces in the "
                             "imagelib surface cache.");
     NS_ENSURE_SUCCESS(rv, rv);
 
+    rv = MOZ_COLLECT_REPORT("imagelib-surface-cache-overflow-count",
+                            KIND_OTHER, UNITS_COUNT,
+                            mOverflowCount,
+                            "Count of how many times the surface cache has hit "
+                            "its capacity and been unable to insert a new "
+                            "surface.");
+    NS_ENSURE_SUCCESS(rv, rv);
+
     return NS_OK;
   }
 
   void CollectSizeOfSurfaces(const ImageKey                  aImageKey,
                              nsTArray<SurfaceMemoryCounter>& aCounters,
                              MallocSizeOf                    aMallocSizeOf)
   {
     nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
@@ -881,16 +891,17 @@ private:
     ImageSurfaceCache> mImageCaches;
   SurfaceTracker                          mExpirationTracker;
   nsRefPtr<MemoryPressureObserver>        mMemoryPressureObserver;
   Mutex                                   mMutex;
   const uint32_t                          mDiscardFactor;
   const Cost                              mMaxCost;
   Cost                                    mAvailableCost;
   Cost                                    mLockedCost;
+  size_t                                  mOverflowCount;
 };
 
 NS_IMPL_ISUPPORTS(SurfaceCacheImpl, nsIMemoryReporter)
 NS_IMPL_ISUPPORTS(SurfaceCacheImpl::MemoryPressureObserver, nsIObserver)
 
 ///////////////////////////////////////////////////////////////////////////////
 // Public API
 ///////////////////////////////////////////////////////////////////////////////
copy from intl/uconv/tests/unit/test_encode_gbk.js
copy to intl/uconv/tests/unit/test_encode_gb18030.js
--- a/intl/uconv/tests/unit/test_encode_gbk.js
+++ b/intl/uconv/tests/unit/test_encode_gb18030.js
@@ -1,15 +1,15 @@
-// Tests conversion from Unicode to gbk
+// Tests conversion from Unicode to gb18030
 // This is a sniff test which doesn't cover the full gbk range: the test string
 // includes only the ASCII range and the first 63 double byte characters
 
 load('CharsetConversionTests.js');
 	
-const inString = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u4E02\u4E04\u4E05\u4E06\u4E0F\u4E12\u4E17\u4E1F\u4E20\u4E21\u4E23\u4E26\u4E29\u4E2E\u4E2F\u4E31\u4E33\u4E35\u4E37\u4E3C\u4E40\u4E41\u4E42\u4E44\u4E46\u4E4A\u4E51\u4E55\u4E57\u4E5A\u4E5B\u4E62\u4E63\u4E64\u4E65\u4E67\u4E68\u4E6A\u4E6B\u4E6C\u4E6D\u4E6E\u4E6F\u4E72\u4E74\u4E75\u4E76\u4E77\u4E78\u4E79\u4E7A\u4E7B\u4E7C\u4E7D\u4E7F\u4E80\u4E81\u4E82\u4E83\u4E84\u4E85\u4E87\u4E8A";
+const inString = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u20AC\u4E02\u4E04\u4E05\u4E06\u4E0F\u4E12\u4E17\u4E1F\u4E20\u4E21\u4E23\u4E26\u4E29\u4E2E\u4E2F\u4E31\u4E33\u4E35\u4E37\u4E3C\u4E40\u4E41\u4E42\u4E44\u4E46\u4E4A\u4E51\u4E55\u4E57\u4E5A\u4E5B\u4E62\u4E63\u4E64\u4E65\u4E67\u4E68\u4E6A\u4E6B\u4E6C\u4E6D\u4E6E\u4E6F\u4E72\u4E74\u4E75\u4E76\u4E77\u4E78\u4E79\u4E7A\u4E7B\u4E7C\u4E7D\u4E7F\u4E80\u4E81\u4E82\u4E83\u4E84\u4E85\u4E87\u4E8A\uFFFD";
 
-const expectedString = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x81@\x81A\x81B\x81C\x81D\x81E\x81F\x81G\x81H\x81I\x81J\x81K\x81L\x81M\x81N\x81O\x81P\x81Q\x81R\x81S\x81T\x81U\x81V\x81W\x81X\x81Y\x81Z\x81[\x81\\\x81]\x81^\x81_\x81`\x81a\x81b\x81c\x81d\x81e\x81f\x81g\x81h\x81i\x81j\x81k\x81l\x81m\x81n\x81o\x81p\x81q\x81r\x81s\x81t\x81u\x81v\x81w\x81x\x81y\x81z\x81{\x81|\x81}\x81~";
+const expectedString = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\xa2\xe3\x81@\x81A\x81B\x81C\x81D\x81E\x81F\x81G\x81H\x81I\x81J\x81K\x81L\x81M\x81N\x81O\x81P\x81Q\x81R\x81S\x81T\x81U\x81V\x81W\x81X\x81Y\x81Z\x81[\x81\\\x81]\x81^\x81_\x81`\x81a\x81b\x81c\x81d\x81e\x81f\x81g\x81h\x81i\x81j\x81k\x81l\x81m\x81n\x81o\x81p\x81q\x81r\x81s\x81t\x81u\x81v\x81w\x81x\x81y\x81z\x81{\x81|\x81}\x81~\x84\x31\xa4\x37";
     
-const aliases = [ "gbk", "x-gbk" ];
+const aliases = [ "gb18030" ];
 
 function run_test() {
   testEncodeAliases();
 }
--- a/intl/uconv/tests/unit/test_encode_gbk.js
+++ b/intl/uconv/tests/unit/test_encode_gbk.js
@@ -1,15 +1,15 @@
 // Tests conversion from Unicode to gbk
 // This is a sniff test which doesn't cover the full gbk range: the test string
 // includes only the ASCII range and the first 63 double byte characters
 
 load('CharsetConversionTests.js');
 	
-const inString = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u4E02\u4E04\u4E05\u4E06\u4E0F\u4E12\u4E17\u4E1F\u4E20\u4E21\u4E23\u4E26\u4E29\u4E2E\u4E2F\u4E31\u4E33\u4E35\u4E37\u4E3C\u4E40\u4E41\u4E42\u4E44\u4E46\u4E4A\u4E51\u4E55\u4E57\u4E5A\u4E5B\u4E62\u4E63\u4E64\u4E65\u4E67\u4E68\u4E6A\u4E6B\u4E6C\u4E6D\u4E6E\u4E6F\u4E72\u4E74\u4E75\u4E76\u4E77\u4E78\u4E79\u4E7A\u4E7B\u4E7C\u4E7D\u4E7F\u4E80\u4E81\u4E82\u4E83\u4E84\u4E85\u4E87\u4E8A";
+const inString = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u20AC\u4E02\u4E04\u4E05\u4E06\u4E0F\u4E12\u4E17\u4E1F\u4E20\u4E21\u4E23\u4E26\u4E29\u4E2E\u4E2F\u4E31\u4E33\u4E35\u4E37\u4E3C\u4E40\u4E41\u4E42\u4E44\u4E46\u4E4A\u4E51\u4E55\u4E57\u4E5A\u4E5B\u4E62\u4E63\u4E64\u4E65\u4E67\u4E68\u4E6A\u4E6B\u4E6C\u4E6D\u4E6E\u4E6F\u4E72\u4E74\u4E75\u4E76\u4E77\u4E78\u4E79\u4E7A\u4E7B\u4E7C\u4E7D\u4E7F\u4E80\u4E81\u4E82\u4E83\u4E84\u4E85\u4E87\u4E8A\uFFFD";
 
-const expectedString = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x81@\x81A\x81B\x81C\x81D\x81E\x81F\x81G\x81H\x81I\x81J\x81K\x81L\x81M\x81N\x81O\x81P\x81Q\x81R\x81S\x81T\x81U\x81V\x81W\x81X\x81Y\x81Z\x81[\x81\\\x81]\x81^\x81_\x81`\x81a\x81b\x81c\x81d\x81e\x81f\x81g\x81h\x81i\x81j\x81k\x81l\x81m\x81n\x81o\x81p\x81q\x81r\x81s\x81t\x81u\x81v\x81w\x81x\x81y\x81z\x81{\x81|\x81}\x81~";
+const expectedString = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x80\x81@\x81A\x81B\x81C\x81D\x81E\x81F\x81G\x81H\x81I\x81J\x81K\x81L\x81M\x81N\x81O\x81P\x81Q\x81R\x81S\x81T\x81U\x81V\x81W\x81X\x81Y\x81Z\x81[\x81\\\x81]\x81^\x81_\x81`\x81a\x81b\x81c\x81d\x81e\x81f\x81g\x81h\x81i\x81j\x81k\x81l\x81m\x81n\x81o\x81p\x81q\x81r\x81s\x81t\x81u\x81v\x81w\x81x\x81y\x81z\x81{\x81|\x81}\x81~";
     
 const aliases = [ "gbk", "x-gbk" ];
 
 function run_test() {
   testEncodeAliases();
 }
--- a/intl/uconv/tests/unit/xpcshell.ini
+++ b/intl/uconv/tests/unit/xpcshell.ini
@@ -96,16 +96,17 @@ support-files =
 [test_encode_CP1252.js]
 [test_encode_CP1253.js]
 [test_encode_CP1254.js]
 [test_encode_CP1255.js]
 [test_encode_CP1256.js]
 [test_encode_CP1257.js]
 [test_encode_CP1258.js]
 [test_encode_CP874.js]
+[test_encode_gb18030.js]
 [test_encode_gbk.js]
 [test_encode_x_mac_arabic.js]
 [test_encode_x_mac_ce.js]
 [test_encode_x_mac_croatian.js]
 [test_encode_x_mac_cyrillic.js]
 [test_encode_x_mac_devanagari.js]
 [test_encode_x_mac_farsi.js]
 [test_encode_x_mac_greek.js]
--- a/intl/uconv/tools/gengb18030tables.pl
+++ b/intl/uconv/tools/gengb18030tables.pl
@@ -196,21 +196,21 @@ sub printgb18030table()
 sub genufut()
 {
  print ( "umaptable -uf < gb18030uniq.txt > gb18030uniq2b.uf\n");
  system( "umaptable -uf < gb18030uniq.txt > gb18030uniq2b.uf");
 
  print ( "umaptable -ut < gb18030uniq.txt > gb18030uniq2b.ut\n");
  system( "umaptable -ut < gb18030uniq.txt > gb18030uniq2b.ut");
 
- print ( "umaptable -uf < cp936uniq.txt > gbkuniq2b.uf\n") ;
- system( "umaptable -uf < cp936uniq.txt > gbkuniq2b.uf") ;
+ print ( "umaptable -uf < cp936uniq.txt > gbkuniq.uf\n") ;
+ system( "umaptable -uf < cp936uniq.txt > gbkuniq.uf") ;
 
- print ( "umaptable -ut < cp936uniq.txt > gbkuniq2b.ut\n") ;
- system( "umaptable -ut < cp936uniq.txt > gbkuniq2b.ut") ;
+ print ( "umaptable -ut < cp936uniq.txt > gbkuniq.ut\n") ;
+ system( "umaptable -ut < cp936uniq.txt > gbkuniq.ut") ;
 
  print ( "umaptable -uf < gb180304b.txt > gb180304bytes.uf\n")  ;
  system( "umaptable -uf < gb180304b.txt > gb180304bytes.uf")  ;
 
  print ( "umaptable -ut < gb180304b.txt > gb180304bytes.ut\n")  ;
  system( "umaptable -ut < gb180304b.txt > gb180304bytes.ut")  ;
 
  print ( "perl cp936tocdx.pl > cp936map.h\n");
rename from intl/uconv/ucvcn/gbkuniq2b.uf
rename to intl/uconv/ucvcn/gbkuniq.uf
--- a/intl/uconv/ucvcn/nsGBKConvUtil.cpp
+++ b/intl/uconv/ucvcn/nsGBKConvUtil.cpp
@@ -35,16 +35,18 @@ bool nsGBKConvUtil::UnicodeToGBKChar(
     if(item != 0) 
     {
       *aOutByte1 = item >> 8;
       *aOutByte2 = item & 0x00FF;
       found = true;
     } else {
       return false;
     }
+  } else if (aChar == UCS2_NO_MAPPING) {
+    return false;
   } else {
     // ugly linear search
     for( int32_t i = 0; i < MAX_GBK_LENGTH; i++ )
     {
       if( aChar == gGBKToUnicodeTable[i])
       {
         *aOutByte1 = (i /  0x00BF + 0x0081) ;
         *aOutByte2 = (i %  0x00BF + 0x0040) ;
--- a/intl/uconv/ucvcn/nsUnicodeToGBK.cpp
+++ b/intl/uconv/ucvcn/nsUnicodeToGBK.cpp
@@ -49,27 +49,27 @@ class nsUnicodeTo4BytesGB18030 : public 
 public: 
   nsUnicodeTo4BytesGB18030()
     : nsTableEncoderSupport(u4BytesGB18030Charset, 
                              (uMappingTable*) &g_uf_gb18030_4bytes, 4) {}
 protected: 
 };
 //-----------------------------------------------------------------------
 //  Private class used by nsUnicodeToGBK
-//    nsUnicodeToGBKUniq2Bytes
+//    nsUnicodeToGBKUniq
 //-----------------------------------------------------------------------
-static const uint16_t g_uf_gbk_2bytes[] = {
-#include "gbkuniq2b.uf"
+static const uint16_t g_uf_gbk[] = {
+#include "gbkuniq.uf"
 };
-class nsUnicodeToGBKUniq2Bytes : public nsTableEncoderSupport
+class nsUnicodeToGBKUniq : public nsTableEncoderSupport
 {
 public: 
-  nsUnicodeToGBKUniq2Bytes()
-    : nsTableEncoderSupport(u2BytesCharset, 
-                             (uMappingTable*) &g_uf_gbk_2bytes, 2) {}
+  nsUnicodeToGBKUniq()
+    : nsTableEncoderSupport(u1ByteCharset,
+                             (uMappingTable*) &g_uf_gbk, 1) {}
 protected: 
 };
 //-----------------------------------------------------------------------
 //  nsUnicodeToGB18030
 //-----------------------------------------------------------------------
 void nsUnicodeToGB18030::CreateExtensionEncoder()
 {
   mExtensionEncoder = new nsUnicodeToGB18030Uniq2Bytes();
@@ -111,17 +111,17 @@ nsUnicodeToGBK::nsUnicodeToGBK(uint32_t 
   nsEncoderSupport(aMaxLength)
 {
   mExtensionEncoder = nullptr;
   m4BytesEncoder = nullptr;
   mSurrogateHigh = 0;
 }
 void nsUnicodeToGBK::CreateExtensionEncoder()
 {
-  mExtensionEncoder = new nsUnicodeToGBKUniq2Bytes();
+  mExtensionEncoder = new nsUnicodeToGBKUniq();
 }
 void nsUnicodeToGBK::Create4BytesEncoder()
 {
   m4BytesEncoder = nullptr;
 }
 bool nsUnicodeToGBK::TryExtensionEncoder(
   char16_t aChar,
   char* aOut,
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -137,16 +137,18 @@ class ObjectOpResult
     JS_PUBLIC_API(bool) failGetterOnly();
     JS_PUBLIC_API(bool) failCantDelete();
 
     JS_PUBLIC_API(bool) failCantSetInterposed();
     JS_PUBLIC_API(bool) failCantDefineWindowElement();
     JS_PUBLIC_API(bool) failCantDeleteWindowElement();
     JS_PUBLIC_API(bool) failCantDeleteWindowNamedProperty();
     JS_PUBLIC_API(bool) failCantPreventExtensions();
+    JS_PUBLIC_API(bool) failNoNamedSetter();
+    JS_PUBLIC_API(bool) failNoIndexedSetter();
 
     uint32_t failureCode() const {
         MOZ_ASSERT(!ok());
         return uint32_t(code_);
     }
 
     /*
      * Report an error or warning if necessary; return true to proceed and
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -838,20 +838,16 @@ HasChild(JSContext* cx, unsigned argc, j
     }
 
     HasChildTracer trc(cx->runtime(), child);
     TraceChildren(&trc, parent.toGCThing(), parent.traceKind());
     args.rval().setBoolean(trc.found());
     return true;
 }
 
-// Stolen from jsmath.cpp
-static const uint64_t RNG_MULTIPLIER = 0x5DEECE66DLL;
-static const uint64_t RNG_MASK = (1LL << 48) - 1;
-
 static bool
 SetSavedStacksRNGState(JSContext* cx, unsigned argc, jsval* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!args.requireAtLeast(cx, "setSavedStacksRNGState", 1))
         return false;
 
     int32_t seed;
--- a/js/src/devtools/automation/autospider.sh
+++ b/js/src/devtools/automation/autospider.sh
@@ -86,18 +86,36 @@ elif [ "$OSTYPE" = "linux-gnu" ]; then
   if [ -n "$AUTOMATION" ]; then
       GCCDIR="${GCCDIR:-/tools/gcc-4.7.2-0moz1}"
       CONFIGURE_ARGS="$CONFIGURE_ARGS --with-ccache"
   fi
   UNAME_M=$(uname -m)
   MAKEFLAGS=-j4
   if [ "$VARIANT" = "arm-sim" ]; then
     USE_64BIT=false
-  elif [ "$UNAME_M" = "x86_64" ]; then
-    USE_64BIT=true
+  else
+    case "$platform" in
+    linux64)
+      USE_64BIT=true
+      ;;
+    linux64-debug)
+      USE_64BIT=true
+      ;;
+    linux)
+      USE_64BIT=false
+      ;;
+    linux-debug)
+      USE_64BIT=false
+      ;;
+    *)
+      if [ "$UNAME_M" = "x86_64" ]; then
+        USE_64BIT=true
+      fi
+      ;;
+    esac
   fi
 
   if [ "$UNAME_M" != "arm" ] && [ -n "$AUTOMATION" ]; then
     export CC=$GCCDIR/bin/gcc
     export CXX=$GCCDIR/bin/g++
     if $USE_64BIT; then
       export LD_LIBRARY_PATH=$GCCDIR/lib64
     else
@@ -126,16 +144,21 @@ if $USE_64BIT; then
   fi
 else
   NSPR64=""
   if [ "$OSTYPE" != "msys" ]; then
     export CC="${CC:-/usr/bin/gcc} -m32"
     export CXX="${CXX:-/usr/bin/g++} -m32"
     export AR=ar
   fi
+  if [ "$OSTYPE" = "linux-gnu" ]; then
+    if [ "$UNAME_M" != "arm" ] && [ -n "$AUTOMATION" ]; then
+      CONFIGURE_ARGS="$CONFIGURE_ARGS --target=i686-pc-linux --host=i686-pc-linux"
+    fi
+  fi
 fi
 
 $SOURCE/js/src/configure $CONFIGURE_ARGS --enable-nspr-build --prefix=$OBJDIR/dist || exit 2
 $MAKE -s -w -j4 || exit 2
 cp -p $SOURCE/build/unix/run-mozilla.sh $OBJDIR/dist/bin
 
 COMMAND_PREFIX=''
 
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1993,42 +1993,38 @@ BytecodeEmitter::checkSideEffects(ParseN
          * We can't easily prove that neither operand ever denotes an
          * object with a toString or valueOf method.
          */
         *answer = true;
         return true;
 
       case PN_UNARY:
         switch (pn->getKind()) {
-          case PNK_DELETE:
-          {
-            ParseNode* pn2 = pn->pn_kid;
-            switch (pn2->getKind()) {
-              case PNK_NAME:
-                if (!bindNameToSlot(pn2))
-                    return false;
-                if (pn2->isConst()) {
-                    MOZ_ASSERT(*answer == false);
-                    return true;
-                }
-                /* FALL THROUGH */
-              case PNK_DOT:
-              case PNK_CALL:
-              case PNK_ELEM:
-              case PNK_SUPERELEM:
-                /* All these delete addressing modes have effects too. */
-                *answer = true;
-                return true;
-              default:
-                return checkSideEffects(pn2, answer);
-            }
-            MOZ_CRASH("We have a returning default case");
+          case PNK_DELETENAME: {
+            ParseNode* nameExpr = pn->pn_kid;
+            MOZ_ASSERT(nameExpr->isKind(PNK_NAME));
+            if (!bindNameToSlot(nameExpr))
+                return false;
+            *answer = !nameExpr->isConst();
+            return true;
           }
 
-          case PNK_TYPEOF:
+          case PNK_DELETEPROP:
+          case PNK_DELETESUPERPROP:
+          case PNK_DELETEELEM:
+          case PNK_DELETESUPERELEM:
+            // All these delete addressing modes have effects, too.
+            *answer = true;
+            return true;
+
+          case PNK_DELETEEXPR:
+            return checkSideEffects(pn->pn_kid, answer);
+
+          case PNK_TYPEOFNAME:
+          case PNK_TYPEOFEXPR:
           case PNK_VOID:
           case PNK_NOT:
           case PNK_BITNOT:
             if (pn->isOp(JSOP_NOT)) {
                 /* ! does not convert its operand via toString or valueOf. */
                 return checkSideEffects(pn->pn_kid, answer);
             }
             /* FALL THROUGH */
@@ -6152,92 +6148,120 @@ BytecodeEmitter::emitStatement(ParseNode
                 return false;
         }
     }
 
     return true;
 }
 
 bool
-BytecodeEmitter::emitDelete(ParseNode* pn)
-{
-    /*
-     * Under ECMA 3, deleting a non-reference returns true -- but alas we
-     * must evaluate the operand if it appears it might have side effects.
-     */
-    ParseNode* pn2 = pn->pn_kid;
-    switch (pn2->getKind()) {
-      case PNK_NAME:
-        if (!bindNameToSlot(pn2))
-            return false;
-        if (!emitAtomOp(pn2, pn2->getOp()))
-            return false;
-        break;
-      case PNK_DOT:
-      {
-        JSOp delOp = sc->strict() ? JSOP_STRICTDELPROP : JSOP_DELPROP;
-        if (!emitPropOp(pn2, delOp))
-            return false;
-        break;
-      }
-      case PNK_SUPERPROP:
-        // Still have to calculate the base, even though we are are going
-        // to throw unconditionally, as calculating the base could also
-        // throw.
-        if (!emit1(JSOP_SUPERBASE))
-            return false;
-        if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER))
-            return false;
-        break;
-      case PNK_ELEM:
-      {
-        JSOp delOp = sc->strict() ? JSOP_STRICTDELELEM : JSOP_DELELEM;
-        if (!emitElemOp(pn2, delOp))
-            return false;
-        break;
-      }
-      case PNK_SUPERELEM:
-        // Still have to calculate everything, even though we're gonna throw
-        // since it may have side effects
-        if (!emitTree(pn2->pn_kid))
-            return false;
-        if (!emit1(JSOP_SUPERBASE))
-            return false;
-        if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER))
-            return false;
-
-        // Another wrinkle: Balance the stack from the emitter's point of view.
-        // Execution will not reach here, as the last bytecode threw.
+BytecodeEmitter::emitDeleteName(ParseNode* node)
+{
+    MOZ_ASSERT(node->isKind(PNK_DELETENAME));
+    MOZ_ASSERT(node->isArity(PN_UNARY));
+
+    ParseNode* nameExpr = node->pn_kid;
+    MOZ_ASSERT(nameExpr->isKind(PNK_NAME));
+
+    if (!bindNameToSlot(nameExpr))
+        return false;
+
+    MOZ_ASSERT(nameExpr->isOp(JSOP_DELNAME));
+    return emitAtomOp(nameExpr, JSOP_DELNAME);
+}
+
+bool
+BytecodeEmitter::emitDeleteProperty(ParseNode* node)
+{
+    MOZ_ASSERT(node->isKind(PNK_DELETEPROP));
+    MOZ_ASSERT(node->isArity(PN_UNARY));
+
+    ParseNode* propExpr = node->pn_kid;
+    MOZ_ASSERT(propExpr->isKind(PNK_DOT));
+
+    JSOp delOp = sc->strict() ? JSOP_STRICTDELPROP : JSOP_DELPROP;
+    return emitPropOp(propExpr, delOp);
+}
+
+bool
+BytecodeEmitter::emitDeleteSuperProperty(ParseNode* node)
+{
+    MOZ_ASSERT(node->isKind(PNK_DELETESUPERPROP));
+    MOZ_ASSERT(node->isArity(PN_UNARY));
+    MOZ_ASSERT(node->pn_kid->isKind(PNK_SUPERPROP));
+
+    // Still have to calculate the base, even though we are are going
+    // to throw unconditionally, as calculating the base could also
+    // throw.
+    if (!emit1(JSOP_SUPERBASE))
+        return false;
+
+    return emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER);
+}
+
+bool
+BytecodeEmitter::emitDeleteElement(ParseNode* node)
+{
+    MOZ_ASSERT(node->isKind(PNK_DELETEELEM));
+    MOZ_ASSERT(node->isArity(PN_UNARY));
+
+    ParseNode* elemExpr = node->pn_kid;
+    MOZ_ASSERT(elemExpr->isKind(PNK_ELEM));
+
+    JSOp delOp = sc->strict() ? JSOP_STRICTDELELEM : JSOP_DELELEM;
+    return emitElemOp(elemExpr, delOp);
+}
+
+bool
+BytecodeEmitter::emitDeleteSuperElement(ParseNode* node)
+{
+    MOZ_ASSERT(node->isKind(PNK_DELETESUPERELEM));
+    MOZ_ASSERT(node->isArity(PN_UNARY));
+
+    ParseNode* superElemExpr = node->pn_kid;
+    MOZ_ASSERT(superElemExpr->isKind(PNK_SUPERELEM));
+
+    // Still have to calculate everything, even though we're gonna throw
+    // since it may have side effects
+    MOZ_ASSERT(superElemExpr->isArity(PN_UNARY));
+    if (!emitTree(superElemExpr->pn_kid))
+        return false;
+    if (!emit1(JSOP_SUPERBASE))
+        return false;
+    if (!emitUint16Operand(JSOP_THROWMSG, JSMSG_CANT_DELETE_SUPER))
+        return false;
+
+    // Another wrinkle: Balance the stack from the emitter's point of view.
+    // Execution will not reach here, as the last bytecode threw.
+    return emit1(JSOP_POP);
+}
+
+bool
+BytecodeEmitter::emitDeleteExpression(ParseNode* node)
+{
+    MOZ_ASSERT(node->isKind(PNK_DELETEEXPR));
+    MOZ_ASSERT(node->isArity(PN_UNARY));
+
+    ParseNode* expression = node->pn_kid;
+
+    // If useless, just emit JSOP_TRUE; otherwise convert |delete <expr>| to
+    // effectively |<expr>, true|.
+    bool useful = false;
+    if (!checkSideEffects(expression, &useful))
+        return false;
+
+    if (useful) {
+        MOZ_ASSERT_IF(expression->isKind(PNK_CALL), !(expression->pn_xflags & PNX_SETCALL));
+        if (!emitTree(expression))
+            return false;
         if (!emit1(JSOP_POP))
             return false;
-        break;
-      default:
-      {
-        /*
-         * If useless, just emit JSOP_TRUE; otherwise convert delete foo()
-         * to foo(), true (a comma expression).
-         */
-        bool useful = false;
-        if (!checkSideEffects(pn2, &useful))
-            return false;
-
-        if (useful) {
-            MOZ_ASSERT_IF(pn2->isKind(PNK_CALL), !(pn2->pn_xflags & PNX_SETCALL));
-            if (!emitTree(pn2))
-                return false;
-            if (!emit1(JSOP_POP))
-                return false;
-        }
-
-        if (!emit1(JSOP_TRUE))
-            return false;
-      }
-    }
-
-    return true;
+    }
+
+    return emit1(JSOP_TRUE);
 }
 
 bool
 BytecodeEmitter::emitSelfHostedCallFunction(ParseNode* pn)
 {
     // Special-casing of callFunction to emit bytecode that directly
     // invokes the callee with the correct |this| object and arguments.
     // callFunction(fun, thisArg, arg0, arg1) thus becomes:
@@ -6959,22 +6983,36 @@ BytecodeEmitter::emitUnary(ParseNode* pn
 {
     if (!updateSourceCoordNotes(pn->pn_pos.begin))
         return false;
 
     /* Unary op, including unary +/-. */
     JSOp op = pn->getOp();
     ParseNode* pn2 = pn->pn_kid;
 
-    if (op == JSOP_TYPEOF && !pn2->isKind(PNK_NAME))
-        op = JSOP_TYPEOFEXPR;
+    bool oldEmittingForInit = emittingForInit;
+    emittingForInit = false;
+    if (!emitTree(pn2))
+        return false;
+
+    emittingForInit = oldEmittingForInit;
+    return emit1(op);
+}
+
+bool
+BytecodeEmitter::emitTypeof(ParseNode* node, JSOp op)
+{
+    MOZ_ASSERT(op == JSOP_TYPEOF || op == JSOP_TYPEOFEXPR);
+
+    if (!updateSourceCoordNotes(node->pn_pos.begin))
+        return false;
 
     bool oldEmittingForInit = emittingForInit;
     emittingForInit = false;
-    if (!emitTree(pn2))
+    if (!emitTree(node->pn_kid))
         return false;
 
     emittingForInit = oldEmittingForInit;
     return emit1(op);
 }
 
 bool
 BytecodeEmitter::emitDefaultsAndDestructuring(ParseNode* pn)
@@ -7439,35 +7477,62 @@ BytecodeEmitter::emitTree(ParseNode* pn)
             if (!emitTree(subexpr))
                 return false;
             if (!emit1(op))
                 return false;
         }
         break;
       }
 
+      case PNK_TYPEOFNAME:
+        ok = emitTypeof(pn, JSOP_TYPEOF);
+        break;
+
+      case PNK_TYPEOFEXPR:
+        ok = emitTypeof(pn, JSOP_TYPEOFEXPR);
+        break;
+
       case PNK_THROW:
-      case PNK_TYPEOF:
       case PNK_VOID:
       case PNK_NOT:
       case PNK_BITNOT:
       case PNK_POS:
       case PNK_NEG:
         ok = emitUnary(pn);
         break;
 
       case PNK_PREINCREMENT:
       case PNK_PREDECREMENT:
       case PNK_POSTINCREMENT:
       case PNK_POSTDECREMENT:
         ok = emitIncOrDec(pn);
         break;
 
-      case PNK_DELETE:
-        ok = emitDelete(pn);
+      case PNK_DELETENAME:
+        ok = emitDeleteName(pn);
+        break;
+
+      case PNK_DELETEPROP:
+        ok = emitDeleteProperty(pn);
+        break;
+
+      case PNK_DELETESUPERPROP:
+        ok = emitDeleteSuperProperty(pn);
+        break;
+
+      case PNK_DELETEELEM:
+        ok = emitDeleteElement(pn);
+        break;
+
+      case PNK_DELETESUPERELEM:
+        ok = emitDeleteSuperElement(pn);
+        break;
+
+      case PNK_DELETEEXPR:
+        ok = emitDeleteExpression(pn);
         break;
 
       case PNK_DOT:
         ok = emitPropOp(pn, JSOP_GETPROP);
         break;
 
       case PNK_SUPERPROP:
         if (!emitSuperPropOp(pn, JSOP_GETPROP_SUPER))
@@ -7500,16 +7565,17 @@ BytecodeEmitter::emitTree(ParseNode* pn)
 
       case PNK_CONST:
       case PNK_LET:
         ok = emitVariables(pn, InitializeVars);
         break;
 
       case PNK_IMPORT:
       case PNK_EXPORT:
+      case PNK_EXPORT_DEFAULT:
       case PNK_EXPORT_FROM:
        // TODO: Implement emitter support for modules
        reportError(nullptr, JSMSG_MODULES_NOT_IMPLEMENTED);
        return false;
 
       case PNK_ARRAYPUSH: {
         /*
          * The array object's stack index is in arrayCompDepth. See below
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -540,17 +540,26 @@ struct BytecodeEmitter
     bool emitCallSiteObject(ParseNode* pn);
     bool emitTemplateString(ParseNode* pn);
     bool emitAssignment(ParseNode* lhs, JSOp op, ParseNode* rhs);
 
     bool emitReturn(ParseNode* pn);
     bool emitStatement(ParseNode* pn);
     bool emitStatementList(ParseNode* pn, ptrdiff_t top);
 
-    bool emitDelete(ParseNode* pn);
+    bool emitDeleteName(ParseNode* pn);
+    bool emitDeleteProperty(ParseNode* pn);
+    bool emitDeleteSuperProperty(ParseNode* pn);
+    bool emitDeleteElement(ParseNode* pn);
+    bool emitDeleteSuperElement(ParseNode* pn);
+    bool emitDeleteExpression(ParseNode* pn);
+
+    // |op| must be JSOP_TYPEOF or JSOP_TYPEOFEXPR.
+    bool emitTypeof(ParseNode* node, JSOp op);
+
     bool emitLogical(ParseNode* pn);
     bool emitUnary(ParseNode* pn);
 
     MOZ_NEVER_INLINE bool emitIncOrDec(ParseNode* pn);
 
     bool emitConditionalExpression(ConditionalExpression& conditional);
 
     bool emitCallOrNew(ParseNode* pn);
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -316,21 +316,27 @@ ContainsHoistedDeclaration(ExclusiveCont
       // method, because some parent component should have asserted itself.
       case PNK_OBJECT_PROPERTY_NAME:
       case PNK_COMPUTED_NAME:
       case PNK_SPREAD:
       case PNK_MUTATEPROTO:
       case PNK_COLON:
       case PNK_SHORTHAND:
       case PNK_CONDITIONAL:
-      case PNK_TYPEOF:
+      case PNK_TYPEOFNAME:
+      case PNK_TYPEOFEXPR:
       case PNK_VOID:
       case PNK_NOT:
       case PNK_BITNOT:
-      case PNK_DELETE:
+      case PNK_DELETENAME:
+      case PNK_DELETEPROP:
+      case PNK_DELETESUPERPROP:
+      case PNK_DELETEELEM:
+      case PNK_DELETESUPERELEM:
+      case PNK_DELETEEXPR:
       case PNK_POS:
       case PNK_NEG:
       case PNK_PREINCREMENT:
       case PNK_POSTINCREMENT:
       case PNK_PREDECREMENT:
       case PNK_POSTDECREMENT:
       case PNK_OR:
       case PNK_AND:
@@ -695,33 +701,21 @@ Fold(ExclusiveContext* cx, ParseNode** p
                     return false;
             }
         }
         pn1 = pn->pn_left;
         pn2 = pn->pn_right;
         break;
 
       case PN_UNARY:
-        /*
-         * Kludge to deal with typeof expressions: because constant folding
-         * can turn an expression into a name node, we have to check here,
-         * before folding, to see if we should throw undefined name errors.
-         *
-         * NB: We know that if pn->pn_op is JSOP_TYPEOF, pn1 will not be
-         * null. This assumption does not hold true for other unary
-         * expressions.
-         */
-        if (pn->isKind(PNK_TYPEOF) && !pn->pn_kid->isKind(PNK_NAME))
-            pn->setOp(JSOP_TYPEOFEXPR);
-
         if (pn->pn_kid) {
             SyntacticContext kidsc =
                 pn->isKind(PNK_NOT)
                 ? SyntacticContext::Condition
-                : pn->isKind(PNK_DELETE)
+                : IsDeleteKind(pn->getKind())
                 ? SyntacticContext::Delete
                 : SyntacticContext::Other;
             if (!Fold(cx, &pn->pn_kid, handler, options, inGenexpLambda, kidsc))
                 return false;
         }
         pn1 = pn->pn_kid;
         break;
 
@@ -741,17 +735,17 @@ Fold(ExclusiveContext* cx, ParseNode** p
             pn1 = *lhsp;
         }
         break;
 
       case PN_NULLARY:
         break;
     }
 
-    // The immediate child of a PNK_DELETE node should not be replaced
+    // The immediate child of a PNK_DELETE* node should not be replaced
     // with node indicating a different syntactic form; |delete x| is not
     // the same as |delete (true && x)|. See bug 888002.
     //
     // pn is the immediate child in question. Its descendants were already
     // constant-folded above, so we're done.
     if (sc == SyntacticContext::Delete)
         return true;
 
@@ -991,17 +985,18 @@ Fold(ExclusiveContext* cx, ParseNode** p
             while ((pn2 = pn3) != nullptr) {
                 pn3 = pn2->pn_next;
                 if (!FoldBinaryNumeric(cx, op, pn, pn2, pn))
                     return false;
             }
         }
         break;
 
-      case PNK_TYPEOF:
+      case PNK_TYPEOFNAME:
+      case PNK_TYPEOFEXPR:
       case PNK_VOID:
       case PNK_NOT:
       case PNK_BITNOT:
       case PNK_POS:
       case PNK_NEG:
         if (pn1->isKind(PNK_NUMBER)) {
             double d;
 
@@ -1095,17 +1090,17 @@ Fold(ExclusiveContext* cx, ParseNode** p
 
             // Supposing we're replacing |obj["prop"]| with |obj.prop|, we now
             // can free the |"prop"| node and |obj["prop"]| nodes -- but not
             // the |obj| node now a sub-node of |expr|.  Mutate |pn| into a
             // node with |"prop"| as its child so that our |pn| doesn't have a
             // necessarily-weird structure (say, by nulling out |pn->pn_left|
             // only) that would fail AST sanity assertions performed by
             // |handler.freeTree(pn)|.
-            pn->setKind(PNK_TYPEOF);
+            pn->setKind(PNK_TYPEOFEXPR);
             pn->setArity(PN_UNARY);
             pn->pn_kid = pn2;
             handler.freeTree(pn);
 
             pn = expr;
         }
         break;
       }
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -67,33 +67,50 @@ class FullParseHandler
     Parser<SyntaxParseHandler>* syntaxParser;
 
     /* new_ methods for creating parse nodes. These report OOM on context. */
     JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
 
     typedef ParseNode* Node;
     typedef Definition* DefinitionNode;
 
+    bool isPropertyAccess(ParseNode* node) {
+        if (node->isKind(PNK_DOT) || node->isKind(PNK_ELEM))
+            return true;
+        return node->isKind(PNK_SUPERPROP) || node->isKind(PNK_SUPERELEM);
+    }
+
+    bool isFunctionCall(ParseNode* node) {
+        // Note: super() is a special form, *not* a function call.
+        return node->isKind(PNK_CALL);
+    }
+
+    bool isDestructuringTarget(ParseNode* node) {
+        return node->isKind(PNK_OBJECT) || node->isKind(PNK_ARRAY);
+    }
+
     FullParseHandler(ExclusiveContext* cx, LifoAlloc& alloc,
                      TokenStream& tokenStream, Parser<SyntaxParseHandler>* syntaxParser,
                      LazyScript* lazyOuterFunction)
       : allocator(cx, alloc),
         tokenStream(tokenStream),
         lazyOuterFunction_(lazyOuterFunction),
         lazyInnerFunctionIndex(0),
         syntaxParser(syntaxParser)
     {}
 
     static ParseNode* null() { return nullptr; }
 
     ParseNode* freeTree(ParseNode* pn) { return allocator.freeTree(pn); }
     void prepareNodeForMutation(ParseNode* pn) { return allocator.prepareNodeForMutation(pn); }
     const Token& currentToken() { return tokenStream.currentToken(); }
 
-    ParseNode* newName(PropertyName* name, uint32_t blockid, const TokenPos& pos) {
+    ParseNode* newName(PropertyName* name, uint32_t blockid, const TokenPos& pos,
+                       ExclusiveContext* cx)
+    {
         return new_<NameNode>(PNK_NAME, JSOP_GETNAME, name, blockid, pos);
     }
 
     ParseNode* newComputedName(ParseNode* expr, uint32_t begin, uint32_t end) {
         TokenPos pos(begin, end);
         return new_<UnaryNode>(PNK_COMPUTED_NAME, JSOP_NOP, pos, expr);
     }
 
@@ -182,21 +199,39 @@ class FullParseHandler
         return new_<ConditionalExpression>(cond, thenExpr, elseExpr);
     }
 
     void markAsSetCall(ParseNode* pn) {
         pn->pn_xflags |= PNX_SETCALL;
     }
 
     ParseNode* newDelete(uint32_t begin, ParseNode* expr) {
-        if (expr->getKind() == PNK_NAME) {
+        if (expr->isKind(PNK_NAME)) {
             expr->pn_dflags |= PND_DEOPTIMIZED;
             expr->setOp(JSOP_DELNAME);
+            return newUnary(PNK_DELETENAME, JSOP_NOP, begin, expr);
         }
-        return newUnary(PNK_DELETE, JSOP_NOP, begin, expr);
+
+        if (expr->isKind(PNK_DOT))
+            return newUnary(PNK_DELETEPROP, JSOP_NOP, begin, expr);
+        if (expr->isKind(PNK_SUPERPROP))
+            return newUnary(PNK_DELETESUPERPROP, JSOP_NOP, begin, expr);
+
+        if (expr->isKind(PNK_ELEM))
+            return newUnary(PNK_DELETEELEM, JSOP_NOP, begin, expr);
+        if (expr->isKind(PNK_SUPERELEM))
+            return newUnary(PNK_DELETESUPERELEM, JSOP_NOP, begin, expr);
+
+        return newUnary(PNK_DELETEEXPR, JSOP_NOP, begin, expr);
+    }
+
+    ParseNode* newTypeof(uint32_t begin, ParseNode* kid) {
+        TokenPos pos(begin, kid->pn_pos.end);
+        ParseNodeKind kind = kid->isKind(PNK_NAME) ? PNK_TYPEOFNAME : PNK_TYPEOFEXPR;
+        return new_<UnaryNode>(kind, JSOP_NOP, pos, kid);
     }
 
     ParseNode* newNullary(ParseNodeKind kind, JSOp op, const TokenPos& pos) {
         return new_<NullaryNode>(kind, op, pos);
     }
 
     ParseNode* newUnary(ParseNodeKind kind, JSOp op, uint32_t begin, ParseNode* kid) {
         TokenPos pos(begin, kid ? kid->pn_pos.end : begin + 1);
@@ -271,16 +306,24 @@ class FullParseHandler
     }
 
     void addArrayElement(ParseNode* literal, ParseNode* element) {
         if (!element->isConstant())
             literal->pn_xflags |= PNX_NONCONST;
         literal->append(element);
     }
 
+    ParseNode* newCall() {
+        return newList(PNK_CALL, JSOP_CALL);
+    }
+
+    ParseNode* newTaggedTemplate() {
+        return newList(PNK_TAGGED_TEMPLATE, JSOP_CALL);
+    }
+
     ParseNode* newObjectLiteral(uint32_t begin) {
         ParseNode* literal = new_<ListNode>(PNK_OBJECT, TokenPos(begin, begin + 1));
         // Later in this stack: remove dependency on this opcode.
         if (literal)
             literal->setOp(JSOP_NEWINIT);
         return literal;
     }
 
@@ -722,33 +765,42 @@ class FullParseHandler
     }
     void setPrologue(ParseNode* pn) {
         pn->pn_prologue = true;
     }
 
     bool isConstant(ParseNode* pn) {
         return pn->isConstant();
     }
-    PropertyName* isName(ParseNode* pn) {
+    PropertyName* maybeName(ParseNode* pn) {
         return pn->isKind(PNK_NAME) ? pn->pn_atom->asPropertyName() : nullptr;
     }
     bool isCall(ParseNode* pn) {
         return pn->isKind(PNK_CALL);
     }
-    PropertyName* isGetProp(ParseNode* pn) {
+    PropertyName* maybeDottedProperty(ParseNode* pn) {
         return pn->is<PropertyAccess>() ? &pn->as<PropertyAccess>().name() : nullptr;
     }
     JSAtom* isStringExprStatement(ParseNode* pn, TokenPos* pos) {
         if (JSAtom* atom = pn->isStringExprStatement()) {
             *pos = pn->pn_kid->pn_pos;
             return atom;
         }
         return nullptr;
     }
 
+    void markAsAssigned(ParseNode* node) { node->markAsAssigned(); }
+    void adjustGetToSet(ParseNode* node) {
+        node->setOp(node->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME);
+    }
+    void maybeDespecializeSet(ParseNode* node) {
+        if (!(js_CodeSpec[node->getOp()].format & JOF_SET))
+            node->setOp(JSOP_SETNAME);
+    }
+
     inline ParseNode* makeAssignment(ParseNode* pn, ParseNode* rhs);
 
     static Definition* getDefinitionNode(Definition* dn) {
         return dn;
     }
     static Definition::Kind getDefinitionKind(Definition* dn) {
         return dn->kind();
     }
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -372,23 +372,34 @@ class NameResolver
           case PNK_DEBUGGER:
           case PNK_EXPORT_BATCH_SPEC:
           case PNK_FRESHENBLOCK:
           case PNK_SUPERPROP:
           case PNK_OBJECT_PROPERTY_NAME:
             MOZ_ASSERT(cur->isArity(PN_NULLARY));
             break;
 
+          case PNK_TYPEOFNAME:
+            MOZ_ASSERT(cur->isArity(PN_UNARY));
+            MOZ_ASSERT(cur->pn_kid->isKind(PNK_NAME));
+            MOZ_ASSERT(!cur->pn_kid->maybeExpr());
+            break;
+
           // Nodes with a single non-null child requiring name resolution.
-          case PNK_TYPEOF:
+          case PNK_TYPEOFEXPR:
           case PNK_VOID:
           case PNK_NOT:
           case PNK_BITNOT:
           case PNK_THROW:
-          case PNK_DELETE:
+          case PNK_DELETENAME:
+          case PNK_DELETEPROP:
+          case PNK_DELETESUPERPROP:
+          case PNK_DELETEELEM:
+          case PNK_DELETESUPERELEM:
+          case PNK_DELETEEXPR:
           case PNK_NEG:
           case PNK_POS:
           case PNK_PREINCREMENT:
           case PNK_POSTINCREMENT:
           case PNK_PREDECREMENT:
           case PNK_POSTDECREMENT:
           case PNK_COMPUTED_NAME:
           case PNK_ARRAYPUSH:
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -216,22 +216,28 @@ PushNodeChildren(ParseNode* pn, NodeStac
       case PNK_FRESHENBLOCK:
       case PNK_SUPERPROP:
         MOZ_ASSERT(pn->isArity(PN_NULLARY));
         MOZ_ASSERT(!pn->isUsed(), "handle non-trivial cases separately");
         MOZ_ASSERT(!pn->isDefn(), "handle non-trivial cases separately");
         return PushResult::Recyclable;
 
       // Nodes with a single non-null child.
-      case PNK_TYPEOF:
+      case PNK_TYPEOFNAME:
+      case PNK_TYPEOFEXPR:
       case PNK_VOID:
       case PNK_NOT:
       case PNK_BITNOT:
       case PNK_THROW:
-      case PNK_DELETE:
+      case PNK_DELETENAME:
+      case PNK_DELETEPROP:
+      case PNK_DELETESUPERPROP:
+      case PNK_DELETEELEM:
+      case PNK_DELETESUPERELEM:
+      case PNK_DELETEEXPR:
       case PNK_POS:
       case PNK_NEG:
       case PNK_PREINCREMENT:
       case PNK_POSTINCREMENT:
       case PNK_PREDECREMENT:
       case PNK_POSTDECREMENT:
       case PNK_COMPUTED_NAME:
       case PNK_ARRAYPUSH:
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -113,17 +113,23 @@ class UpvarCookie
     F(BREAK) \
     F(CONTINUE) \
     F(VAR) \
     F(CONST) \
     F(GLOBALCONST) \
     F(WITH) \
     F(RETURN) \
     F(NEW) \
-    F(DELETE) \
+    /* Delete operations.  These must be sequential. */ \
+    F(DELETENAME) \
+    F(DELETEPROP) \
+    F(DELETESUPERPROP) \
+    F(DELETEELEM) \
+    F(DELETESUPERELEM) \
+    F(DELETEEXPR) \
     F(TRY) \
     F(CATCH) \
     F(CATCHLIST) \
     F(THROW) \
     F(DEBUGGER) \
     F(GENERATOR) \
     F(YIELD) \
     F(YIELD_STAR) \
@@ -152,17 +158,18 @@ class UpvarCookie
     F(CLASS) \
     F(CLASSMETHOD) \
     F(CLASSMETHODLIST) \
     F(CLASSNAMES) \
     F(SUPERPROP) \
     F(SUPERELEM) \
     \
     /* Unary operators. */ \
-    F(TYPEOF) \
+    F(TYPEOFNAME) \
+    F(TYPEOFEXPR) \
     F(VOID) \
     F(NOT) \
     F(BITNOT) \
     \
     /* \
      * Binary operators. \
      * These must be in the same order as TOK_OR and friends in TokenStream.h. \
      */ \
@@ -222,16 +229,22 @@ enum ParseNodeKind
 #undef EMIT_ENUM
     PNK_LIMIT, /* domain size */
     PNK_BINOP_FIRST = PNK_OR,
     PNK_BINOP_LAST = PNK_MOD,
     PNK_ASSIGNMENT_START = PNK_ASSIGN,
     PNK_ASSIGNMENT_LAST = PNK_MODASSIGN
 };
 
+inline bool
+IsDeleteKind(ParseNodeKind kind)
+{
+    return PNK_DELETENAME <= kind && kind <= PNK_DELETEEXPR;
+}
+
 /*
  * Label        Variant     Members
  * -----        -------     -------
  * <Definitions>
  * PNK_FUNCTION name        pn_funbox: ptr to js::FunctionBox holding function
  *                            object containing arg and var properties.  We
  *                            create the function object at parse (not emit)
  *                            time to specialize arg and var bytecodes early.
@@ -377,28 +390,38 @@ enum ParseNodeKind
  * PNK_URSH
  * PNK_ADD,     binary      pn_left: left-assoc ADD expr, pn_right: MUL expr
  * PNK_SUB
  * PNK_STAR,    binary      pn_left: left-assoc MUL expr, pn_right: UNARY expr
  * PNK_DIV,                 pn_op: JSOP_MUL, JSOP_DIV, JSOP_MOD
  * PNK_MOD
  * PNK_POS,     unary       pn_kid: UNARY expr
  * PNK_NEG
- * PNK_TYPEOF,  unary       pn_kid: UNARY expr
- * PNK_VOID,
+ * PNK_VOID,    unary       pn_kid: UNARY expr
  * PNK_NOT,
  * PNK_BITNOT
+ * PNK_TYPEOFNAME, unary    pn_kid: UNARY expr
+ * PNK_TYPEOFEXPR
  * PNK_PREINCREMENT, unary  pn_kid: MEMBER expr
  * PNK_POSTINCREMENT,
  * PNK_PREDECREMENT,
  * PNK_POSTDECREMENT
  * PNK_NEW      list        pn_head: list of ctor, arg1, arg2, ... argN
  *                          pn_count: 1 + N (where N is number of args)
  *                          ctor is a MEMBER expr
- * PNK_DELETE   unary       pn_kid: MEMBER expr
+ * PNK_DELETENAME unary     pn_kid: PNK_NAME expr
+ * PNK_DELETEPROP unary     pn_kid: PNK_DOT expr
+ * PNK_DELETESUPERPROP unary pn_kid: PNK_SUPERPROP expr
+ * PNK_DELETEELEM unary     pn_kid: PNK_ELEM expr
+ * PNK_DELETESUPERELEM unary pn_kid: PNK_SUPERELEM expr
+ * PNK_DELETEEXPR unary     pn_kid: MEMBER expr that's evaluated, then the
+ *                          overall delete evaluates to true; can't be a kind
+ *                          for a more-specific PNK_DELETE* unless constant
+ *                          folding (or a similar parse tree manipulation) has
+ *                          occurred
  * PNK_DOT      name        pn_expr: MEMBER expr to left of .
  *                          pn_atom: name to right of .
  * PNK_ELEM     binary      pn_left: MEMBER expr to left of [
  *                          pn_right: expr between [ and ]
  * PNK_CALL     list        pn_head: list of call, arg1, arg2, ... argN
  *                          pn_count: 1 + N (where N is number of args)
  *                          call is a MEMBER expr naming a callable object
  * PNK_GENEXP   list        Exactly like PNK_CALL, used for the implicit call
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -738,42 +738,16 @@ Parser<ParseHandler>::reportBadReturn(No
             return false;
     } else {
         errnum = anonerrnum;
     }
     return report(kind, pc->sc->strict(), pn, errnum, name.ptr());
 }
 
 /*
- * Check that assigning to lhs is permitted.  Assigning to 'eval' or
- * 'arguments' is banned in strict mode.
- */
-template <typename ParseHandler>
-bool
-Parser<ParseHandler>::checkStrictAssignment(Node lhs)
-{
-    if (!pc->sc->needStrictChecks())
-        return true;
-
-    JSAtom* atom = handler.isName(lhs);
-    if (!atom)
-        return true;
-
-    if (atom == context->names().eval || atom == context->names().arguments) {
-        JSAutoByteString name;
-        if (!AtomToPrintableString(context, atom, &name))
-            return false;
-
-        if (!report(ParseStrictError, pc->sc->strict(), lhs, JSMSG_BAD_STRICT_ASSIGN, name.ptr()))
-            return false;
-    }
-    return true;
-}
-
-/*
  * Check that it is permitted to introduce a binding for atom.  Strict mode
  * forbids introducing new definitions for 'eval', 'arguments', or for any
  * strict mode reserved keyword.  Use pn for reporting error locations, or use
  * pc's token stream if pn is nullptr.
  */
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::checkStrictBinding(PropertyName* name, Node pn)
@@ -3282,30 +3256,30 @@ Parser<ParseHandler>::bindVarOrGlobalCon
             }
         }
     }
 
     parser->handler.linkUseToDef(pn, dn);
     return true;
 }
 
-template <>
+template <typename ParseHandler>
 bool
-Parser<FullParseHandler>::makeSetCall(ParseNode* pn, unsigned msg)
-{
-    MOZ_ASSERT(pn->isKind(PNK_CALL));
-    MOZ_ASSERT(pn->isArity(PN_LIST));
-    MOZ_ASSERT(pn->isOp(JSOP_CALL) || pn->isOp(JSOP_SPREADCALL) ||
-               pn->isOp(JSOP_EVAL) || pn->isOp(JSOP_STRICTEVAL) ||
-               pn->isOp(JSOP_SPREADEVAL) || pn->isOp(JSOP_STRICTSPREADEVAL) ||
-               pn->isOp(JSOP_FUNCALL) || pn->isOp(JSOP_FUNAPPLY));
-
-    if (!report(ParseStrictError, pc->sc->strict(), pn, msg))
+Parser<ParseHandler>::makeSetCall(Node target, unsigned msg)
+{
+    MOZ_ASSERT(handler.isFunctionCall(target));
+
+    // Assignment to function calls is forbidden in ES6.  We're still somewhat
+    // concerned about sites using this in dead code, so forbid it only in
+    // strict mode code (or if the werror option has been set), and otherwise
+    // warn.
+    if (!report(ParseStrictError, pc->sc->strict(), target, msg))
         return false;
-    handler.markAsSetCall(pn);
+
+    handler.markAsSetCall(target);
     return true;
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::noteNameUse(HandlePropertyName name, Node pn)
 {
     /*
@@ -4518,16 +4492,20 @@ Parser<FullParseHandler>::exportDeclarat
             kid = functionStmt(YieldIsKeyword, AllowDefaultName);
             break;
           case TOK_CLASS:
             kid = classDefinition(YieldIsKeyword, ClassStatement, AllowDefaultName);
             break;
           default:
             tokenStream.ungetToken();
             kid = assignExpr(InAllowed, YieldIsKeyword);
+            if (kid) {
+                if (!MatchOrInsertSemicolon(tokenStream))
+                    return null();
+            }
             break;
         }
         if (!kid)
             return null();
 
         return handler.newExportDefaultDeclaration(kid, TokenPos(begin, pos().end));
       }
 
@@ -5128,19 +5106,18 @@ Parser<SyntaxParseHandler>::forStatement
             return null();
     }
     if (isForIn || isForOf) {
         /* Parse the rest of the for/in or for/of head. */
         forStmt.type = isForOf ? STMT_FOR_OF_LOOP : STMT_FOR_IN_LOOP;
 
         /* Check that the left side of the 'in' or 'of' is valid. */
         if (!isForDecl &&
-            lhsNode != SyntaxParseHandler::NodeName &&
-            lhsNode != SyntaxParseHandler::NodeGetProp &&
-            lhsNode != SyntaxParseHandler::NodeLValue)
+            !handler.maybeName(lhsNode) &&
+            !handler.isPropertyAccess(lhsNode))
         {
             JS_ALWAYS_FALSE(abortIfSyntaxParser());
             return null();
         }
 
         if (!simpleForDecl) {
             JS_ALWAYS_FALSE(abortIfSyntaxParser());
             return null();
@@ -6415,84 +6392,66 @@ Parser<ParseHandler>::condExpr1(InHandli
 
     // Advance to the next token; the caller is responsible for interpreting it.
     TokenKind ignored;
     if (!tokenStream.getToken(&ignored))
         return null();
     return handler.newConditional(condition, thenExpr, elseExpr);
 }
 
-template <>
+template <typename ParseHandler>
 bool
-Parser<FullParseHandler>::checkAndMarkAsAssignmentLhs(ParseNode* pn, AssignmentFlavor flavor)
-{
-    switch (pn->getKind()) {
-      case PNK_NAME:
-        if (!checkStrictAssignment(pn))
-            return false;
-        if (flavor == KeyedDestructuringAssignment) {
-            /*
-             * We may be called on a name node that has already been
-             * specialized, in the very weird "for (var [x] = i in o) ..."
-             * case. See bug 558633.
-             */
-            if (!(js_CodeSpec[pn->getOp()].format & JOF_SET))
-                pn->setOp(JSOP_SETNAME);
-        } else {
-            pn->setOp(pn->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME);
-        }
-        pn->markAsAssigned();
-        break;
-
-      case PNK_DOT:
-      case PNK_ELEM:
-      case PNK_SUPERPROP:
-      case PNK_SUPERELEM:
-        break;
-
-      case PNK_ARRAY:
-      case PNK_OBJECT:
+Parser<ParseHandler>::checkAndMarkAsAssignmentLhs(Node target, AssignmentFlavor flavor)
+{
+    // Handle destructuring object/array patterns specially.
+    if (handler.isDestructuringTarget(target)) {
         if (flavor == CompoundAssignment) {
             report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_ASS);
             return false;
         }
-        if (!checkDestructuring(nullptr, pn))
-            return false;
-        break;
-
-      case PNK_CALL:
-        if (flavor == KeyedDestructuringAssignment) {
-            report(ParseError, false, pn, JSMSG_BAD_DESTRUCT_TARGET);
-            return false;
-        }
-        if (!makeSetCall(pn, JSMSG_BAD_LEFTSIDE_OF_ASS))
+
+        return checkDestructuring(nullptr, target);
+    }
+
+    // All other permitted targets are simple.
+    if (!reportIfNotValidSimpleAssignmentTarget(target, flavor))
+        return false;
+
+    if (handler.isPropertyAccess(target))
+        return true;
+
+    if (handler.maybeName(target)) {
+        // The arguments/eval identifiers are simple in non-strict mode code,
+        // but warn to discourage use nonetheless.
+        if (!reportIfArgumentsEvalTarget(target))
             return false;
-        break;
-
-      default:
-        unsigned errnum = (flavor == KeyedDestructuringAssignment) ? JSMSG_BAD_DESTRUCT_TARGET :
-            JSMSG_BAD_LEFTSIDE_OF_ASS;
-        report(ParseError, false, pn, errnum);
+
+        if (flavor == KeyedDestructuringAssignment) {
+            // We may be called on a name node that has already been
+            // specialized, in the very weird "for (var [x] = i in o) ..."
+            // case. See bug 558633.
+            //
+            // XXX Is this necessary with the changes in bug 1164741?  This is
+            //     likely removable now.
+            handler.maybeDespecializeSet(target);
+        } else {
+            handler.adjustGetToSet(target);
+        }
+        handler.markAsAssigned(target);
+        return true;
+    }
+
+    MOZ_ASSERT(handler.isFunctionCall(target));
+
+    if (flavor == KeyedDestructuringAssignment) {
+        report(ParseError, false, target, JSMSG_BAD_DESTRUCT_TARGET);
         return false;
     }
-    return true;
-}
-
-template <>
-bool
-Parser<SyntaxParseHandler>::checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor)
-{
-    /* Full syntax checking of valid assignment LHS terms requires a parse tree. */
-    if (pn != SyntaxParseHandler::NodeName &&
-        pn != SyntaxParseHandler::NodeGetProp &&
-        pn != SyntaxParseHandler::NodeLValue)
-    {
-        return abortIfSyntaxParser();
-    }
-    return checkStrictAssignment(pn);
+
+    return makeSetCall(target, JSMSG_BAD_LEFTSIDE_OF_ASS);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandling,
                                  InvokedPrediction invoked)
 {
     JS_CHECK_RECURSION(context, return null());
@@ -6601,151 +6560,226 @@ Parser<ParseHandler>::assignExpr(InHandl
     Node rhs = assignExpr(inHandling, yieldHandling);
     pc->inDeclDestructuring = saved;
     if (!rhs)
         return null();
 
     return handler.newAssignment(kind, lhs, rhs, pc, op);
 }
 
-static const char incop_name_str[][10] = {"increment", "decrement"};
-
-template <>
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::isValidSimpleAssignmentTarget(Node node)
+{
+    if (PropertyName* name = handler.maybeName(node)) {
+        // Note that we implement *exactly* the ES6 semantics here.  Warning
+        // for arguments/eval when extraWarnings is set isn't handled here.
+        if (!pc->sc->strict())
+            return true;
+
+        return name != context->names().arguments && name != context->names().eval;
+    }
+
+    if (handler.isPropertyAccess(node))
+        return true;
+    return handler.isFunctionCall(node);
+}
+
+template <typename ParseHandler>
 bool
-Parser<FullParseHandler>::checkAndMarkAsIncOperand(ParseNode* kid, TokenKind tt, bool preorder)
+Parser<ParseHandler>::reportIfArgumentsEvalTarget(Node target)
+{
+    PropertyName* name = handler.maybeName(target);
+    if (!name)
+        return true;
+
+    const char* chars = (name == context->names().arguments)
+                        ? js_arguments_str
+                        : (name == context->names().eval)
+                        ? js_eval_str
+                        : nullptr;
+    if (!chars)
+        return true;
+
+    if (!report(ParseStrictError, pc->sc->strict(), target, JSMSG_BAD_STRICT_ASSIGN, chars))
+        return false;
+
+    MOZ_ASSERT(!pc->sc->strict(), "in strict mode an error should have been reported");
+    return true;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::reportIfNotValidSimpleAssignmentTarget(Node target,
+                                                             AssignmentFlavor flavor)
+{
+    if (isValidSimpleAssignmentTarget(target))
+        return true;
+
+    // Use a special error if the target is arguments/eval, as a nicety.
+    if (!reportIfArgumentsEvalTarget(target))
+        return false;
+
+    unsigned errnum;
+    const char* extra = nullptr;
+
+    switch (flavor) {
+      case IncrementAssignment:
+        errnum = JSMSG_BAD_OPERAND;
+        extra = "increment";
+        break;
+
+      case DecrementAssignment:
+        errnum = JSMSG_BAD_OPERAND;
+        extra = "decrement";
+        break;
+
+      case KeyedDestructuringAssignment:
+        errnum = JSMSG_BAD_DESTRUCT_TARGET;
+        break;
+
+      case PlainAssignment:
+      case CompoundAssignment:
+        errnum = JSMSG_BAD_LEFTSIDE_OF_ASS;
+        break;
+    }
+
+    report(ParseError, pc->sc->strict(), target, errnum, extra);
+    return false;
+}
+
+template <typename ParseHandler>
+bool
+Parser<ParseHandler>::checkAndMarkAsIncOperand(Node target, AssignmentFlavor flavor)
 {
     // Check.
-    if (!kid->isKind(PNK_NAME) &&
-        !kid->isKind(PNK_DOT) &&
-        !kid->isKind(PNK_SUPERPROP) &&
-        !kid->isKind(PNK_SUPERELEM) &&
-        !kid->isKind(PNK_ELEM) &&
-        !(kid->isKind(PNK_CALL) &&
-          (kid->isOp(JSOP_CALL) || kid->isOp(JSOP_SPREADCALL) ||
-           kid->isOp(JSOP_EVAL) || kid->isOp(JSOP_STRICTEVAL) ||
-           kid->isOp(JSOP_SPREADEVAL) || kid->isOp(JSOP_STRICTSPREADEVAL) ||
-           kid->isOp(JSOP_FUNCALL) ||
-           kid->isOp(JSOP_FUNAPPLY))))
-    {
-        report(ParseError, false, null(), JSMSG_BAD_OPERAND, incop_name_str[tt == TOK_DEC]);
+    if (!reportIfNotValidSimpleAssignmentTarget(target, flavor))
         return false;
-    }
-
-    if (!checkStrictAssignment(kid))
+
+    // Assignment to arguments/eval is allowed outside strict mode code,
+    // but it's dodgy.  Report a strict warning (error, if werror was set).
+    if (!reportIfArgumentsEvalTarget(target))
         return false;
 
     // Mark.
-    if (kid->isKind(PNK_NAME)) {
-        kid->markAsAssigned();
-    } else if (kid->isKind(PNK_CALL)) {
-        if (!makeSetCall(kid, JSMSG_BAD_INCOP_OPERAND))
+    if (handler.maybeName(target)) {
+        handler.markAsAssigned(target);
+    } else if (handler.isFunctionCall(target)) {
+        if (!makeSetCall(target, JSMSG_BAD_INCOP_OPERAND))
             return false;
     }
     return true;
 }
 
-template <>
-bool
-Parser<SyntaxParseHandler>::checkAndMarkAsIncOperand(Node kid, TokenKind tt, bool preorder)
-{
-    // To the extent of what we support in syntax-parse mode, the rules for
-    // inc/dec operands are the same as for assignment. There are differences,
-    // such as destructuring; but if we hit any of those cases, we'll abort and
-    // reparse in full mode.
-    return checkAndMarkAsAssignmentLhs(kid, IncDecAssignment);
-}
-
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, JSOp op,
                                   uint32_t begin)
 {
     Node kid = unaryExpr(yieldHandling);
     if (!kid)
         return null();
     return handler.newUnary(kind, op, begin, kid);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, InvokedPrediction invoked)
 {
-    Node pn, pn2;
-
     JS_CHECK_RECURSION(context, return null());
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
     uint32_t begin = pos().begin;
     switch (tt) {
-      case TOK_TYPEOF:
-        return unaryOpExpr(yieldHandling, PNK_TYPEOF, JSOP_TYPEOF, begin);
       case TOK_VOID:
         return unaryOpExpr(yieldHandling, PNK_VOID, JSOP_VOID, begin);
       case TOK_NOT:
         return unaryOpExpr(yieldHandling, PNK_NOT, JSOP_NOT, begin);
       case TOK_BITNOT:
         return unaryOpExpr(yieldHandling, PNK_BITNOT, JSOP_BITNOT, begin);
       case TOK_ADD:
         return unaryOpExpr(yieldHandling, PNK_POS, JSOP_POS, begin);
       case TOK_SUB:
         return unaryOpExpr(yieldHandling, PNK_NEG, JSOP_NEG, begin);
 
+      case TOK_TYPEOF: {
+        // The |typeof| operator is specially parsed to distinguish its
+        // application to a name, from its application to a non-name
+        // expression:
+        //
+        //   // Looks up the name, doesn't find it and so evaluates to
+        //   // "undefined".
+        //   assertEq(typeof nonExistentName, "undefined");
+        //
+        //   // Evaluates expression, triggering a runtime ReferenceError for
+        //   // the undefined name.
+        //   typeof (1, nonExistentName);
+        Node kid = unaryExpr(yieldHandling);
+        if (!kid)
+            return null();
+
+        return handler.newTypeof(begin, kid);
+      }
+
       case TOK_INC:
       case TOK_DEC:
       {
         TokenKind tt2;
         if (!tokenStream.getToken(&tt2, TokenStream::Operand))
             return null();
-        pn2 = memberExpr(yieldHandling, tt2, true);
+        Node pn2 = memberExpr(yieldHandling, tt2, true);
         if (!pn2)
             return null();
-        if (!checkAndMarkAsIncOperand(pn2, tt, true))
+        AssignmentFlavor flavor = (tt == TOK_INC) ? IncrementAssignment : DecrementAssignment;
+        if (!checkAndMarkAsIncOperand(pn2, flavor))
             return null();
         return handler.newUnary((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT,
                                 JSOP_NOP,
                                 begin,
                                 pn2);
       }
 
       case TOK_DELETE: {
         Node expr = unaryExpr(yieldHandling);
         if (!expr)
             return null();
 
         // Per spec, deleting any unary expression is valid -- it simply
         // returns true -- except for one case that is illegal in strict mode.
-        if (handler.isName(expr)) {
+        if (handler.maybeName(expr)) {
             if (!report(ParseStrictError, pc->sc->strict(), expr, JSMSG_DEPRECATED_DELETE_OPERAND))
                 return null();
             pc->sc->setBindingsAccessedDynamically();
         }
 
         return handler.newDelete(begin, expr);
       }
 
-      default:
-        pn = memberExpr(yieldHandling, tt, /* allowCallSyntax = */ true, invoked);
+      default: {
+        Node pn = memberExpr(yieldHandling, tt, /* allowCallSyntax = */ true, invoked);
         if (!pn)
             return null();
 
         /* Don't look across a newline boundary for a postfix incop. */
         if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
             return null();
         if (tt == TOK_INC || tt == TOK_DEC) {
             tokenStream.consumeKnownToken(tt);
-            if (!checkAndMarkAsIncOperand(pn, tt, false))
+            AssignmentFlavor flavor = (tt == TOK_INC) ? IncrementAssignment : DecrementAssignment;
+            if (!checkAndMarkAsIncOperand(pn, flavor))
                 return null();
             return handler.newUnary((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT,
                                     JSOP_NOP,
                                     begin,
                                     pn);
         }
         return pn;
+      }
     }
 }
 
 /*
  * A dedicated helper for transplanting the legacy comprehension expression E in
  *
  *   [E for (V in I)]   // legacy array comprehension
  *   (E for (V in I))   // legacy generator expression
@@ -7917,23 +7951,23 @@ Parser<ParseHandler>::memberExpr(YieldHa
                    tt == TOK_NO_SUBS_TEMPLATE)
         {
             if (isSuper) {
                 // For now...
                 report(ParseError, false, null(), JSMSG_BAD_SUPER);
                 return null();
             }
 
-            JSOp op = JSOP_CALL;
-            nextMember = handler.newList(tt == TOK_LP ? PNK_CALL : PNK_TAGGED_TEMPLATE, JSOP_CALL);
+            nextMember = tt == TOK_LP ? handler.newCall() : handler.newTaggedTemplate();
             if (!nextMember)
                 return null();
 
-            if (JSAtom* atom = handler.isName(lhs)) {
-                if (tt == TOK_LP && atom == context->names().eval) {
+            JSOp op = JSOP_CALL;
+            if (PropertyName* name = handler.maybeName(lhs)) {
+                if (tt == TOK_LP && name == context->names().eval) {
                     /* Select JSOP_EVAL and flag pc as heavyweight. */
                     op = pc->sc->strict() ? JSOP_STRICTEVAL : JSOP_EVAL;
                     pc->sc->setBindingsAccessedDynamically();
                     pc->sc->setHasDirectEval();
 
                     /*
                      * In non-strict mode code, direct calls to eval can add
                      * variables to the call object.
@@ -7942,23 +7976,24 @@ Parser<ParseHandler>::memberExpr(YieldHa
                         pc->sc->asFunctionBox()->setHasExtensibleScope();
 
                     // If we're in a method, mark the method as requiring
                     // support for 'super', since direct eval code can use it.
                     // (If we're not in a method, that's fine, so ignore the
                     // return value.)
                     checkAndMarkSuperScope();
                 }
-            } else if (JSAtom* atom = handler.isGetProp(lhs)) {
-                /* Select JSOP_FUNAPPLY given foo.apply(...). */
-                if (atom == context->names().apply) {
+            } else if (PropertyName* prop = handler.maybeDottedProperty(lhs)) {
+                // Use the JSOP_FUN{APPLY,CALL} optimizations given the right
+                // syntax.
+                if (prop == context->names().apply) {
                     op = JSOP_FUNAPPLY;
                     if (pc->sc->isFunctionBox())
                         pc->sc->asFunctionBox()->usesApply = true;
-                } else if (atom == context->names().call) {
+                } else if (prop == context->names().call) {
                     op = JSOP_FUNCALL;
                 }
             }
 
             handler.setBeginPosition(nextMember, lhs);
             handler.addList(nextMember, lhs);
 
             if (tt == TOK_LP) {
@@ -7997,17 +8032,17 @@ Parser<ParseHandler>::memberExpr(YieldHa
 
     return lhs;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::newName(PropertyName* name)
 {
-    return handler.newName(name, pc->blockid(), pos());
+    return handler.newName(name, pc->blockid(), pos(), context);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::identifierName(YieldHandling yieldHandling)
 {
     RootedPropertyName name(context, tokenStream.currentName());
     if (yieldHandling == YieldIsKeyword && name == context->names().yield) {
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -651,34 +651,45 @@ class Parser : private JS::AutoGCRooter,
         return versionNumber() >= JSVERSION_1_6;
 #endif
     }
 
     enum AssignmentFlavor {
         PlainAssignment,
         CompoundAssignment,
         KeyedDestructuringAssignment,
-        IncDecAssignment
+        IncrementAssignment,
+        DecrementAssignment
     };
 
     bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor);
     bool matchInOrOf(bool* isForInp, bool* isForOfp);
 
     bool checkFunctionArguments();
     bool makeDefIntoUse(Definition* dn, Node pn, JSAtom* atom);
     bool checkFunctionDefinition(HandlePropertyName funName, Node* pn, FunctionSyntaxKind kind,
                                  bool* pbodyProcessed);
     bool finishFunctionDefinition(Node pn, FunctionBox* funbox, Node body);
     bool addFreeVariablesFromLazyFunction(JSFunction* fun, ParseContext<ParseHandler>* pc);
 
     bool isValidForStatementLHS(Node pn1, JSVersion version, bool forDecl, bool forEach,
                                 ParseNodeKind headKind);
     bool checkForHeadConstInitializers(Node pn1);
-    bool checkAndMarkAsIncOperand(Node kid, TokenKind tt, bool preorder);
+
+    bool isValidSimpleAssignmentTarget(Node node);
+
+    // Invalid assignment targets are handled differently in different places.
+    // Select the desired semantics using |flavor|.
+    bool reportIfArgumentsEvalTarget(Node target);
+    bool reportIfNotValidSimpleAssignmentTarget(Node target, AssignmentFlavor flavor);
+
+    bool checkAndMarkAsIncOperand(Node kid, AssignmentFlavor flavor);
+
     bool checkStrictAssignment(Node lhs);
+
     bool checkStrictBinding(PropertyName* name, Node pn);
     bool defineArg(Node funcpn, HandlePropertyName name,
                    bool disallowDuplicateArgs = false, Node* duplicatedArg = nullptr);
     Node pushLexicalScope(StmtInfoPC* stmt);
     Node pushLexicalScope(Handle<StaticBlockObject*> blockObj, StmtInfoPC* stmt);
     Node pushLetScope(Handle<StaticBlockObject*> blockObj, StmtInfoPC* stmt);
     bool noteNameUse(HandlePropertyName name, Node pn);
     Node computedPropertyName(YieldHandling yieldHandling, Node literal);
@@ -691,17 +702,17 @@ class Parser : private JS::AutoGCRooter,
     bool checkAndPrepareLexical(bool isConst, const TokenPos& errorPos);
     Node makeInitializedLexicalBinding(HandlePropertyName name, bool isConst, const TokenPos& pos);
 
     Node newBindingNode(PropertyName* name, bool functionScope, VarContext varContext = HoistVars);
     bool checkDestructuring(BindData<ParseHandler>* data, Node left);
     bool checkDestructuringObject(BindData<ParseHandler>* data, Node objectPattern);
     bool checkDestructuringArray(BindData<ParseHandler>* data, Node arrayPattern);
     bool bindInitialized(BindData<ParseHandler>* data, Node pn);
-    bool makeSetCall(Node pn, unsigned msg);
+    bool makeSetCall(Node node, unsigned errnum);
     Node cloneDestructuringDefault(Node opn);
     Node cloneLeftHandSide(Node opn);
     Node cloneParseTree(Node opn);
 
     Node newNumber(const Token& tok) {
         return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos);
     }
 
@@ -731,26 +742,16 @@ class Parser : private JS::AutoGCRooter,
     bool asmJS(Node list);
 
     void addTelemetry(JSCompartment::DeprecatedLanguageExtension e);
 
     friend class LegacyCompExprTransplanter;
     friend struct BindData<ParseHandler>;
 };
 
-/* Declare some required template specializations. */
-
-template <>
-bool
-Parser<FullParseHandler>::checkAndMarkAsAssignmentLhs(ParseNode* pn, AssignmentFlavor flavor);
-
-template <>
-bool
-Parser<SyntaxParseHandler>::checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor);
-
 } /* namespace frontend */
 } /* namespace js */
 
 /*
  * Convenience macro to access Parser.tokenStream as a pointer.
  */
 #define TS(p) (&(p)->tokenStream)
 
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -34,25 +34,44 @@ class SyntaxParseHandler
     JSAtom* lastAtom;
     TokenPos lastStringPos;
     TokenStream& tokenStream;
 
   public:
     enum Node {
         NodeFailure = 0,
         NodeGeneric,
-        NodeName,
         NodeGetProp,
         NodeStringExprStatement,
-        NodeLValue,
         NodeReturn,
         NodeHoistableDeclaration,
         NodeBreak,
         NodeThrow,
 
+        // This is needed for proper assignment-target handling.  ES6 formally
+        // requires function calls *not* pass IsValidSimpleAssignmentTarget,
+        // but at last check there were still sites with |f() = 5| and similar
+        // in code not actually executed (or at least not executed enough to be
+        // noticed).
+        NodeFunctionCall,
+
+        // Nodes representing names.  These *must* be sequential per |isName|.
+        NodeArgumentsName,
+        NodeEvalName,
+        NodeName,
+
+        NodeDottedProperty,
+        NodeElement,
+        NodeSuperProperty,
+        NodeSuperElement,
+
+        // Valuable for recognizing potential destructuring patterns.
+        NodeArray,
+        NodeObject,
+
         // In rare cases a parenthesized |node| doesn't have the same semantics
         // as |node|.  Each such node has a special Node value, and we use a
         // different Node value to represent the parenthesized form.  See also
         // isUnparenthesized*(Node), newExprStatement(Node, uint32_t),
         // parenthesize(Node), and meaningMightChangeIfParenthesized(Node).
 
         // The directive prologue at the start of a FunctionBody or ScriptBody
         // is the longest sequence (possibly empty) of string literal
@@ -85,16 +104,30 @@ class SyntaxParseHandler
         // (Technically this isn't needed, as these are *only* extraWarnings
         // warnings, and parsing with that option disables syntax parsing.  But
         // it seems best to be consistent, and perhaps the syntax parser will
         // eventually enforce extraWarnings and will require this then.)
         NodeUnparenthesizedAssignment
     };
     typedef Definition::Kind DefinitionNode;
 
+    bool isPropertyAccess(Node node) {
+        return node == NodeDottedProperty || node == NodeElement ||
+               node == NodeSuperProperty || node == NodeSuperElement;
+    }
+
+    bool isFunctionCall(Node node) {
+        // Note: super() is a special form, *not* a function call.
+        return node == NodeFunctionCall;
+    }
+
+    bool isDestructuringTarget(Node node) {
+        return node == NodeArray || node == NodeObject;
+    }
+
   private:
     static bool meaningMightChangeIfParenthesized(Node node) {
         return node == NodeUnparenthesizedString ||
                node == NodeUnparenthesizedCommaExpr ||
                node == NodeUnparenthesizedYieldExpr ||
                node == NodeUnparenthesizedAssignment;
     }
 
@@ -106,18 +139,22 @@ class SyntaxParseHandler
       : lastAtom(nullptr),
         tokenStream(tokenStream)
     {}
 
     static Node null() { return NodeFailure; }
 
     void trace(JSTracer* trc) {}
 
-    Node newName(PropertyName* name, uint32_t blockid, const TokenPos& pos) {
+    Node newName(PropertyName* name, uint32_t blockid, const TokenPos& pos, ExclusiveContext* cx) {
         lastAtom = name;
+        if (name == cx->names().arguments)
+            return NodeArgumentsName;
+        if (name == cx->names().eval)
+            return NodeEvalName;
         return NodeName;
     }
 
     Node newComputedName(Node expr, uint32_t start, uint32_t end) {
         return NodeName;
     }
 
     DefinitionNode newPlaceholder(JSAtom* atom, uint32_t blockid, const TokenPos& pos) {
@@ -154,17 +191,27 @@ class SyntaxParseHandler
 
     template <class Boxer>
     Node newRegExp(RegExpObject* reobj, const TokenPos& pos, Boxer& boxer) { return NodeGeneric; }
 
     Node newConditional(Node cond, Node thenExpr, Node elseExpr) { return NodeGeneric; }
 
     Node newElision() { return NodeGeneric; }
 
-    Node newDelete(uint32_t begin, Node expr) { return NodeGeneric; }
+    void markAsSetCall(Node node) {
+        MOZ_ASSERT(node == NodeFunctionCall);
+    }
+
+    Node newDelete(uint32_t begin, Node expr) {
+        return NodeGeneric;
+    }
+
+    Node newTypeof(uint32_t begin, Node kid) {
+        return NodeGeneric;
+    }
 
     Node newUnary(ParseNodeKind kind, JSOp op, uint32_t begin, Node kid) {
         return NodeGeneric;
     }
 
     Node newBinary(ParseNodeKind kind, JSOp op = JSOP_NOP) { return NodeGeneric; }
     Node newBinary(ParseNodeKind kind, Node left, JSOp op = JSOP_NOP) { return NodeGeneric; }
     Node newBinary(ParseNodeKind kind, Node left, Node right, JSOp op = JSOP_NOP) {
@@ -179,25 +226,35 @@ class SyntaxParseHandler
         return NodeGeneric;
     }
 
     // Expressions
 
     Node newArrayComprehension(Node body, unsigned blockid, const TokenPos& pos) {
         return NodeGeneric;
     }
-    Node newArrayLiteral(uint32_t begin, unsigned blockid) { return NodeGeneric; }
+    Node newArrayLiteral(uint32_t begin, unsigned blockid) { return NodeArray; }
     bool addElision(Node literal, const TokenPos& pos) { return true; }
     bool addSpreadElement(Node literal, uint32_t begin, Node inner) { return true; }
     void addArrayElement(Node literal, Node element) { }
 
-    Node newObjectLiteral(uint32_t begin) { return NodeGeneric; }
+    Node newCall() { return NodeFunctionCall; }
+    Node newTaggedTemplate() { return NodeGeneric; }
+
+    Node newObjectLiteral(uint32_t begin) { return NodeObject; }
     Node newClassMethodList(uint32_t begin) { return NodeGeneric; }
-    Node newSuperProperty(JSAtom* atom, const TokenPos& pos) { return NodeGeneric; }
-    Node newSuperElement(Node expr, const TokenPos& pos) { return NodeGeneric; }
+
+    Node newSuperProperty(PropertyName* prop, const TokenPos& pos) {
+        return NodeSuperProperty;
+    }
+
+    Node newSuperElement(Node expr, const TokenPos& pos) {
+        return NodeSuperElement;
+    }
+
     bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; }
     bool addPropertyDefinition(Node literal, Node name, Node expr) { return true; }
     bool addShorthand(Node literal, Node name, Node expr) { return true; }
     bool addObjectMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; }
     bool addClassMethodDefinition(Node literal, Node name, Node fn, JSOp op, bool isStatic) { return true; }
     Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeUnparenthesizedYieldExpr; }
     Node newYieldStarExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
 
@@ -228,20 +285,20 @@ class SyntaxParseHandler
     Node newThrowStatement(Node expr, const TokenPos& pos) { return NodeThrow; }
     Node newTryStatement(uint32_t begin, Node body, Node catchList, Node finallyBlock) {
         return NodeGeneric;
     }
     Node newDebuggerStatement(const TokenPos& pos) { return NodeGeneric; }
 
     Node newPropertyAccess(Node pn, PropertyName* name, uint32_t end) {
         lastAtom = name;
-        return NodeGetProp;
+        return NodeDottedProperty;
     }
 
-    Node newPropertyByValue(Node pn, Node kid, uint32_t end) { return NodeLValue; }
+    Node newPropertyByValue(Node pn, Node kid, uint32_t end) { return NodeElement; }
 
     bool addCatchBlock(Node catchList, Node letBlock,
                        Node catchName, Node catchGuard, Node catchBody) { return true; }
 
     bool setLastFunctionArgumentDefault(Node funcpn, Node pn) { return true; }
     void setLastFunctionArgumentDestructuring(Node funcpn, Node pn) {}
     Node newFunctionDefinition() { return NodeHoistableDeclaration; }
     void setFunctionBody(Node pn, Node kid) {}
@@ -300,18 +357,22 @@ class SyntaxParseHandler
         return newList(PNK_CATCHLIST, JSOP_NOP);
     }
 
     Node newCommaExpressionList(Node kid) {
         return NodeUnparenthesizedCommaExpr;
     }
 
     void addList(Node list, Node kid) {
-        MOZ_ASSERT(list == NodeGeneric || list == NodeUnparenthesizedCommaExpr ||
-                   list == NodeHoistableDeclaration);
+        MOZ_ASSERT(list == NodeGeneric ||
+                   list == NodeArray ||
+                   list == NodeObject ||
+                   list == NodeUnparenthesizedCommaExpr ||
+                   list == NodeHoistableDeclaration ||
+                   list == NodeFunctionCall);
     }
 
     Node newAssignment(ParseNodeKind kind, Node lhs, Node rhs,
                        ParseContext<SyntaxParseHandler>* pc, JSOp op)
     {
         if (kind == PNK_ASSIGN)
             return NodeUnparenthesizedAssignment;
         return newBinary(kind, lhs, rhs, op);
@@ -350,30 +411,45 @@ class SyntaxParseHandler
         return node;
     }
     MOZ_WARN_UNUSED_RESULT Node setLikelyIIFE(Node pn) {
         return pn; // Remain in syntax-parse mode.
     }
     void setPrologue(Node pn) {}
 
     bool isConstant(Node pn) { return false; }
-    PropertyName* isName(Node pn) {
-        return (pn == NodeName) ? lastAtom->asPropertyName() : nullptr;
+    PropertyName* maybeName(Node pn) {
+        if (pn == NodeName || pn == NodeArgumentsName || pn == NodeEvalName)
+            return lastAtom->asPropertyName();
+        return nullptr;
     }
-    PropertyName* isGetProp(Node pn) {
-        return (pn == NodeGetProp) ? lastAtom->asPropertyName() : nullptr;
+
+    PropertyName* maybeDottedProperty(Node node) {
+        // Note: |super.apply(...)| is a special form that calls an "apply"
+        // method retrieved from one value, but using a *different* value as
+        // |this|.  It's not really eligible for the funapply/funcall
+        // optimizations as they're currently implemented (assuming a single
+        // value is used for both retrieval and |this|).
+        if (node != NodeDottedProperty)
+            return nullptr;
+        return lastAtom->asPropertyName();
     }
+
     JSAtom* isStringExprStatement(Node pn, TokenPos* pos) {
         if (pn == NodeStringExprStatement) {
             *pos = lastStringPos;
             return lastAtom;
         }
         return nullptr;
     }
 
+    void markAsAssigned(Node node) {}
+    void adjustGetToSet(Node node) {}
+    void maybeDespecializeSet(Node node) {}
+
     Node makeAssignment(Node pn, Node rhs) { return NodeGeneric; }
 
     static Node getDefinitionNode(DefinitionNode dn) { return NodeGeneric; }
     static Definition::Kind getDefinitionKind(DefinitionNode dn) { return dn; }
     static bool isPlaceholderDefinition(DefinitionNode dn) { return dn == Definition::PLACEHOLDER; }
     void linkUseToDef(Node pn, DefinitionNode dn) {}
     DefinitionNode resolve(DefinitionNode dn) { return dn; }
     void deoptimizeUsesWithin(DefinitionNode dn, const TokenPos& pos) {}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/modules/bug1169850.js
@@ -0,0 +1,9 @@
+// export-default should throw SyntaxError until it's implemented.
+
+var caught = false;
+try {
+  eval("export default 1;");
+} catch (e) {
+  caught = true;
+}
+assertEq(caught, true);
--- a/js/src/jit-test/tests/modules/export-declaration.js
+++ b/js/src/jit-test/tests/modules/export-declaration.js
@@ -318,16 +318,20 @@ program([
     exportDeclaration(
         lit(1234),
         null,
         null,
         true
     )
 ]).assert(Reflect.parse("export default 1234"));
 
+assertThrowsInstanceOf(function () {
+   Reflect.parse("export default 1234 5678");
+}, SyntaxError);
+
 var loc = Reflect.parse("export { a as b } from 'c'", {
     loc: true
 }).body[0].loc;
 
 assertEq(loc.start.line, 1);
 assertEq(loc.start.column, 0);
 assertEq(loc.start.line, 1);
 assertEq(loc.end.column, 26);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -5151,31 +5151,16 @@ CodeGenerator::visitPowD(LPowD* ins)
     masm.passABIArg(value, MoveOp::DOUBLE);
     masm.passABIArg(power, MoveOp::DOUBLE);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ecmaPow), MoveOp::DOUBLE);
 
     MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
 }
 
 void
-CodeGenerator::visitRandom(LRandom* ins)
-{
-    Register temp = ToRegister(ins->temp());
-    Register temp2 = ToRegister(ins->temp2());
-
-    masm.loadJSContext(temp);
-
-    masm.setupUnalignedABICall(1, temp2);
-    masm.passABIArg(temp);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
-
-    MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
-}
-
-void
 CodeGenerator::visitMathFunctionD(LMathFunctionD* ins)
 {
     Register temp = ToRegister(ins->temp());
     FloatRegister input = ToFloatRegister(ins->input());
     MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
 
     const MathCache* mathCache = ins->mir()->cache();
 
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -210,17 +210,16 @@ class CodeGenerator : public CodeGenerat
     void visitSetPropertyPolymorphicV(LSetPropertyPolymorphicV* ins);
     void visitArraySplice(LArraySplice* splice);
     void visitSetPropertyPolymorphicT(LSetPropertyPolymorphicT* ins);
     void visitAbsI(LAbsI* lir);
     void visitAtan2D(LAtan2D* lir);
     void visitHypot(LHypot* lir);
     void visitPowI(LPowI* lir);
     void visitPowD(LPowD* lir);
-    void visitRandom(LRandom* lir);
     void visitMathFunctionD(LMathFunctionD* ins);
     void visitMathFunctionF(LMathFunctionF* ins);
     void visitModD(LModD* ins);
     void visitMinMaxI(LMinMaxI* lir);
     void visitBinaryV(LBinaryV* lir);
     void emitCompareS(LInstruction* lir, JSOp op, Register left, Register right, Register output);
     void visitCompareS(LCompareS* lir);
     void visitCompareStrictS(LCompareStrictS* lir);
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -3121,33 +3121,16 @@ class LPowD : public LCallInstructionHel
     const LAllocation* power() {
         return getOperand(1);
     }
     const LDefinition* temp() {
         return getTemp(0);
     }
 };
 
-// Math.random().
-class LRandom : public LCallInstructionHelper<1, 0, 2>
-{
-  public:
-    LIR_HEADER(Random)
-    LRandom(const LDefinition& temp, const LDefinition& temp2) {
-        setTemp(0, temp);
-        setTemp(1, temp2);
-    }
-    const LDefinition* temp() {
-        return getTemp(0);
-    }
-    const LDefinition* temp2() {
-        return getTemp(1);
-    }
-};
-
 class LMathFunctionD : public LCallInstructionHelper<1, 1, 1>
 {
   public:
     LIR_HEADER(MathFunctionD)
     LMathFunctionD(const LAllocation& input, const LDefinition& temp) {
         setOperand(0, input);
         setTemp(0, temp);
     }
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1395,23 +1395,16 @@ LIRGenerator::visitPow(MPow* ins)
     } else {
         lir = new(alloc()) LPowD(useRegisterAtStart(input), useRegisterAtStart(power),
                                  tempFixed(CallTempReg0));
     }
     defineReturn(lir, ins);
 }
 
 void
-LIRGenerator::visitRandom(MRandom* ins)
-{
-    LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
-    defineReturn(lir, ins);
-}
-
-void
 LIRGenerator::visitMathFunction(MMathFunction* ins)
 {
     MOZ_ASSERT(IsFloatingPointType(ins->type()));
     MOZ_ASSERT(ins->type() == ins->input()->type());
 
     LInstruction* lir;
     if (ins->type() == MIRType_Double) {
         // Note: useRegisterAtStart is safe here, the temp is not a FP register.
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -125,17 +125,16 @@ class LIRGenerator : public LIRGenerator
     void visitRound(MRound* ins);
     void visitMinMax(MMinMax* ins);
     void visitAbs(MAbs* ins);
     void visitClz(MClz* ins);
     void visitSqrt(MSqrt* ins);
     void visitAtan2(MAtan2* ins);
     void visitHypot(MHypot* ins);
     void visitPow(MPow* ins);
-    void visitRandom(MRandom* ins);
     void visitMathFunction(MMathFunction* ins);
     void visitAdd(MAdd* ins);
     void visitSub(MSub* ins);
     void visitMul(MMul* ins);
     void visitDiv(MDiv* ins);
     void visitMod(MMod* ins);
     void visitConcat(MConcat* ins);
     void visitCharCodeAt(MCharCodeAt* ins);
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/arm/CodeGenerator-arm.h"
 
 #include "mozilla/MathAlgorithms.h"
 
 #include "jscntxt.h"
 #include "jscompartment.h"
+#include "jsmath.h"
 #include "jsnum.h"
 
 #include "jit/CodeGenerator.h"
 #include "jit/JitCompartment.h"
 #include "jit/JitFrames.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #include "js/Conversions.h"
@@ -2315,8 +2316,23 @@ CodeGeneratorARM::memoryBarrier(MemoryBa
         masm.ma_dmb();
 }
 
 void
 CodeGeneratorARM::visitMemoryBarrier(LMemoryBarrier* ins)
 {
     memoryBarrier(ins->type());
 }
+
+void
+CodeGeneratorARM::visitRandom(LRandom* ins)
+{
+    Register temp = ToRegister(ins->temp());
+    Register temp2 = ToRegister(ins->temp2());
+
+    masm.loadJSContext(temp);
+
+    masm.setupUnalignedABICall(1, temp2);
+    masm.passABIArg(temp);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
+
+    MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
+}
--- a/js/src/jit/arm/CodeGenerator-arm.h
+++ b/js/src/jit/arm/CodeGenerator-arm.h
@@ -211,16 +211,18 @@ class CodeGeneratorARM : public CodeGene
     void visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins);
     void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins);
     void visitAsmJSPassStackArg(LAsmJSPassStackArg* ins);
 
     void visitMemoryBarrier(LMemoryBarrier* ins);
 
     void generateInvalidateEpilogue();
 
+    void visitRandom(LRandom* ins);
+
   protected:
     void visitEffectiveAddress(LEffectiveAddress* ins);
     void visitUDiv(LUDiv* ins);
     void visitUMod(LUMod* ins);
     void visitSoftUDivOrMod(LSoftUDivOrMod* ins);
 
   public:
     // Unimplemented SIMD instructions
--- a/js/src/jit/arm/LIR-arm.h
+++ b/js/src/jit/arm/LIR-arm.h
@@ -507,12 +507,29 @@ class LAsmJSAtomicBinopCallout : public 
         return getOperand(1);
     }
 
     const MAsmJSAtomicBinopHeap* mir() const {
         return mir_->toAsmJSAtomicBinopHeap();
     }
 };
 
+// Math.random().
+class LRandom : public LCallInstructionHelper<1, 0, 2>
+{
+  public:
+    LIR_HEADER(Random)
+    LRandom(const LDefinition& temp, const LDefinition& temp2) {
+        setTemp(0, temp);
+        setTemp(1, temp2);
+    }
+    const LDefinition* temp() {
+        return getTemp(0);
+    }
+    const LDefinition* temp2() {
+        return getTemp(1);
+    }
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm_LIR_arm_h */
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -710,8 +710,15 @@ LIRGeneratorARM::visitSubstr(MSubstr* in
                                          useRegister(ins->begin()),
                                          useRegister(ins->length()),
                                          temp(),
                                          temp(),
                                          tempByteOpRegister());
     define(lir, ins);
     assignSafepoint(lir, ins);
 }
+
+void
+LIRGeneratorARM::visitRandom(MRandom* ins)
+{
+    LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
+    defineReturn(lir, ins);
+}
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -100,16 +100,17 @@ class LIRGeneratorARM : public LIRGenera
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
     void visitSimdBinaryArith(MSimdBinaryArith* ins);
     void visitSimdSelect(MSimdSelect* ins);
     void visitSimdSplatX4(MSimdSplatX4* ins);
     void visitSimdValueX4(MSimdValueX4* ins);
     void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins);
     void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
     void visitSubstr(MSubstr* ins);
+    void visitRandom(MRandom* ins);
 };
 
 typedef LIRGeneratorARM LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_arm_Lowering_arm_h */
--- a/js/src/jit/mips/CodeGenerator-mips.cpp
+++ b/js/src/jit/mips/CodeGenerator-mips.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/mips/CodeGenerator-mips.h"
 
 #include "mozilla/MathAlgorithms.h"
 
 #include "jscntxt.h"
 #include "jscompartment.h"
+#include "jsmath.h"
 #include "jsnum.h"
 
 #include "jit/CodeGenerator.h"
 #include "jit/JitCompartment.h"
 #include "jit/JitFrames.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #include "js/Conversions.h"
@@ -2147,8 +2148,23 @@ CodeGeneratorMIPS::visitNegD(LNegD* ins)
 void
 CodeGeneratorMIPS::visitNegF(LNegF* ins)
 {
     FloatRegister input = ToFloatRegister(ins->input());
     FloatRegister output = ToFloatRegister(ins->output());
 
     masm.as_negs(output, input);
 }
+
+void
+CodeGeneratorMIPS::visitRandom(LRandom* ins)
+{
+    Register temp = ToRegister(ins->temp());
+    Register temp2 = ToRegister(ins->temp2());
+
+    masm.loadJSContext(temp);
+
+    masm.setupUnalignedABICall(1, temp2);
+    masm.passABIArg(temp);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
+
+    MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
+}
--- a/js/src/jit/mips/CodeGenerator-mips.h
+++ b/js/src/jit/mips/CodeGenerator-mips.h
@@ -240,16 +240,18 @@ class CodeGeneratorMIPS : public CodeGen
     void visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins);
     void visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins);
     void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins);
 
     void visitAsmJSPassStackArg(LAsmJSPassStackArg* ins);
 
     void generateInvalidateEpilogue();
 
+    void visitRandom(LRandom* ins);
+
   protected:
     void visitEffectiveAddress(LEffectiveAddress* ins);
     void visitUDivOrMod(LUDivOrMod* ins);
 
   public:
     // Unimplemented SIMD instructions
     void visitSimdSplatX4(LSimdSplatX4* lir) { MOZ_CRASH("NYI"); }
     void visitInt32x4(LInt32x4* ins) { MOZ_CRASH("NYI"); }
--- a/js/src/jit/mips/LIR-mips.h
+++ b/js/src/jit/mips/LIR-mips.h
@@ -382,12 +382,29 @@ class LAsmJSLoadFuncPtr : public LInstru
     const MAsmJSLoadFuncPtr* mir() const {
         return mir_->toAsmJSLoadFuncPtr();
     }
     const LAllocation* index() {
         return getOperand(0);
     }
 };
 
+// Math.random().
+class LRandom : public LCallInstructionHelper<1, 0, 2>
+{
+  public:
+    LIR_HEADER(Random)
+    LRandom(const LDefinition& temp, const LDefinition& temp2) {
+        setTemp(0, temp);
+        setTemp(1, temp2);
+    }
+    const LDefinition* temp() {
+        return getTemp(0);
+    }
+    const LDefinition* temp2() {
+        return getTemp(1);
+    }
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_mips_LIR_mips_h */
--- a/js/src/jit/mips/Lowering-mips.cpp
+++ b/js/src/jit/mips/Lowering-mips.cpp
@@ -579,8 +579,15 @@ LIRGeneratorMIPS::visitAsmJSAtomicBinopH
     MOZ_CRASH("NYI");
 }
 
 void
 LIRGeneratorMIPS::visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins)
 {
     MOZ_CRASH("NYI");
 }
+
+void
+LIRGeneratorMIPS::visitRandom(MRandom* ins)
+{
+    LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
+    defineReturn(lir, ins);
+}
--- a/js/src/jit/mips/Lowering-mips.h
+++ b/js/src/jit/mips/Lowering-mips.h
@@ -100,16 +100,17 @@ class LIRGeneratorMIPS : public LIRGener
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
     void visitSimdBinaryArith(MSimdBinaryArith* ins);
     void visitSimdSelect(MSimdSelect* ins);
     void visitSimdSplatX4(MSimdSplatX4* ins);
     void visitSimdValueX4(MSimdValueX4* ins);
     void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins);
     void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
     void visitSubstr(MSubstr* ins);
+    void visitRandom(MRandom* ins);
 };
 
 typedef LIRGeneratorMIPS LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_mips_Lowering_mips_h */
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -560,16 +560,23 @@ class Assembler : public AssemblerX86Sha
     }
     void xorq(Register src, Register dest) {
         masm.xorq_rr(src.encoding(), dest.encoding());
     }
     void xorq(Imm32 imm, Register dest) {
         masm.xorq_ir(imm.value, dest.encoding());
     }
 
+    void imulq(Register src, Register dest) {
+        masm.imulq_rr(src.encoding(), dest.encoding());
+    }
+    void vcvtsi2sdq(Register src, FloatRegister dest) {
+        masm.vcvtsi2sdq_rr(src.encoding(), dest.encoding());
+    }
+
     void mov(ImmWord word, Register dest) {
         // Use xor for setting registers to zero, as it is specially optimized
         // for this purpose on modern hardware. Note that it does clobber FLAGS
         // though. Use xorl instead of xorq since they are functionally
         // equivalent (32-bit instructions zero-extend their results to 64 bits)
         // and xorl has a smaller encoding.
         if (word.value == 0)
             xorl(dest, dest);
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -814,8 +814,134 @@ CodeGeneratorX64::visitTruncateFToInt32(
     FloatRegister input = ToFloatRegister(ins->input());
     Register output = ToRegister(ins->output());
 
     // On x64, branchTruncateFloat32 uses vcvttss2sq. Unlike the x86
     // implementation, this should handle most floats and we can just
     // call a stub if it fails.
     emitTruncateFloat32(input, output, ins->mir());
 }
+
+namespace js {
+namespace jit {
+
+// Out-of-line math_random_no_outparam call for LRandom.
+class OutOfLineRandom : public OutOfLineCodeBase<CodeGeneratorX64>
+{
+    LRandom* lir_;
+
+  public:
+    explicit OutOfLineRandom(LRandom* lir)
+      : lir_(lir)
+    { }
+
+    void accept(CodeGeneratorX64* codegen) {
+        codegen->visitOutOfLineRandom(this);
+    }
+
+    LRandom* lir() const {
+        return lir_;
+    }
+};
+
+} // namespace jit
+} // namespace js
+
+static const double RNG_DSCALE_INV = 1 / RNG_DSCALE;
+
+void
+CodeGeneratorX64::visitRandom(LRandom* ins)
+{
+    FloatRegister output = ToFloatRegister(ins->output());
+
+    Register JSCompartmentReg = ToRegister(ins->temp());
+    Register rngStateReg = ToRegister(ins->temp2());
+    Register highReg = ToRegister(ins->temp3());
+    Register lowReg = ToRegister(ins->temp4());
+    Register rngMaskReg = ToRegister(ins->temp5());
+
+    // rngState = cx->compartment()->rngState
+    masm.loadJSContext(JSCompartmentReg);
+    masm.loadPtr(Address(JSCompartmentReg, JSContext::offsetOfCompartment()), JSCompartmentReg);
+    masm.loadPtr(Address(JSCompartmentReg, JSCompartment::offsetOfRngState()), rngStateReg);
+
+    // if rngState == 0, escape from inlined code and call
+    // math_random_no_outparam.
+    OutOfLineRandom* ool = new(alloc()) OutOfLineRandom(ins);
+    addOutOfLineCode(ool, ins->mir());
+    masm.branchTestPtr(Assembler::Zero, rngStateReg, rngStateReg, ool->entry());
+
+    // nextstate = rngState * RNG_MULTIPLIER;
+    Register& rngMultiplierReg = lowReg;
+    masm.movq(ImmWord(RNG_MULTIPLIER), rngMultiplierReg);
+    masm.imulq(rngMultiplierReg, rngStateReg);
+
+    // nextstate += RNG_ADDEND;
+    masm.addq(Imm32(RNG_ADDEND), rngStateReg);
+
+    // nextstate &= RNG_MASK;
+    masm.movq(ImmWord(RNG_MASK), rngMaskReg);
+    masm.andq(rngMaskReg, rngStateReg);
+
+    // rngState = nextstate
+
+    // if rngState == 0, escape from inlined code and call
+    // math_random_no_outparam.
+    masm.j(Assembler::Zero, ool->entry());
+
+    // high = (nextstate >> (RNG_STATE_WIDTH - RNG_HIGH_BITS)) << RNG_LOW_BITS;
+    masm.movq(rngStateReg, highReg);
+    masm.shrq(Imm32(RNG_STATE_WIDTH - RNG_HIGH_BITS), highReg);
+    masm.shlq(Imm32(RNG_LOW_BITS), highReg);
+
+    // nextstate = rngState * RNG_MULTIPLIER;
+    masm.imulq(rngMultiplierReg, rngStateReg);
+
+    // nextstate += RNG_ADDEND;
+    masm.addq(Imm32(RNG_ADDEND), rngStateReg);
+
+    // nextstate &= RNG_MASK;
+    masm.andq(rngMaskReg, rngStateReg);
+
+    // low = nextstate >> (RNG_STATE_WIDTH - RNG_LOW_BITS);
+    masm.movq(rngStateReg, lowReg);
+    masm.shrq(Imm32(RNG_STATE_WIDTH - RNG_LOW_BITS), lowReg);
+
+    // output = double(high | low);
+    masm.orq(highReg, lowReg);
+    masm.vcvtsi2sdq(lowReg, output);
+
+    // output = output * RNG_DSCALE_INV;
+    Register& rngDscaleInvReg = lowReg;
+    masm.movq(ImmPtr(&RNG_DSCALE_INV), rngDscaleInvReg);
+    masm.vmulsd(Operand(rngDscaleInvReg, 0), output, output);
+
+    // cx->compartment()->rngState = nextstate
+    masm.storePtr(rngStateReg, Address(JSCompartmentReg, JSCompartment::offsetOfRngState()));
+
+    masm.bind(ool->rejoin());
+}
+
+void
+CodeGeneratorX64::visitOutOfLineRandom(OutOfLineRandom* ool)
+{
+    LRandom* ins = ool->lir();
+    Register temp = ToRegister(ins->temp());
+    Register temp2 = ToRegister(ins->temp2());
+    MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
+
+    LiveRegisterSet regs;
+    regs.add(ReturnFloat32Reg);
+    regs.add(ReturnDoubleReg);
+    regs.add(ReturnInt32x4Reg);
+    regs.add(ReturnFloat32x4Reg);
+    saveVolatile(regs);
+
+    masm.loadJSContext(temp);
+
+    masm.setupUnalignedABICall(1, temp2);
+    masm.passABIArg(temp);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
+
+    restoreVolatile(regs);
+
+    masm.jump(ool->rejoin());
+}
--- a/js/src/jit/x64/CodeGenerator-x64.h
+++ b/js/src/jit/x64/CodeGenerator-x64.h
@@ -7,16 +7,18 @@
 #ifndef jit_x64_CodeGenerator_x64_h
 #define jit_x64_CodeGenerator_x64_h
 
 #include "jit/x86-shared/CodeGenerator-x86-shared.h"
 
 namespace js {
 namespace jit {
 
+class OutOfLineRandom;
+
 class CodeGeneratorX64 : public CodeGeneratorX86Shared
 {
     CodeGeneratorX64* thisFromCtor() {
         return this;
     }
 
   protected:
     ValueOperand ToValue(LInstruction* ins, size_t pos);
@@ -53,16 +55,18 @@ class CodeGeneratorX64 : public CodeGene
     void visitAsmJSAtomicBinopHeap(LAsmJSAtomicBinopHeap* ins);
     void visitAsmJSAtomicBinopHeapForEffect(LAsmJSAtomicBinopHeapForEffect* ins);
     void visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins);
     void visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins);
     void visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins);
     void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins);
     void visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir);
     void visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir);
+    void visitRandom(LRandom* ins);
+    void visitOutOfLineRandom(OutOfLineRandom* ool);
 };
 
 typedef CodeGeneratorX64 CodeGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x64_CodeGenerator_x64_h */
--- a/js/src/jit/x64/LIR-x64.h
+++ b/js/src/jit/x64/LIR-x64.h
@@ -116,12 +116,47 @@ class LAsmJSLoadFuncPtr : public LInstru
     const LAllocation* index() {
         return getOperand(0);
     }
     const LDefinition* temp() {
         return getTemp(0);
     }
 };
 
+// Math.random().
+class LRandom : public LInstructionHelper<1, 0, 5>
+{
+  public:
+    LIR_HEADER(Random)
+    LRandom(const LDefinition &temp, const LDefinition &temp2, const LDefinition &temp3,
+            const LDefinition &temp4, const LDefinition &temp5)
+    {
+        setTemp(0, temp);
+        setTemp(1, temp2);
+        setTemp(2, temp3);
+        setTemp(3, temp4);
+        setTemp(4, temp5);
+    }
+    const LDefinition* temp() {
+        return getTemp(0);
+    }
+    const LDefinition* temp2() {
+        return getTemp(1);
+    }
+    const LDefinition *temp3() {
+        return getTemp(2);
+    }
+    const LDefinition *temp4() {
+        return getTemp(3);
+    }
+    const LDefinition *temp5() {
+        return getTemp(4);
+    }
+
+    MRandom* mir() const {
+        return mir_->toRandom();
+    }
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x64_LIR_x64_h */
--- a/js/src/jit/x64/Lowering-x64.cpp
+++ b/js/src/jit/x64/Lowering-x64.cpp
@@ -304,8 +304,19 @@ LIRGeneratorX64::visitSubstr(MSubstr* in
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGeneratorX64::visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins)
 {
     MOZ_CRASH("NYI");
 }
+
+void
+LIRGeneratorX64::visitRandom(MRandom* ins)
+{
+    LRandom *lir = new(alloc()) LRandom(temp(),
+                                        temp(),
+                                        temp(),
+                                        temp(),
+                                        temp());
+    defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
+}
--- a/js/src/jit/x64/Lowering-x64.h
+++ b/js/src/jit/x64/Lowering-x64.h
@@ -46,16 +46,17 @@ class LIRGeneratorX64 : public LIRGenera
     void visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32* ins);
     void visitAsmJSLoadHeap(MAsmJSLoadHeap* ins);
     void visitAsmJSStoreHeap(MAsmJSStoreHeap* ins);
     void visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr* ins);
     void visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins);
     void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins);
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
     void visitSubstr(MSubstr* ins);
+    void visitRandom(MRandom* ins);
 };
 
 typedef LIRGeneratorX64 LIRGeneratorSpecific;
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x64_Lowering_x64_h */
--- a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h
+++ b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h
@@ -1306,16 +1306,24 @@ public:
             m_formatter.oneByteOp(OP_IMUL_GvEvIb, src, dst);
             m_formatter.immediate8s(value);
         } else {
             m_formatter.oneByteOp(OP_IMUL_GvEvIz, src, dst);
             m_formatter.immediate32(value);
         }
     }
 
+#ifdef JS_CODEGEN_X64
+    void imulq_rr(RegisterID src, RegisterID dst)
+    {
+        spew("imulq      %s, %s", GPReg64Name(src), GPReg64Name(dst));
+        m_formatter.twoByteOp64(OP2_IMUL_GvEv, src, dst);
+    }
+#endif
+
     void idivl_r(RegisterID divisor)
     {
         spew("idivl      %s", GPReg32Name(divisor));
         m_formatter.oneByteOp(OP_GROUP3_Ev, divisor, GROUP3_OP_IDIV);
     }
 
     void divl_r(RegisterID divisor)
     {
@@ -2766,16 +2774,23 @@ public:
 
 #ifdef JS_CODEGEN_X86
     void vcvtsi2sd_mr(const void* address, XMMRegisterID src0, XMMRegisterID dst)
     {
         twoByteOpSimd("vcvtsi2sd", VEX_SD, OP2_CVTSI2SD_VsdEd, address, src0, dst);
     }
 #endif
 
+#ifdef JS_CODEGEN_X64
+    void vcvtsi2sdq_rr(RegisterID src, XMMRegisterID dst)
+    {
+        twoByteOpInt64Simd("vcvtsi2sdq", VEX_SD, OP2_CVTSI2SD_VsdEd, src, invalid_xmm, dst);
+    }
+#endif
+
     void vcvttsd2si_rr(XMMRegisterID src, RegisterID dst)
     {
         twoByteOpSimdInt32("vcvttsd2si", VEX_SD, OP2_CVTTSD2SI_GdWsd, src, dst);
     }
 
     void vcvttss2si_rr(XMMRegisterID src, RegisterID dst)
     {
         twoByteOpSimdInt32("vcvttss2si", VEX_SS, OP2_CVTTSD2SI_GdWsd, src, dst);
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/x86/CodeGenerator-x86.h"
 
 #include "mozilla/Casting.h"
 #include "mozilla/DebugOnly.h"
 
+#include "jsmath.h"
 #include "jsnum.h"
 
 #include "jit/IonCaches.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #include "js/Conversions.h"
 #include "vm/Shape.h"
 
@@ -1107,8 +1108,23 @@ CodeGeneratorX86::visitOutOfLineTruncate
         masm.storeCallResult(output);
         masm.pop(input);
 
         restoreVolatile(output);
     }
 
     masm.jump(ool->rejoin());
 }
+
+void
+CodeGeneratorX86::visitRandom(LRandom* ins)
+{
+    Register temp = ToRegister(ins->temp());
+    Register temp2 = ToRegister(ins->temp2());
+
+    masm.loadJSContext(temp);
+
+    masm.setupUnalignedABICall(1, temp2);
+    masm.passABIArg(temp);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, math_random_no_outparam), MoveOp::DOUBLE);
+
+    MOZ_ASSERT(ToFloatRegister(ins->output()) == ReturnDoubleReg);
+}
--- a/js/src/jit/x86/CodeGenerator-x86.h
+++ b/js/src/jit/x86/CodeGenerator-x86.h
@@ -66,16 +66,18 @@ class CodeGeneratorX86 : public CodeGene
     void visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins);
     void visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins);
     void visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr* ins);
     void visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc* ins);
 
     void visitOutOfLineTruncate(OutOfLineTruncate* ool);
     void visitOutOfLineTruncateFloat32(OutOfLineTruncateFloat32* ool);
 
+    void visitRandom(LRandom* ins);
+
   private:
     void asmJSAtomicComputeAddress(Register addrTemp, Register ptrReg, bool boundsCheck,
                                    int32_t offset, int32_t endOffset, Register out,
                                    Label& rejoin);
 };
 
 typedef CodeGeneratorX86 CodeGeneratorSpecific;
 
--- a/js/src/jit/x86/LIR-x86.h
+++ b/js/src/jit/x86/LIR-x86.h
@@ -138,12 +138,29 @@ class LAsmJSLoadFuncPtr : public LInstru
     MAsmJSLoadFuncPtr* mir() const {
         return mir_->toAsmJSLoadFuncPtr();
     }
     const LAllocation* index() {
         return getOperand(0);
     }
 };
 
+// Math.random().
+class LRandom : public LCallInstructionHelper<1, 0, 2>
+{
+  public:
+    LIR_HEADER(Random)
+    LRandom(const LDefinition& temp, const LDefinition& temp2) {
+        setTemp(0, temp);
+        setTemp(1, temp2);
+    }
+    const LDefinition* temp() {
+        return getTemp(0);
+    }
+    const LDefinition* temp2() {
+        return getTemp(1);
+    }
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x86_LIR_x86_h */
--- a/js/src/jit/x86/Lowering-x86.cpp
+++ b/js/src/jit/x86/Lowering-x86.cpp
@@ -408,8 +408,15 @@ LIRGeneratorX86::visitSubstr(MSubstr* in
                                          useRegister(ins->begin()),
                                          useRegister(ins->length()),
                                          temp(),
                                          LDefinition::BogusTemp(),
                                          tempByteOpRegister());
     define(lir, ins);
     assignSafepoint(lir, ins);
 }
+
+void
+LIRGeneratorX86::visitRandom(MRandom* ins)
+{
+    LRandom* lir = new(alloc()) LRandom(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
+    defineReturn(lir, ins);
+}
--- a/js/src/jit/x86/Lowering-x86.h
+++ b/js/src/jit/x86/Lowering-x86.h
@@ -52,16 +52,17 @@ class LIRGeneratorX86 : public LIRGenera
     void visitAsmJSUnsignedToFloat32(MAsmJSUnsignedToFloat32* ins);
     void visitAsmJSLoadHeap(MAsmJSLoadHeap* ins);
     void visitAsmJSStoreHeap(MAsmJSStoreHeap* ins);
     void visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr* ins);
     void visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins);
     void visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins);
     void visitStoreTypedArrayElementStatic(MStoreTypedArrayElementStatic* ins);
     void visitSubstr(MSubstr* ins);
+    void visitRandom(MRandom* ins);
     void lowerPhi(MPhi* phi);
 
     static bool allowTypedElementHoleCheck() {
         return true;
     }
 
     static bool allowStaticTypedArrayAccesses() {
         return true;
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -482,16 +482,18 @@ MSG_DEF(JSMSG_SYMBOL_TO_STRING,        0
 MSG_DEF(JSMSG_SYMBOL_TO_NUMBER,        0, JSEXN_TYPEERR, "can't convert symbol to number")
 
 // Atomics and futexes
 MSG_DEF(JSMSG_ATOMICS_BAD_ARRAY,         0, JSEXN_TYPEERR, "invalid array type for the operation")
 MSG_DEF(JSMSG_ATOMICS_TOO_LONG,          0, JSEXN_RANGEERR, "timeout value too large")
 MSG_DEF(JSMSG_ATOMICS_WAIT_NOT_ALLOWED,  0, JSEXN_ERR, "waiting is not allowed on this thread")
 
 // XPConnect wrappers and DOM bindings
-MSG_DEF(JSMSG_CANT_SET_INTERPOSED,      1, JSEXN_TYPEERR, "unable to set interposed data property '{0}'")
+MSG_DEF(JSMSG_CANT_SET_INTERPOSED,       1, JSEXN_TYPEERR, "unable to set interposed data property '{0}'")
 MSG_DEF(JSMSG_CANT_DEFINE_WINDOW_ELEMENT, 0, JSEXN_TYPEERR, "can't define elements on a Window object")
 MSG_DEF(JSMSG_CANT_DELETE_WINDOW_ELEMENT, 0, JSEXN_TYPEERR, "can't delete elements from a Window object")
 MSG_DEF(JSMSG_CANT_DELETE_WINDOW_NAMED_PROPERTY, 1, JSEXN_TYPEERR, "can't delete property {0} from window's named properties object")
-MSG_DEF(JSMSG_CANT_PREVENT_EXTENSIONS,  0, JSEXN_TYPEERR, "can't prevent extensions on this proxy object")
+MSG_DEF(JSMSG_CANT_PREVENT_EXTENSIONS,   0, JSEXN_TYPEERR, "can't prevent extensions on this proxy object")
+MSG_DEF(JSMSG_NO_NAMED_SETTER,           2, JSEXN_TYPEERR, "{0} doesn't have a named property setter for '{1}'")
+MSG_DEF(JSMSG_NO_INDEXED_SETTER,         2, JSEXN_TYPEERR, "{0} doesn't have an indexed property setter for '{1}'")
 
 // Super
 MSG_DEF(JSMSG_CANT_DELETE_SUPER, 0, JSEXN_REFERENCEERR, "invalid delete involving 'super'")
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -125,22 +125,31 @@ JS::CallArgs::requireAtLeast(JSContext* 
                              fnname, numArgsStr, required == 2 ? "" : "s");
         return false;
     }
 
     return true;
 }
 
 static bool
-ErrorTakesIdArgument(unsigned msg)
+ErrorTakesArguments(unsigned msg)
 {
     MOZ_ASSERT(msg < JSErr_Limit);
     unsigned argCount = js_ErrorFormatString[msg].argCount;
-    MOZ_ASSERT(argCount <= 1);
-    return argCount == 1;
+    MOZ_ASSERT(argCount <= 2);
+    return argCount == 1 || argCount == 2;
+}
+
+static bool
+ErrorTakesObjectArgument(unsigned msg)
+{
+    MOZ_ASSERT(msg < JSErr_Limit);
+    unsigned argCount = js_ErrorFormatString[msg].argCount;
+    MOZ_ASSERT(argCount <= 2);
+    return argCount == 2;
 }
 
 JS_PUBLIC_API(bool)
 JS::ObjectOpResult::reportStrictErrorOrWarning(JSContext* cx, HandleObject obj, HandleId id,
                                                bool strict)
 {
     static_assert(unsigned(OkCode) == unsigned(JSMSG_NOT_AN_ERROR),
                   "unsigned value of OkCode must not be an error code");
@@ -148,38 +157,43 @@ JS::ObjectOpResult::reportStrictErrorOrW
     MOZ_ASSERT(!ok());
 
     unsigned flags = strict ? JSREPORT_ERROR : (JSREPORT_WARNING | JSREPORT_STRICT);
     if (code_ == JSMSG_OBJECT_NOT_EXTENSIBLE || code_ == JSMSG_SET_NON_OBJECT_RECEIVER) {
         RootedValue val(cx, ObjectValue(*obj));
         return ReportValueErrorFlags(cx, flags, code_, JSDVG_IGNORE_STACK, val,
                                      nullptr, nullptr, nullptr);
     }
-    if (ErrorTakesIdArgument(code_)) {
+    if (ErrorTakesArguments(code_)) {
         RootedValue idv(cx, IdToValue(id));
         RootedString str(cx, ValueToSource(cx, idv));
         if (!str)
             return false;
 
         JSAutoByteString propName(cx, str);
         if (!propName)
             return false;
 
+        if (ErrorTakesObjectArgument(code_)) {
+            return JS_ReportErrorFlagsAndNumber(cx, flags, GetErrorMessage, nullptr, code_,
+                                                obj->getClass()->name, propName.ptr());
+        }
+
         return JS_ReportErrorFlagsAndNumber(cx, flags, GetErrorMessage, nullptr, code_,
                                             propName.ptr());
     }
     return JS_ReportErrorFlagsAndNumber(cx, flags, GetErrorMessage, nullptr, code_);
 }
 
 JS_PUBLIC_API(bool)
 JS::ObjectOpResult::reportStrictErrorOrWarning(JSContext* cx, HandleObject obj, bool strict)
 {
     MOZ_ASSERT(code_ != Uninitialized);
     MOZ_ASSERT(!ok());
-    MOZ_ASSERT(!ErrorTakesIdArgument(code_));
+    MOZ_ASSERT(!ErrorTakesArguments(code_));
 
     unsigned flags = strict ? JSREPORT_ERROR : (JSREPORT_WARNING | JSREPORT_STRICT);
     return JS_ReportErrorFlagsAndNumber(cx, flags, GetErrorMessage, nullptr, code_);
 }
 
 JS_PUBLIC_API(bool)
 JS::ObjectOpResult::failCantRedefineProp()
 {
@@ -229,16 +243,28 @@ JS::ObjectOpResult::failCantDeleteWindow
 }
 
 JS_PUBLIC_API(bool)
 JS::ObjectOpResult::failCantPreventExtensions()
 {
     return fail(JSMSG_CANT_PREVENT_EXTENSIONS);
 }
 
+JS_PUBLIC_API(bool)
+JS::ObjectOpResult::failNoNamedSetter()
+{
+    return fail(JSMSG_NO_NAMED_SETTER);
+}
+
+JS_PUBLIC_API(bool)
+JS::ObjectOpResult::failNoIndexedSetter()
+{
+    return fail(JSMSG_NO_INDEXED_SETTER);
+}
+
 JS_PUBLIC_API(int64_t)
 JS_Now()
 {
     return PRMJ_Now();
 }
 
 JS_PUBLIC_API(jsval)
 JS_GetNaNValue(JSContext* cx)
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -287,16 +287,19 @@ struct JSContext : public js::ExclusiveC
     ~JSContext();
 
     JSRuntime* runtime() const { return runtime_; }
     js::PerThreadData& mainThread() const { return runtime()->mainThread; }
 
     static size_t offsetOfRuntime() {
         return offsetof(JSContext, runtime_);
     }
+    static size_t offsetOfCompartment() {
+        return offsetof(JSContext, compartment_);
+    }
 
     friend class js::ExclusiveContext;
     friend class JS::AutoSaveExceptionState;
     friend class js::jit::DebugModeOSRVolatileJitFrameIterator;
     friend void js::ReportOverRecursed(JSContext*);
 
   private:
     /* Exception state -- the exception member is a GC root by definition. */
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -450,16 +450,20 @@ struct JSCompartment
 
     void findOutgoingEdges(js::gc::ComponentFinder<JS::Zone>& finder);
 
     js::DtoaCache dtoaCache;
 
     /* Random number generator state, used by jsmath.cpp. */
     uint64_t rngState;
 
+    static size_t offsetOfRngState() {
+        return offsetof(JSCompartment, rngState);
+    }
+
   private:
     JSCompartment* thisForCtor() { return this; }
 
   public:
     //
     // The Debugger observes execution on a frame-by-frame basis. The
     // invariants of JSCompartment's debug mode bits, JSScript::isDebuggee,
     // InterpreterFrame::isDebuggee, and BaselineFrame::isDebuggee are
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -304,24 +304,16 @@ js::IsAtomsCompartment(JSCompartment* co
 
 JS_FRIEND_API(bool)
 js::IsAtomsZone(JS::Zone* zone)
 {
     return zone->runtimeFromAnyThread()->isAtomsZone(zone);
 }
 
 JS_FRIEND_API(bool)
-js::IsInNonStrictPropertySet(JSContext* cx)
-{
-    jsbytecode* pc;
-    JSScript* script = cx->currentScript(&pc, JSContext::ALLOW_CROSS_COMPARTMENT);
-    return script && !IsStrictSetPC(pc) && (js_CodeSpec[*pc].format & JOF_SET);
-}
-
-JS_FRIEND_API(bool)
 js::IsFunctionObject(JSObject* obj)
 {
     return obj->is<JSFunction>();
 }
 
 JS_FRIEND_API(bool)
 js::IsScopeObject(JSObject* obj)
 {
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -476,25 +476,16 @@ extern JS_FRIEND_API(bool)
 IsSystemZone(JS::Zone* zone);
 
 extern JS_FRIEND_API(bool)
 IsAtomsCompartment(JSCompartment* comp);
 
 extern JS_FRIEND_API(bool)
 IsAtomsZone(JS::Zone* zone);
 
-/*
- * Returns whether we're in a non-strict property set (in that we're in a
- * non-strict script and the bytecode we're on is a property set).  The return
- * value does NOT indicate any sort of exception was thrown: it's just a
- * boolean.
- */
-extern JS_FRIEND_API(bool)
-IsInNonStrictPropertySet(JSContext* cx);
-
 struct WeakMapTracer
 {
     JSRuntime* runtime;
 
     explicit WeakMapTracer(JSRuntime* rt) : runtime(rt) {}
 
     // Weak map tracer callback, called once for every binding of every
     // weak map that was live at the time of the last garbage collection.
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -760,47 +760,43 @@ random_generateSeed()
 #else
 # error "Platform needs to implement random_generateSeed()"
 #endif
 
     seed.u64 ^= PRMJ_Now();
     return seed.u64;
 }
 
-static const uint64_t RNG_MULTIPLIER = 0x5DEECE66DLL;
-static const uint64_t RNG_ADDEND = 0xBLL;
-static const uint64_t RNG_MASK = (1LL << 48) - 1;
-
 /*
  * Math.random() support, lifted from java.util.Random.java.
  */
 void
 js::random_initState(uint64_t* rngState)
 {
     /* Our PRNG only uses 48 bits, so squeeze our entropy into those bits. */
     uint64_t seed = random_generateSeed();
     seed ^= (seed >> 16);
     *rngState = (seed ^ RNG_MULTIPLIER) & RNG_MASK;
 }
 
 uint64_t
 js::random_next(uint64_t* rngState, int bits)
 {
     MOZ_ASSERT((*rngState & 0xffff000000000000ULL) == 0, "Bad rngState");
-    MOZ_ASSERT(bits > 0 && bits <= 48, "bits is out of range");
+    MOZ_ASSERT(bits > 0 && bits <= RNG_STATE_WIDTH, "bits is out of range");
 
     if (*rngState == 0) {
         random_initState(rngState);
     }
 
     uint64_t nextstate = *rngState * RNG_MULTIPLIER;
     nextstate += RNG_ADDEND;
     nextstate &= RNG_MASK;
     *rngState = nextstate;
-    return nextstate >> (48 - bits);
+    return nextstate >> (RNG_STATE_WIDTH - bits);
 }
 
 double
 js::math_random_no_outparam(JSContext* cx)
 {
     /* Calculate random without memory traffic, for use in the JITs. */
     return random_nextDouble(&cx->compartment()->rngState);
 }
--- a/js/src/jsmath.h
+++ b/js/src/jsmath.h
@@ -92,21 +92,28 @@ InitMathClass(JSContext* cx, HandleObjec
 
 extern void
 random_initState(uint64_t* rngState);
 
 extern uint64_t
 random_next(uint64_t* rngState, int bits);
 
 static const double RNG_DSCALE = double(1LL << 53);
+static const int RNG_STATE_WIDTH = 48;
+static const int RNG_HIGH_BITS = 26;
+static const int RNG_LOW_BITS = 27;
+static const uint64_t RNG_MULTIPLIER = 0x5DEECE66DLL;
+static const uint64_t RNG_ADDEND = 0xBLL;
+static const uint64_t RNG_MASK = (1LL << RNG_STATE_WIDTH) - 1;
 
 inline double
 random_nextDouble(uint64_t* rng)
 {
-    return double((random_next(rng, 26) << 27) + random_next(rng, 27)) / RNG_DSCALE;
+    return double((random_next(rng, RNG_HIGH_BITS) << RNG_LOW_BITS) +
+                  random_next(rng, RNG_LOW_BITS)) / RNG_DSCALE;
 }
 
 extern double
 math_random_no_outparam(JSContext* cx);
 
 extern bool
 math_random(JSContext* cx, unsigned argc, js::Value* vp);
 
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -1905,31 +1905,31 @@ ASTSerializer::aop(JSOp op)
       default:
         return AOP_ERR;
     }
 }
 
 UnaryOperator
 ASTSerializer::unop(ParseNodeKind kind, JSOp op)
 {
-    if (kind == PNK_DELETE)
+    if (IsDeleteKind(kind))
         return UNOP_DELETE;
 
+    if (kind == PNK_TYPEOFNAME || kind == PNK_TYPEOFEXPR)
+        return UNOP_TYPEOF;
+
     switch (op) {
       case JSOP_NEG:
         return UNOP_NEG;
       case JSOP_POS:
         return UNOP_POS;
       case JSOP_NOT:
         return UNOP_NOT;
       case JSOP_BITNOT:
         return UNOP_BITNOT;
-      case JSOP_TYPEOF:
-      case JSOP_TYPEOFEXPR:
-        return UNOP_TYPEOF;
       case JSOP_VOID:
         return UNOP_VOID;
       default:
         return UNOP_ERR;
     }
 }
 
 BinaryOperator
@@ -2921,18 +2921,24 @@ ASTSerializer::expression(ParseNode* pn,
       case PNK_MOD:
       case PNK_BITOR:
       case PNK_BITXOR:
       case PNK_BITAND:
       case PNK_IN:
       case PNK_INSTANCEOF:
         return leftAssociate(pn, dst);
 
-      case PNK_DELETE:
-      case PNK_TYPEOF:
+      case PNK_DELETENAME:
+      case PNK_DELETEPROP:
+      case PNK_DELETESUPERPROP:
+      case PNK_DELETEELEM:
+      case PNK_DELETESUPERELEM:
+      case PNK_DELETEEXPR:
+      case PNK_TYPEOFNAME:
+      case PNK_TYPEOFEXPR:
       case PNK_VOID:
       case PNK_NOT:
       case PNK_BITNOT:
       case PNK_POS:
       case PNK_NEG: {
         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos));
 
         UnaryOperator op = unop(pn->getKind(), pn->getOp());
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -200,20 +200,20 @@ class JS_FRIEND_API(OpaqueCrossCompartme
 
     /* SpiderMonkey extensions. */
     virtual bool getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
                                        MutableHandle<JSPropertyDescriptor> desc) const override;
     virtual bool hasOwn(JSContext* cx, HandleObject wrapper, HandleId id,
                         bool* bp) const override;
     virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper,
                                               AutoIdVector& props) const override;
-    virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext* cx) const;
+    virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext* cx) const override;
     virtual const char* className(JSContext* cx, HandleObject wrapper) const override;
-    virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const;
-    virtual bool defaultValue(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp) const;
+    virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override;
+    virtual bool defaultValue(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp) const override;
 
     static const OpaqueCrossCompartmentWrapper singleton;
 };
 
 /*
  * Base class for security wrappers. A security wrapper is potentially hiding
  * all or part of some wrapped object thus SecurityWrapper defaults to denying
  * access to the wrappee. This is the opposite of Wrapper which tries to be
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -24,21 +24,21 @@ namespace js {
  * versions.  If deserialization fails, the data should be invalidated if
  * possible.
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 287;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 288;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
-static_assert(JSErr_Limit == 398,
+static_assert(JSErr_Limit == 400,
               "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
               "removed MSG_DEFs from js.msg, you should increment "
               "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
               "expected JSErr_Limit value.");
 
 class XDRBuffer {
   public:
     explicit XDRBuffer(JSContext* cx)
--- a/js/xpconnect/src/XPCMaps.cpp
+++ b/js/xpconnect/src/XPCMaps.cpp
@@ -169,17 +169,17 @@ JSObject2WrappedJSMap::SizeOfWrappedJS(m
 Native2WrappedNativeMap*
 Native2WrappedNativeMap::newMap(int length)
 {
     return new Native2WrappedNativeMap(length);
 }
 
 Native2WrappedNativeMap::Native2WrappedNativeMap(int length)
 {
-    mTable = new PLDHashTable2(PL_DHashGetStubOps(), sizeof(Entry), length);
+    mTable = new PLDHashTable(PL_DHashGetStubOps(), sizeof(Entry), length);
 }
 
 Native2WrappedNativeMap::~Native2WrappedNativeMap()
 {
     delete mTable;
 }
 
 size_t
@@ -217,17 +217,17 @@ IID2WrappedJSClassMap::newMap(int length
     if (map && map->mTable)
         return map;
     delete map;
     return nullptr;
 }
 
 IID2WrappedJSClassMap::IID2WrappedJSClassMap(int length)
 {
-    mTable = new PLDHashTable2(&Entry::sOps, sizeof(Entry), length);
+    mTable = new PLDHashTable(&Entry::sOps, sizeof(Entry), length);
 }
 
 IID2WrappedJSClassMap::~IID2WrappedJSClassMap()
 {
     delete mTable;
 }
 
 
@@ -250,17 +250,17 @@ IID2NativeInterfaceMap::newMap(int lengt
     if (map && map->mTable)
         return map;
     delete map;
     return nullptr;
 }
 
 IID2NativeInterfaceMap::IID2NativeInterfaceMap(int length)
 {
-    mTable = new PLDHashTable2(&Entry::sOps, sizeof(Entry), length);
+    mTable = new PLDHashTable(&Entry::sOps, sizeof(Entry), length);
 }
 
 IID2NativeInterfaceMap::~IID2NativeInterfaceMap()
 {
     delete mTable;
 }
 
 size_t
@@ -291,17 +291,17 @@ ClassInfo2NativeSetMap::newMap(int lengt
     if (map && map->mTable)
         return map;
     delete map;
     return nullptr;
 }
 
 ClassInfo2NativeSetMap::ClassInfo2NativeSetMap(int length)
 {
-    mTable = new PLDHashTable2(PL_DHashGetStubOps(), sizeof(Entry), length);
+    mTable = new PLDHashTable(PL_DHashGetStubOps(), sizeof(Entry), length);
 }
 
 ClassInfo2NativeSetMap::~ClassInfo2NativeSetMap()
 {
     delete mTable;
 }
 
 size_t
@@ -321,17 +321,17 @@ ClassInfo2NativeSetMap::ShallowSizeOfInc
 ClassInfo2WrappedNativeProtoMap*
 ClassInfo2WrappedNativeProtoMap::newMap(int length)
 {
     return new ClassInfo2WrappedNativeProtoMap(length);
 }
 
 ClassInfo2WrappedNativeProtoMap::ClassInfo2WrappedNativeProtoMap(int length)
 {
-    mTable = new PLDHashTable2(PL_DHashGetStubOps(), sizeof(Entry), length);
+    mTable = new PLDHashTable(PL_DHashGetStubOps(), sizeof(Entry), length);
 }
 
 ClassInfo2WrappedNativeProtoMap::~ClassInfo2WrappedNativeProtoMap()
 {
     delete mTable;
 }
 
 size_t
@@ -441,17 +441,17 @@ NativeSetMap::newMap(int length)
     if (map && map->mTable)
         return map;
     delete map;
     return nullptr;
 }
 
 NativeSetMap::NativeSetMap(int length)
 {
-    mTable = new PLDHashTable2(&Entry::sOps, sizeof(Entry), length);
+    mTable = new PLDHashTable(&Entry::sOps, sizeof(Entry), length);
 }
 
 NativeSetMap::~NativeSetMap()
 {
     delete mTable;
 }
 
 size_t
@@ -504,17 +504,17 @@ IID2ThisTranslatorMap::newMap(int length
     if (map && map->mTable)
         return map;
     delete map;
     return nullptr;
 }
 
 IID2ThisTranslatorMap::IID2ThisTranslatorMap(int length)
 {
-    mTable = new PLDHashTable2(&Entry::sOps, sizeof(Entry), length);
+    mTable = new PLDHashTable(&Entry::sOps, sizeof(Entry), length);
 }
 
 IID2ThisTranslatorMap::~IID2ThisTranslatorMap()
 {
     delete mTable;
 }
 
 /***************************************************************************/
@@ -580,17 +580,17 @@ XPCNativeScriptableSharedMap::newMap(int
     if (map && map->mTable)
         return map;
     delete map;
     return nullptr;
 }
 
 XPCNativeScriptableSharedMap::XPCNativeScriptableSharedMap(int length)
 {
-    mTable = new PLDHashTable2(&Entry::sOps, sizeof(Entry), length);
+    mTable = new PLDHashTable(&Entry::sOps, sizeof(Entry), length);
 }
 
 XPCNativeScriptableSharedMap::~XPCNativeScriptableSharedMap()
 {
     delete mTable;
 }
 
 bool
@@ -631,18 +631,18 @@ XPCWrappedNativeProtoMap::newMap(int len
     if (map && map->mTable)
         return map;
     delete map;
     return nullptr;
 }
 
 XPCWrappedNativeProtoMap::XPCWrappedNativeProtoMap(int length)
 {
-    mTable = new PLDHashTable2(PL_DHashGetStubOps(),
-                               sizeof(PLDHashEntryStub), length);
+    mTable = new PLDHashTable(PL_DHashGetStubOps(),
+                              sizeof(PLDHashEntryStub), length);
 }
 
 XPCWrappedNativeProtoMap::~XPCWrappedNativeProtoMap()
 {
     delete mTable;
 }
 
 /***************************************************************************/
--- a/js/xpconnect/src/XPCMaps.h
+++ b/js/xpconnect/src/XPCMaps.h
@@ -153,17 +153,17 @@ public:
     ~Native2WrappedNativeMap();
 private:
     Native2WrappedNativeMap();    // no implementation
     explicit Native2WrappedNativeMap(int size);
 
     static size_t SizeOfEntryExcludingThis(PLDHashEntryHdr* hdr, mozilla::MallocSizeOf mallocSizeOf, void*);
 
 private:
-    PLDHashTable2* mTable;
+    PLDHashTable* mTable;
 };
 
 /*************************/
 
 class IID2WrappedJSClassMap
 {
 public:
     struct Entry : public PLDHashEntryHdr
@@ -207,17 +207,17 @@ public:
     inline uint32_t Enumerate(PLDHashEnumerator f, void* arg)
         {return PL_DHashTableEnumerate(mTable, f, arg);}
 
     ~IID2WrappedJSClassMap();
 private:
     IID2WrappedJSClassMap();    // no implementation
     explicit IID2WrappedJSClassMap(int size);
 private:
-    PLDHashTable2* mTable;
+    PLDHashTable* mTable;
 };
 
 /*************************/
 
 class IID2NativeInterfaceMap
 {
 public:
     struct Entry : public PLDHashEntryHdr
@@ -266,17 +266,17 @@ public:
     ~IID2NativeInterfaceMap();
 private:
     IID2NativeInterfaceMap();    // no implementation
     explicit IID2NativeInterfaceMap(int size);
 
     static size_t SizeOfEntryExcludingThis(PLDHashEntryHdr* hdr, mozilla::MallocSizeOf mallocSizeOf, void*);
 
 private:
-    PLDHashTable2* mTable;
+    PLDHashTable* mTable;
 };
 
 /*************************/
 
 class ClassInfo2NativeSetMap
 {
 public:
     struct Entry : public PLDHashEntryHdr
@@ -323,17 +323,17 @@ public:
     // pointers to *all* XPCNativeSets).  Hence the "Shallow".
     size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 
     ~ClassInfo2NativeSetMap();
 private:
     ClassInfo2NativeSetMap();    // no implementation
     explicit ClassInfo2NativeSetMap(int size);
 private:
-    PLDHashTable2* mTable;
+    PLDHashTable* mTable;
 };
 
 /*************************/
 
 class ClassInfo2WrappedNativeProtoMap
 {
 public:
     struct Entry : public PLDHashEntryHdr
@@ -379,17 +379,17 @@ public:
     ~ClassInfo2WrappedNativeProtoMap();
 private:
     ClassInfo2WrappedNativeProtoMap();    // no implementation
     explicit ClassInfo2WrappedNativeProtoMap(int size);
 
     static size_t SizeOfEntryExcludingThis(PLDHashEntryHdr* hdr, mozilla::MallocSizeOf mallocSizeOf, void*);
 
 private:
-    PLDHashTable2* mTable;
+    PLDHashTable* mTable;
 };
 
 /*************************/
 
 class NativeSetMap
 {
 public:
     struct Entry : public PLDHashEntryHdr
@@ -449,17 +449,17 @@ public:
     ~NativeSetMap();
 private:
     NativeSetMap();    // no implementation
     explicit NativeSetMap(int size);
 
     static size_t SizeOfEntryExcludingThis(PLDHashEntryHdr* hdr, mozilla::MallocSizeOf mallocSizeOf, void*);
 
 private:
-    PLDHashTable2* mTable;
+    PLDHashTable* mTable;
 };
 
 /***************************************************************************/
 
 class IID2ThisTranslatorMap
 {
 public:
     struct Entry : public PLDHashEntryHdr
@@ -507,17 +507,17 @@ public:
     inline uint32_t Enumerate(PLDHashEnumerator f, void* arg)
         {return PL_DHashTableEnumerate(mTable, f, arg);}
 
     ~IID2ThisTranslatorMap();
 private:
     IID2ThisTranslatorMap();    // no implementation
     explicit IID2ThisTranslatorMap(int size);
 private:
-    PLDHashTable2* mTable;
+    PLDHashTable* mTable;
 };
 
 /***************************************************************************/
 
 class XPCNativeScriptableSharedMap
 {
 public:
     struct Entry : public PLDHashEntryHdr
@@ -543,17 +543,17 @@ public:
     inline uint32_t Enumerate(PLDHashEnumerator f, void* arg)
         {return PL_DHashTableEnumerate(mTable, f, arg);}
 
     ~XPCNativeScriptableSharedMap();
 private:
     XPCNativeScriptableSharedMap();    // no implementation
     explicit XPCNativeScriptableSharedMap(int size);
 private:
-    PLDHashTable2* mTable;
+    PLDHashTable* mTable;
 };
 
 /***************************************************************************/
 
 class XPCWrappedNativeProtoMap
 {
 public:
     static XPCWrappedNativeProtoMap* newMap(int length);
@@ -581,17 +581,17 @@ public:
     inline uint32_t Enumerate(PLDHashEnumerator f, void* arg)
         {return PL_DHashTableEnumerate(mTable, f, arg);}
 
     ~XPCWrappedNativeProtoMap();
 private:
     XPCWrappedNativeProtoMap();    // no implementation
     explicit XPCWrappedNativeProtoMap(int size);
 private:
-    PLDHashTable2* mTable;
+    PLDHashTable* mTable;
 };
 
 /***************************************************************************/
 
 class JSObject2JSObjectMap
 {
     typedef js::HashMap<JSObject*, JS::Heap<JSObject*>, js::PointerHasher<JSObject*, 3>,
                         js::SystemAllocPolicy> Map;
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -454,17 +454,17 @@ JSXrayTraits::resolveOwnProperty(JSConte
         return true;
     }
 
     // The non-HasPrototypes semantics implemented by traditional Xrays are kind
     // of broken with respect to |own|-ness and the holder. The common code
     // muddles through by only checking the holder for non-|own| lookups, but
     // that doesn't work for us. So we do an explicit holder check here, and hope
     // that this mess gets fixed up soon.
-    if (!JS_GetPropertyDescriptorById(cx, holder, id, desc))
+    if (!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
         return false;
     if (desc.object()) {
         desc.object().set(wrapper);
         return true;
     }
 
     // Handle the 'constructor' property.
     if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_CONSTRUCTOR)) {
@@ -516,23 +516,23 @@ JSXrayTraits::resolveOwnProperty(JSConte
         if (fsMatch->selfHostedName) {
             fun = JS::GetSelfHostedFunction(cx, fsMatch->selfHostedName, id, fsMatch->nargs);
         } else {
             fun = JS_NewFunctionById(cx, fsMatch->call.op, fsMatch->nargs, 0, id);
         }
         if (!fun)
             return false;
 
-        // The generic Xray machinery only defines non-own properties on the holder.
-        // This is broken, and will be fixed at some point, but for now we need to
-        // cache the value explicitly. See the corresponding call to
-        // JS_GetPropertyById at the top of this function.
+        // The generic Xray machinery only defines non-own properties of the target on
+        // the holder. This is broken, and will be fixed at some point, but for now we
+        // need to cache the value explicitly. See the corresponding call to
+        // JS_GetOwnPropertyDescriptorById at the top of this function.
         RootedObject funObj(cx, JS_GetFunctionObject(fun));
         return JS_DefinePropertyById(cx, holder, id, funObj, 0) &&
-               JS_GetPropertyDescriptorById(cx, holder, id, desc);
+               JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
     }
 
     // Scan through the properties.
     const JSPropertySpec* psMatch = nullptr;
     for (const JSPropertySpec* ps = clasp->spec.prototypeProperties(); ps && ps->name; ++ps) {
         if (PropertySpecNameEqualsId(ps->name, id)) {
             psMatch = ps;
             break;
@@ -575,17 +575,17 @@ JSXrayTraits::resolveOwnProperty(JSConte
                                      UndefinedHandleValue,
                                      // This particular descriptor, unlike most,
                                      // actually stores JSNatives directly,
                                      // since we just set it up.  Do NOT pass
                                      // JSPROP_PROPOP_ACCESSORS here!
                                      desc.attributes(),
                                      JS_PROPERTYOP_GETTER(desc.getter()),
                                      JS_PROPERTYOP_SETTER(desc.setter())) &&
-               JS_GetPropertyDescriptorById(cx, holder, id, desc);
+               JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
     }
 
     return true;
 }
 
 bool
 JSXrayTraits::delete_(JSContext* cx, HandleObject wrapper, HandleId id, ObjectOpResult& result)
 {
@@ -1237,17 +1237,17 @@ XPCWrappedNativeXrayTraits::resolveNativ
         JSFunction* toString = JS_NewFunction(cx, XrayToString, 0, 0, "toString");
         if (!toString)
             return false;
 
         FillPropertyDescriptor(desc, wrapper, 0,
                                ObjectValue(*JS_GetFunctionObject(toString)));
 
         return JS_DefinePropertyById(cx, holder, id, desc) &&
-               JS_GetPropertyDescriptorById(cx, holder, id, desc);
+               JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
     }
 
     desc.object().set(holder);
     desc.setAttributes(JSPROP_ENUMERATE);
     desc.setGetter(nullptr);
     desc.setSetter(nullptr);
     desc.value().set(JSVAL_VOID);
 
@@ -1330,17 +1330,17 @@ XrayTraits::resolveOwnProperty(JSContext
     if (!getExpandoObject(cx, target, wrapper, &expando))
         return false;
 
     // Check for expando properties first. Note that the expando object lives
     // in the target compartment.
     bool found = false;
     if (expando) {
         JSAutoCompartment ac(cx, expando);
-        if (!JS_GetPropertyDescriptorById(cx, expando, id, desc))
+        if (!JS_GetOwnPropertyDescriptorById(cx, expando, id, desc))
             return false;
         found = !!desc.object();
     }
 
     // Next, check for ES builtins.
     if (!found && JS_IsGlobalObject(target)) {
         JSProtoKey key = JS_IdToProtoKey(cx, id);
         JSAutoCompartment ac(cx, target);
@@ -1376,17 +1376,17 @@ XrayTraits::resolveOwnProperty(JSContext
     {
         if (!JS_AlreadyHasOwnPropertyById(cx, holder, id, &found))
             return false;
         if (!found && !JS_DefinePropertyById(cx, holder, id, UndefinedHandleValue,
                                              JSPROP_ENUMERATE | JSPROP_SHARED,
                                              wrappedJSObject_getter)) {
             return false;
         }
-        if (!JS_GetPropertyDescriptorById(cx, holder, id, desc))
+        if (!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
             return false;
         desc.object().set(wrapper);
         return true;
     }
 
     return true;
 }
 
@@ -1424,17 +1424,17 @@ XPCWrappedNativeXrayTraits::resolveOwnPr
             }
         }
     }
 
     // Xray wrappers don't use the regular wrapper hierarchy, so we should be
     // in the wrapper's compartment here, not the wrappee.
     MOZ_ASSERT(js::IsObjectInContextCompartment(wrapper, cx));
 
-    return JS_GetPropertyDescriptorById(cx, holder, id, desc);
+    return JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
 }
 
 bool
 XPCWrappedNativeXrayTraits::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
                                            Handle<JSPropertyDescriptor> desc,
                                            Handle<JSPropertyDescriptor> existingDesc,
                                            JS::ObjectOpResult& result, bool* defined)
 {
@@ -1567,17 +1567,17 @@ DOMXrayTraits::resolveOwnProperty(JSCont
                 }
                 desc.value().setObject(*obj);
                 FillPropertyDescriptor(desc, wrapper, true);
                 return JS_WrapPropertyDescriptor(cx, desc);
             }
         }
     }
 
-    if (!JS_GetPropertyDescriptorById(cx, holder, id, desc))
+    if (!JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
         return false;
     if (desc.object()) {
         desc.object().set(wrapper);
         return true;
     }
 
     RootedObject obj(cx, getTargetObject(wrapper));
     bool cacheOnHolder;
@@ -1585,17 +1585,17 @@ DOMXrayTraits::resolveOwnProperty(JSCont
         return false;
 
     MOZ_ASSERT(!desc.object() || desc.object() == wrapper, "What did we resolve this on?");
 
     if (!desc.object() || !cacheOnHolder)
         return true;
 
     return JS_DefinePropertyById(cx, holder, id, desc) &&
-           JS_GetPropertyDescriptorById(cx, holder, id, desc);
+           JS_GetOwnPropertyDescriptorById(cx, holder, id, desc);
 }
 
 bool
 DOMXrayTraits::defineProperty(JSContext* cx, HandleObject wrapper, HandleId id,
                               Handle<JSPropertyDescriptor> desc,
                               Handle<JSPropertyDescriptor> existingDesc,
                               JS::ObjectOpResult& result, bool* defined)
 {
@@ -1861,17 +1861,17 @@ XrayWrapper<Base, Traits>::getPropertyDe
     // Finally, we call resolveNativeProperty, which checks non-own properties,
     // and unconditionally caches what it finds on the holder.
 
     // Check resolveOwnProperty.
     if (!Traits::singleton.resolveOwnProperty(cx, *this, wrapper, holder, id, desc))
         return false;
 
     // Check the holder.
-    if (!desc.object() && !JS_GetPropertyDescriptorById(cx, holder, id, desc))
+    if (!desc.object() && !JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
         return false;
     if (desc.object()) {
         desc.object().set(wrapper);
         return true;
     }
 
     // Nothing in the cache. Call through, and cache the result.
     if (!Traits::singleton.resolveNativeProperty(cx, wrapper, holder, id, desc))
@@ -1903,17 +1903,17 @@ XrayWrapper<Base, Traits>::getPropertyDe
         }
     }
 
     // If we still have nothing, we're done.
     if (!desc.object())
         return true;
 
     if (!JS_DefinePropertyById(cx, holder, id, desc) ||
-        !JS_GetPropertyDescriptorById(cx, holder, id, desc))
+        !JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
     {
         return false;
     }
     MOZ_ASSERT(desc.object());
     desc.object().set(wrapper);
     return true;
 }
 
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -4403,16 +4403,25 @@ InvalidateVisibleBoundsChangesForScrolle
         printf_stderr("Invalidating layer %p: %s\n", aLayer, str.get());
       }
 #endif
     }
     data->mIgnoreInvalidationsOutsideRect = Nothing();
   }
 }
 
+static inline const Maybe<ParentLayerIntRect>&
+GetStationaryClipInContainer(Layer* aLayer)
+{
+  if (size_t metricsCount = aLayer->GetFrameMetricsCount()) {
+    return aLayer->GetFrameMetrics(metricsCount - 1).GetClipRect();
+  }
+  return aLayer->GetClipRect();
+}
+
 void
 ContainerState::PostprocessRetainedLayers(nsIntRegion* aOpaqueRegionForContainer)
 {
   nsAutoTArray<OpaqueRegionEntry,4> opaqueRegions;
   bool hideAll = false;
   int32_t opaqueRegionForContainer = -1;
 
   for (int32_t i = mNewChildLayers.Length() - 1; i >= 0; --i) {
@@ -4429,17 +4438,17 @@ ContainerState::PostprocessRetainedLayer
     OpaqueRegionEntry* data = FindOpaqueRegionEntry(opaqueRegions,
         animatedGeometryRootForOpaqueness, e->mFixedPosFrameForLayerData);
 
     SetupScrollingMetadata(e);
 
     if (hideAll) {
       e->mVisibleRegion.SetEmpty();
     } else if (!e->mLayer->IsScrollbarContainer()) {
-      const Maybe<ParentLayerIntRect>& clipRect = e->mLayer->GetClipRect();
+      const Maybe<ParentLayerIntRect>& clipRect = GetStationaryClipInContainer(e->mLayer);
       if (clipRect && opaqueRegionForContainer >= 0 &&
           opaqueRegions[opaqueRegionForContainer].mOpaqueRegion.Contains(ParentLayerIntRect::ToUntyped(*clipRect))) {
         e->mVisibleRegion.SetEmpty();
       } else if (data) {
         e->mVisibleRegion.Sub(e->mVisibleRegion, data->mOpaqueRegion);
       }
     }
 
@@ -4469,17 +4478,17 @@ ContainerState::PostprocessRetainedLayer
           opaqueRegionForContainer = opaqueRegions.Length();
         }
         data = opaqueRegions.AppendElement();
         data->mAnimatedGeometryRoot = animatedGeometryRootToCover;
         data->mFixedPosFrameForLayerData = e->mFixedPosFrameForLayerData;
       }
 
       nsIntRegion clippedOpaque = e->mOpaqueRegion;
-      const Maybe<ParentLayerIntRect>& clipRect = e->mLayer->GetClipRect();
+      Maybe<ParentLayerIntRect> clipRect = e->mLayer->GetCombinedClipRect();
       if (clipRect) {
         clippedOpaque.AndWith(ParentLayerIntRect::ToUntyped(*clipRect));
       }
       data->mOpaqueRegion.Or(data->mOpaqueRegion, clippedOpaque);
       if (e->mHideAllLayersBelow) {
         hideAll = true;
       }
     }
--- a/layout/base/nsFrameManager.cpp
+++ b/layout/base/nsFrameManager.cpp
@@ -159,23 +159,21 @@ nsFrameManager::Destroy()
 //----------------------------------------------------------------------
 
 // Placeholder frame functions
 nsPlaceholderFrame*
 nsFrameManager::GetPlaceholderFrameFor(const nsIFrame* aFrame)
 {
   NS_PRECONDITION(aFrame, "null param unexpected");
 
-  if (mPlaceholderMap.IsInitialized()) {
-    PlaceholderMapEntry *entry = static_cast<PlaceholderMapEntry*>
-                                            (PL_DHashTableSearch(const_cast<PLDHashTable2*>(&mPlaceholderMap),
-                                aFrame));
-    if (entry) {
-      return entry->placeholderFrame;
-    }
+  PlaceholderMapEntry *entry = static_cast<PlaceholderMapEntry*>
+                                          (PL_DHashTableSearch(const_cast<PLDHashTable*>(&mPlaceholderMap),
+                              aFrame));
+  if (entry) {
+    return entry->placeholderFrame;
   }
 
   return nullptr;
 }
 
 nsresult
 nsFrameManager::RegisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame)
 {
@@ -196,38 +194,34 @@ nsFrameManager::RegisterPlaceholderFrame
 
 void
 nsFrameManager::UnregisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame)
 {
   NS_PRECONDITION(aPlaceholderFrame, "null param unexpected");
   NS_PRECONDITION(nsGkAtoms::placeholderFrame == aPlaceholderFrame->GetType(),
                   "unexpected frame type");
 
-  if (mPlaceholderMap.IsInitialized()) {
-    PL_DHashTableRemove(&mPlaceholderMap,
-                        aPlaceholderFrame->GetOutOfFlowFrame());
-  }
+  PL_DHashTableRemove(&mPlaceholderMap,
+                      aPlaceholderFrame->GetOutOfFlowFrame());
 }
 
 static PLDHashOperator
 UnregisterPlaceholders(PLDHashTable* table, PLDHashEntryHdr* hdr,
                        uint32_t number, void* arg)
 {
   PlaceholderMapEntry* entry = static_cast<PlaceholderMapEntry*>(hdr);
   entry->placeholderFrame->SetOutOfFlowFrame(nullptr);
   return PL_DHASH_NEXT;
 }
 
 void
 nsFrameManager::ClearPlaceholderFrameMap()
 {
-  if (mPlaceholderMap.IsInitialized()) {
-    PL_DHashTableEnumerate(&mPlaceholderMap, UnregisterPlaceholders, nullptr);
-    mPlaceholderMap.Clear();
-  }
+  PL_DHashTableEnumerate(&mPlaceholderMap, UnregisterPlaceholders, nullptr);
+  mPlaceholderMap.Clear();
 }
 
 //----------------------------------------------------------------------
 
 /* static */ nsStyleContext*
 nsFrameManager::GetStyleContextInMap(UndisplayedMap* aMap, nsIContent* aContent)
 {
   if (!aContent) {
--- a/layout/base/nsFrameManagerBase.h
+++ b/layout/base/nsFrameManagerBase.h
@@ -51,17 +51,17 @@ public:
 protected:
   class UndisplayedMap;
 
   // weak link, because the pres shell owns us
   nsIPresShell* MOZ_NON_OWNING_REF mPresShell;
   // the pres shell owns the style set
   nsStyleSet*                     mStyleSet;
   nsIFrame*                       mRootFrame;
-  PLDHashTable2                   mPlaceholderMap;
+  PLDHashTable                    mPlaceholderMap;
   UndisplayedMap*                 mUndisplayedMap;
   UndisplayedMap*                 mDisplayContentsMap;
   bool                            mIsDestroyingFrames;  // The frame manager is destroying some frame(s).
 
   // The frame tree generation number
   // We use this to avoid unnecessary screenshotting
   // on Android. Unfortunately, this is static to match
   // the single consumer which is also static. Keeping
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -8214,16 +8214,18 @@ nsLayoutUtils::ComputeFrameMetrics(nsIFr
       LayoutDeviceIntSize::FromAppUnitsRounded(pageScrollAmount, presContext->AppUnitsPerDevPixel());
     metrics.SetPageScrollAmount(pageScrollAmountInDevPixels);
 
     if (!aScrollFrame->GetParent() ||
         EventStateManager::CanVerticallyScrollFrameWithWheel(aScrollFrame->GetParent()))
     {
       metrics.SetAllowVerticalScrollWithWheel();
     }
+
+    metrics.SetUsesContainerScrolling(scrollableFrame->UsesContainerScrolling());
   }
 
   // If we have the scrollparent being the same as the scroll id, the
   // compositor-side code could get into an infinite loop while building the
   // overscroll handoff chain.
   MOZ_ASSERT(aScrollParentId == FrameMetrics::NULL_SCROLL_ID || scrollId != aScrollParentId);
   metrics.SetScrollId(scrollId);
   metrics.SetIsRoot(aIsRoot);
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -3060,23 +3060,16 @@ ScrollFrameHelper::ComputeFrameMetrics(L
   }
 
   bool needsParentLayerClip = true;
   if (gfxPrefs::LayoutUseContainersForRootFrames() && !mAddClipRectToLayer) {
     // For containerful frames, the clip is on the container frame.
     needsParentLayerClip = false;
   }
 
-  // Note: Do not apply clips to the scroll ports of metrics above the first
-  // one on a given layer. They will be applied by the compositor instead,
-  // with async transforms for the scrollframes interspersed between them.
-  if (aOutput->Length() > 0) {
-    needsParentLayerClip = false;
-  }
-
   nsPoint toReferenceFrame = mOuter->GetOffsetToCrossDoc(aContainerReferenceFrame);
   bool isRoot = mIsRoot && mOuter->PresContext()->IsRootContentDocument();
 
   Maybe<nsRect> parentLayerClip;
   if (needsParentLayerClip) {
     nsRect clip = nsRect(mScrollPort.TopLeft() + toReferenceFrame,
                          nsLayoutUtils::CalculateCompositionSizeForFrame(mOuter));
     if (isRoot) {
@@ -5588,8 +5581,17 @@ ScrollFrameHelper::GetSnapPointForDestin
   } else {
     snapped = true;
   }
   if (snapped) {
     aDestination = finalPos;
   }
   return snapped;
 }
+
+bool
+ScrollFrameHelper::UsesContainerScrolling() const
+{
+  if (gfxPrefs::LayoutUseContainersForRootFrames()) {
+    return mIsRoot;
+  }
+  return false;
+}
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -354,16 +354,17 @@ public:
       // If the block has some text-overflow stuff we should kick off a paint
       // because we have special behaviour for it when APZ scrolling is active.
       mOuter->SchedulePaint();
     }
   }
   bool IsTransformingByAPZ() const {
     return mTransformingByAPZ;
   }
+  bool UsesContainerScrolling() const;
 
   void ScheduleSyntheticMouseMove();
   static void ScrollActivityCallback(nsITimer *aTimer, void* anInstance);
 
   void HandleScrollbarStyleSwitching();
 
   nsIAtom* LastScrollOrigin() const { return mLastScrollOrigin; }
   nsIAtom* LastSmoothScrollOrigin() const { return mLastSmoothScrollOrigin; }
@@ -840,16 +841,19 @@ public:
                                 aParameters, aOutput);
   }
   virtual bool IsIgnoringViewportClipping() const override {
     return mHelper.IsIgnoringViewportClipping();
   }
   virtual void MarkScrollbarsDirtyForReflow() const override {
     mHelper.MarkScrollbarsDirtyForReflow();
   }
+  virtual bool UsesContainerScrolling() const override {
+    return mHelper.UsesContainerScrolling();
+  }
 
   // nsIStatefulFrame
   NS_IMETHOD SaveState(nsPresState** aState) override {
     NS_ENSURE_ARG_POINTER(aState);
     *aState = mHelper.SaveState();
     return NS_OK;
   }
   NS_IMETHOD RestoreState(nsPresState* aState) override {
@@ -1301,16 +1305,19 @@ public:
 
   virtual bool IsScrollbarOnRight() const override {
     return mHelper.IsScrollbarOnRight();
   }
 
   virtual void SetTransformingByAPZ(bool aTransforming) override {
     mHelper.SetTransformingByAPZ(aTransforming);
   }
+  virtual bool UsesContainerScrolling() const override {
+    return mHelper.UsesContainerScrolling();
+  }
   bool IsTransformingByAPZ() const override {
     return mHelper.IsTransformingByAPZ();
   }
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -427,11 +427,16 @@ public:
 
   /**
    * Mark the scrollbar frames for reflow.
    */
   virtual void MarkScrollbarsDirtyForReflow() const = 0;
 
   virtual void SetTransformingByAPZ(bool aTransforming) = 0;
   virtual bool IsTransformingByAPZ() const = 0;
+
+  /**
+   * Whether or not this frame uses containerful scrolling.
+   */
+  virtual bool UsesContainerScrolling() const = 0;
 };
 
 #endif
--- a/layout/reftests/image-element/reftest.list
+++ b/layout/reftests/image-element/reftest.list
@@ -1,9 +1,9 @@
-== bug-364968.html bug-364968-ref.html
+random == bug-364968.html bug-364968-ref.html
 == bug-463204.html bug-463204-ref.html
 fails-if(Android&&AndroidVersion<15&&AndroidVersion!=10) == canvas-outside-document.html canvas-inside-document.html
 == mozsetimageelement-01.html mozsetimageelement-01-ref.html
 skip-if(B2G||Mulet) == mozsetimageelement-02.html about:blank # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) == image-outside-document-invalidate.html about:blank # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) == canvas-outside-document-invalidate-01.html about:blank # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) fails-if(azureSkia) fails-if(cocoaWidget) == canvas-outside-document-invalidate-02.html about:blank # See bug 666800 # bug 773482 # Initial mulet triage: parity with B2G/B2G Desktop
 #fails with Skia due to Skia bug http://code.google.com/p/skia/issues/detail?id=568
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -445,20 +445,20 @@ public:
 protected:
   typedef nsTArray<RuleValue> RuleValueList;
   void AppendRuleToTable(PLDHashTable* aTable, const void* aKey,
                          const RuleSelectorPair& aRuleInfo);
   void AppendUniversalRule(const RuleSelectorPair& aRuleInfo);
 
   int32_t     mRuleCount;
 
-  PLDHashTable2 mIdTable;
-  PLDHashTable2 mClassTable;
-  PLDHashTable2 mTagTable;
-  PLDHashTable2 mNameSpaceTable;
+  PLDHashTable mIdTable;
+  PLDHashTable mClassTable;
+  PLDHashTable mTagTable;
+  PLDHashTable mNameSpaceTable;
   RuleValueList mUniversalRules;
 
   struct EnumData {
     const RuleValue* mCurValue;
     const RuleValue* mEnd;
   };
   EnumData* mEnumList;
   int32_t   mEnumListSize;
@@ -887,24 +887,24 @@ struct RuleCascadeData {
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
 
   RuleHash                 mRuleHash;
   RuleHash*
     mPseudoElementRuleHashes[nsCSSPseudoElements::ePseudo_PseudoElementCount];
   nsTArray<nsCSSRuleProcessor::StateSelector>  mStateSelectors;
   EventStates              mSelectorDocumentStates;
-  PLDHashTable2            mClassSelectors;
-  PLDHashTable2            mIdSelectors;
+  PLDHashTable             mClassSelectors;
+  PLDHashTable             mIdSelectors;
   nsTArray<nsCSSSelector*> mPossiblyNegatedClassSelectors;
   nsTArray<nsCSSSelector*> mPossiblyNegatedIDSelectors;
-  PLDHashTable2            mAttributeSelectors;
-  PLDHashTable2            mAnonBoxRules;
+  PLDHashTable             mAttributeSelectors;
+  PLDHashTable             mAnonBoxRules;
 #ifdef MOZ_XUL
-  PLDHashTable2            mXULTreeRules;
+  PLDHashTable             mXULTreeRules;
 #endif
 
   nsTArray<nsFontFaceRuleContainer> mFontFaceRules;
   nsTArray<nsCSSKeyframesRule*> mKeyframesRules;
   nsTArray<nsCSSFontFeatureValuesRule*> mFontFeatureValuesRules;
   nsTArray<nsCSSPageRule*> mPageRules;
   nsTArray<nsCSSCounterStyleRule*> mCounterStyleRules;
 
@@ -3316,17 +3316,17 @@ struct CascadeEnumData {
   nsTArray<nsCSSKeyframesRule*>& mKeyframesRules;
   nsTArray<nsCSSFontFeatureValuesRule*>& mFontFeatureValuesRules;
   nsTArray<nsCSSPageRule*>& mPageRules;
   nsTArray<nsCSSCounterStyleRule*>& mCounterStyleRules;
   nsMediaQueryResultCacheKey& mCacheKey;
   PLArenaPool mArena;
   // Hooray, a manual PLDHashTable since nsClassHashtable doesn't
   // provide a getter that gives me a *reference* to the value.
-  PLDHashTable2 mRulesByWeight; // of PerWeightDataListItem linked lists
+  PLDHashTable mRulesByWeight; // of PerWeightDataListItem linked lists
   uint8_t mSheetType;
 };
 
 /*
  * This enumerates style rules in a sheet (and recursively into any
  * grouping rules) in order to:
  *  (1) add any style rules, in order, into data->mRulesByWeight (for
  *      the primary CSS cascade), where they are separated by weight
@@ -3516,19 +3516,16 @@ nsCSSRuleProcessor::RefreshRuleCascade(n
     if (newCascade) {
       CascadeEnumData data(aPresContext, newCascade->mFontFaceRules,
                            newCascade->mKeyframesRules,
                            newCascade->mFontFeatureValuesRules,
                            newCascade->mPageRules,
                            newCascade->mCounterStyleRules,
                            newCascade->mCacheKey,
                            mSheetType);
-      if (!data.mRulesByWeight.IsInitialized())
-        return; /* out of memory */
-
       for (uint32_t i = 0; i < mSheets.Length(); ++i) {
         if (!CascadeSheet(mSheets.ElementAt(i), &data))
           return; /* out of memory */
       }
 
       // Sort the hash table of per-weight linked lists by weight.
       uint32_t weightCount = data.mRulesByWeight.EntryCount();
       nsAutoArrayPtr<PerWeightData> weightArray(new PerWeightData[weightCount]);
--- a/layout/style/nsHTMLStyleSheet.h
+++ b/layout/style/nsHTMLStyleSheet.h
@@ -154,13 +154,13 @@ public: // for mLangRuleTable structures
 private:
   nsIDocument*            mDocument;
   nsRefPtr<HTMLColorRule> mLinkRule;
   nsRefPtr<HTMLColorRule> mVisitedRule;
   nsRefPtr<HTMLColorRule> mActiveRule;
   nsRefPtr<TableQuirkColorRule> mTableQuirkColorRule;
   nsRefPtr<TableTHRule>   mTableTHRule;
 
-  PLDHashTable2           mMappedAttrTable;
-  PLDHashTable2           mLangRuleTable;
+  PLDHashTable            mMappedAttrTable;
+  PLDHashTable            mLangRuleTable;
 };
 
 #endif /* !defined(nsHTMLStyleSheet_h_) */
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -1422,17 +1422,17 @@ nsRuleNode::DestroyInternal(nsRuleNode *
   if (aDestroyQueueTail) {
     destroyQueueTail = *aDestroyQueueTail;
   } else {
     destroyQueue = nullptr;
     destroyQueueTail = &destroyQueue;
   }
 
   if (ChildrenAreHashed()) {
-    PLDHashTable2 *children = ChildrenHash();
+    PLDHashTable *children = ChildrenHash();
     PL_DHashTableEnumerate(children, EnqueueRuleNodeChildren,
                            &destroyQueueTail);
     *destroyQueueTail = nullptr; // ensure null-termination
     delete children;
   } else if (HaveChildren()) {
     *destroyQueueTail = ChildrenList();
     do {
       destroyQueueTail = &(*destroyQueueTail)->mNextSibling;
@@ -1609,19 +1609,19 @@ void nsRuleNode::SetUsedDirectly()
   }
 }
 
 void
 nsRuleNode::ConvertChildrenToHash(int32_t aNumKids)
 {
   NS_ASSERTION(!ChildrenAreHashed() && HaveChildren(),
                "must have a non-empty list of children");
-  PLDHashTable2 *hash = new PLDHashTable2(&ChildrenHashOps,
-                                          sizeof(ChildrenHashEntry),
-                                          aNumKids);
+  PLDHashTable *hash = new PLDHashTable(&ChildrenHashOps,
+                                        sizeof(ChildrenHashEntry),
+                                        aNumKids);
   for (nsRuleNode* curr = ChildrenList(); curr; curr = curr->mNextSibling) {
     // This will never fail because of the initial size we gave the table.
     ChildrenHashEntry *entry = static_cast<ChildrenHashEntry*>(
       PL_DHashTableAdd(hash, curr->mRule, fallible));
     NS_ASSERTION(!entry->mRuleNode, "duplicate entries in list");
     entry->mRuleNode = curr;
   }
   SetChildrenHash(hash);
@@ -9369,17 +9369,17 @@ nsRuleNode::SweepChildren(nsTArray<nsRul
 {
   NS_ASSERTION(!(mDependentBits & NS_RULE_NODE_GC_MARK),
                "missing DestroyIfNotMarked() call");
   NS_ASSERTION(HaveChildren(),
                "why call SweepChildren with no children?");
   uint32_t childrenDestroyed = 0;
   nsRuleNode* survivorsWithChildren = nullptr;
   if (ChildrenAreHashed()) {
-    PLDHashTable2* children = ChildrenHash();
+    PLDHashTable* children = ChildrenHash();
     uint32_t oldChildCount = children->EntryCount();
     PL_DHashTableEnumerate(children, SweepHashEntry, &survivorsWithChildren);
     childrenDestroyed = oldChildCount - children->EntryCount();
     if (childrenDestroyed == oldChildCount) {
       delete children;
       mChildren.asVoid = nullptr;
     }
   } else {
--- a/layout/style/nsRuleNode.h
+++ b/layout/style/nsRuleNode.h
@@ -312,17 +312,17 @@ private:
   // The children of this node are stored in either a hashtable or list
   // that maps from rules to our nsRuleNode children.  When matching
   // rules, we use this mapping to transition from node to node
   // (constructing new nodes as needed to flesh out the tree).
 
   union {
     void* asVoid;
     nsRuleNode* asList;
-    PLDHashTable2* asHash;
+    PLDHashTable* asHash;
   } mChildren; // Accessed only through the methods below.
 
   enum {
     kTypeMask = 0x1,
     kListType = 0x0,
     kHashType = 0x1
   };
   enum {
@@ -338,28 +338,28 @@ private:
     return (intptr_t(mChildren.asVoid) & kTypeMask) == kHashType;
   }
   nsRuleNode* ChildrenList() {
     return mChildren.asList;
   }
   nsRuleNode** ChildrenListPtr() {
     return &mChildren.asList;
   }
-  PLDHashTable2* ChildrenHash() {
-    return (PLDHashTable2*) (intptr_t(mChildren.asHash) & ~intptr_t(kTypeMask));
+  PLDHashTable* ChildrenHash() {
+    return (PLDHashTable*) (intptr_t(mChildren.asHash) & ~intptr_t(kTypeMask));
   }
   void SetChildrenList(nsRuleNode *aList) {
     NS_ASSERTION(!(intptr_t(aList) & kTypeMask),
                  "pointer not 2-byte aligned");
     mChildren.asList = aList;
   }
-  void SetChildrenHash(PLDHashTable2 *aHashtable) {
+  void SetChildrenHash(PLDHashTable *aHashtable) {
     NS_ASSERTION(!(intptr_t(aHashtable) & kTypeMask),
                  "pointer not 2-byte aligned");
-    mChildren.asHash = (PLDHashTable2*)(intptr_t(aHashtable) | kHashType);
+    mChildren.asHash = (PLDHashTable*)(intptr_t(aHashtable) | kHashType);
   }
   void ConvertChildrenToHash(int32_t aNumKids);
 
   nsCachedStyleData mStyleData;   // Any data we cached on the rule node.
 
   uint32_t mDependentBits; // Used to cache the fact that we can look up
                            // cached data under a parent rule.
 
--- a/layout/tables/SpanningCellSorter.h
+++ b/layout/tables/SpanningCellSorter.h
@@ -57,17 +57,17 @@ private:
     Item *mArray[ARRAY_SIZE];
     int32_t SpanToIndex(int32_t aSpan) { return aSpan - ARRAY_BASE; }
     int32_t IndexToSpan(int32_t aIndex) { return aIndex + ARRAY_BASE; }
     bool UseArrayForSpan(int32_t aSpan) {
         NS_ASSERTION(SpanToIndex(aSpan) >= 0, "cell without colspan");
         return SpanToIndex(aSpan) < ARRAY_SIZE;
     }
 
-    PLDHashTable2 mHashTable;
+    PLDHashTable mHashTable;
     struct HashTableEntry : public PLDHashEntryHdr {
         int32_t mColSpan;
         Item *mItems;
     };
 
     static const PLDHashTableOps HashTableOps;
 
     static PLDHashNumber
--- a/layout/xul/nsMenuBarFrame.cpp
+++ b/layout/xul/nsMenuBarFrame.cpp
@@ -413,12 +413,13 @@ nsMenuBarFrame::DestroyFrom(nsIFrame* aD
   mTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keypress"), mMenuBarListener, false);
   mTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keydown"), mMenuBarListener, false);
   mTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keyup"), mMenuBarListener, false);
 
   mTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, true);
   mTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, false);
   mTarget->RemoveEventListener(NS_LITERAL_STRING("blur"), mMenuBarListener, true);
 
+  mMenuBarListener->OnDestroyMenuBarFrame();
   mMenuBarListener = nullptr;
 
   nsBoxFrame::DestroyFrom(aDestructRoot);
 }
--- a/layout/xul/nsMenuBarListener.cpp
+++ b/layout/xul/nsMenuBarListener.cpp
@@ -43,16 +43,22 @@ nsMenuBarListener::nsMenuBarListener(nsM
 }
 
 ////////////////////////////////////////////////////////////////////////
 nsMenuBarListener::~nsMenuBarListener() 
 {
 }
 
 void
+nsMenuBarListener::OnDestroyMenuBarFrame()
+{
+  mMenuBarFrame = nullptr;
+}
+
+void
 nsMenuBarListener::InitializeStatics()
 {
   Preferences::AddBoolVarCache(&mAccessKeyFocuses,
                                "ui.key.menuAccessKeyFocuses");
 }
 
 nsresult
 nsMenuBarListener::GetMenuAccessKey(int32_t* aAccessKey)
@@ -135,25 +141,40 @@ nsMenuBarListener::KeyUp(nsIDOMEvent* aK
     uint32_t theChar;
     keyEvent->GetKeyCode(&theChar);
 
     if (!defaultPrevented && mAccessKeyDown && !mAccessKeyDownCanceled &&
         (int32_t)theChar == mAccessKey)
     {
       // The access key was down and is now up, and no other
       // keys were pressed in between.
+      bool toggleMenuActiveState = true;
       if (!mMenuBarFrame->IsActive()) {
-        mMenuBarFrame->SetActiveByKeyboard();
+        // First, close all existing popups because other popups shouldn't
+        // handle key events when menubar is active and IME should be
+        // disabled.
+        nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+        if (pm) {
+          pm->Rollup(0, false, nullptr, nullptr);
+        }
+        // If menubar active state is changed or the menubar is destroyed
+        // during closing the popups, we should do nothing anymore.
+        toggleMenuActiveState = !Destroyed() && !mMenuBarFrame->IsActive();
       }
-      ToggleMenuActiveState();
+      if (toggleMenuActiveState) {
+        if (!mMenuBarFrame->IsActive()) {
+          mMenuBarFrame->SetActiveByKeyboard();
+        }
+        ToggleMenuActiveState();
+      }
     }
     mAccessKeyDown = false;
     mAccessKeyDownCanceled = false;
 
-    bool active = mMenuBarFrame->IsActive();
+    bool active = !Destroyed() && mMenuBarFrame->IsActive();
     if (active) {
       aKeyEvent->StopPropagation();
       aKeyEvent->PreventDefault();
       return NS_OK; // I am consuming event
     }
   }
   
   return NS_OK; // means I am NOT consuming event
--- a/layout/xul/nsMenuBarListener.h
+++ b/layout/xul/nsMenuBarListener.h
@@ -37,29 +37,33 @@ public:
   nsresult MouseDown(nsIDOMEvent* aMouseEvent);
 
   static nsresult GetMenuAccessKey(int32_t* aAccessKey);
   
   NS_DECL_ISUPPORTS
 
   static bool IsAccessKeyPressed(nsIDOMKeyEvent* event);
 
+  void OnDestroyMenuBarFrame();
+
 protected:
   /** default destructor
    */
   virtual ~nsMenuBarListener();
 
   static void InitAccessKey();
 
   static mozilla::Modifiers GetModifiersForAccessKey(nsIDOMKeyEvent* event);
 
   // This should only be called by the nsMenuBarListener during event dispatch,
   // thus ensuring that this doesn't get destroyed during the process.
   void ToggleMenuActiveState();
 
+  bool Destroyed() const { return !mMenuBarFrame; }
+
   nsMenuBarFrame* mMenuBarFrame; // The menu bar object.
   // Whether or not the ALT key is currently down.
   bool mAccessKeyDown;
   // Whether or not the ALT key down is canceled by other action.
   bool mAccessKeyDownCanceled;
   static bool mAccessKeyFocuses; // Does the access key by itself focus the menubar?
   static int32_t mAccessKey;     // See nsIDOMKeyEvent.h for sample values
   static mozilla::Modifiers mAccessKeyMask;// Modifier mask for the access key
--- a/layout/xul/test/browser.ini
+++ b/layout/xul/test/browser.ini
@@ -1,5 +1,7 @@
 [DEFAULT]
 
 [browser_bug685470.js]
 [browser_bug703210.js]
 [browser_bug706743.js]
+[browser_bug1163304.js]
+skip-if = os != 'linux' && os != 'win' // Due to testing menubar behavior with keyboard
new file mode 100644
--- /dev/null
+++ b/layout/xul/test/browser_bug1163304.js
@@ -0,0 +1,35 @@
+function test() {
+  waitForExplicitFinish();
+
+  let searchBar = BrowserSearch.searchBar;
+  searchBar.focus();
+
+  let DOMWindowUtils = EventUtils._getDOMWindowUtils();
+  is(DOMWindowUtils.IMEStatus, DOMWindowUtils.IME_STATUS_ENABLED,
+     "IME should be available when searchbar has focus");
+
+  let searchPopup = document.getElementById("PopupSearchAutoComplete");
+  searchPopup.addEventListener("popupshown", function (aEvent) {
+    searchPopup.removeEventListener("popupshown", arguments.callee);
+    setTimeout(function () {
+      is(DOMWindowUtils.IMEStatus, DOMWindowUtils.IME_STATUS_ENABLED,
+         "IME should be available even when the popup of searchbar is open");
+      searchPopup.addEventListener("popuphidden", function (aEvent) {
+        searchPopup.removeEventListener("popuphidden", arguments.callee);
+        setTimeout(function () {
+          is(DOMWindowUtils.IMEStatus, DOMWindowUtils.IME_STATUS_DISABLED,
+             "IME should not be available when menubar is active");
+          // Inactivate the menubar (and restore the focus to the searchbar
+          EventUtils.synthesizeKey("VK_ESCAPE", {});
+          is(DOMWindowUtils.IMEStatus, DOMWindowUtils.IME_STATUS_ENABLED,
+             "IME should be available after focus is back to the searchbar");
+          finish();
+        }, 0);
+      });
+      // Activate the menubar, then, the popup should be closed
+      EventUtils.synthesizeKey("VK_ALT", {});
+    }, 0);
+  });
+  // Open popup of the searchbar
+  EventUtils.synthesizeKey("VK_F4", {});
+}
--- a/memory/mozalloc/mozalloc_abort.cpp
+++ b/memory/mozalloc/mozalloc_abort.cpp
@@ -50,20 +50,29 @@ void fillAbortMessage(char (&msg)[N], ui
         reinterpret_cast<void*>(retAddress - uintptr_t(info.dli_fbase));
     const char* const sym = info.dli_sname ? info.dli_sname : "";
 
     snprintf(msg, sizeof(msg), "abort() called from %s:%p (%s)",
              base_module ? base_module + 1 : module, module_offset, sym);
 }
 #endif
 
-#if defined(XP_UNIX)
+#if defined(XP_UNIX) && !defined(MOZ_ASAN)
 // Define abort() here, so that it is used instead of the system abort(). This
 // lets us control the behavior when aborting, in order to get better results
 // on *NIX platforms. See mozalloc_abort for details.
+//
+// For AddressSanitizer, we must not redefine system abort because the ASan
+// option "abort_on_error=1" calls abort() and therefore causes the following
+// call chain with our redefined abort:
+//
+// ASan -> abort() -> moz_abort() -> MOZ_CRASH() -> Segmentation fault
+//
+// That segmentation fault will be interpreted as another bug by ASan and as a
+// result, ASan will just exit(1) instead of aborting.
 void abort(void)
 {
 #ifdef MOZ_WIDGET_ANDROID
     char msg[64] = {};
     fillAbortMessage(msg, uintptr_t(__builtin_return_address(0)));
 #else
     const char* const msg = "Redirecting call to abort() to mozalloc_abort\n";
 #endif
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -565,18 +565,19 @@ pref("apz.test.logging_enabled", false);
 #ifdef XP_MACOSX
 // Whether to run in native HiDPI mode on machines with "Retina"/HiDPI display;
 //   <= 0 : hidpi mode disabled, display will just use pixel-based upscaling
 //   == 1 : hidpi supported if all screens share the same backingScaleFactor
 //   >= 2 : hidpi supported even with mixed backingScaleFactors (somewhat broken)
 pref("gfx.hidpi.enabled", 2);
 #endif
 
-#if !defined(MOZ_WIDGET_ANDROID)
-// Containerless scrolling for root frames does not yet pass tests on Android.
+#if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID)
+// Containerless scrolling for root frames does not yet pass tests on Android
+// or B2G.
 pref("layout.scroll.root-frame-containers", false);
 #endif
 
 // Whether to enable LayerScope tool and default listening port
 pref("gfx.layerscope.enabled", false);
 pref("gfx.layerscope.port", 23456);
 
 // Log severe performance warnings to the error console and profiles.
@@ -1376,16 +1377,22 @@ pref("network.http.tcp_keepalive.short_l
 pref("network.http.tcp_keepalive.short_lived_idle_time", 10);
 
 pref("network.http.tcp_keepalive.long_lived_connections", true);
 pref("network.http.tcp_keepalive.long_lived_idle_time", 600);
 
 pref("network.http.enforce-framing.http1", false); // should be named "strict"
 pref("network.http.enforce-framing.soft", true);
 
+// Whether nsHttpChannel should use the PackagedAppService to load
+// resources from a package when directed to a URL
+// such as http://domain.com/package.pak!//resource.html
+// See http://www.w3.org/TR/web-packaging/#streamable-package-format
+pref("network.http.enable-packaged-apps", false);
+
 // default values for FTP
 // in a DSCP environment this should be 40 (0x28, or AF11), per RFC-4594,
 // Section 4.8 "High-Throughput Data Service Class", and 80 (0x50, or AF22)
 // per Section 4.7 "Low-Latency Data Service Class".
 pref("network.ftp.data.qos", 0);
 pref("network.ftp.control.qos", 0);
 
 // If this pref is false only one xpcom event will be served per poll
--- a/modules/libpref/nsPrefBranch.cpp
+++ b/modules/libpref/nsPrefBranch.cpp
@@ -550,19 +550,16 @@ NS_IMETHODIMP nsPrefBranch::GetChildList
 
   NS_ENSURE_ARG(aStartingAt);
   NS_ENSURE_ARG_POINTER(aCount);
   NS_ENSURE_ARG_POINTER(aChildArray);
 
   *aChildArray = nullptr;
   *aCount = 0;
 
-  if (!gHashTable->IsInitialized())
-    return NS_ERROR_NOT_INITIALIZED;
-
   // this will contain a list of all the pref name strings
   // allocate on the stack for speed
 
   ed.parent = getPrefName(aStartingAt);
   ed.pref_list = &prefArray;
   PL_DHashTableEnumerate(gHashTable, pref_enumChild, &ed);
 
   // now that we've built up the list, run the callback on
--- a/modules/libpref/prefapi.cpp
+++ b/modules/libpref/prefapi.cpp
@@ -63,17 +63,17 @@ matchPrefEntry(PLDHashTable*, const PLDH
     if (prefEntry->key == key) return true;
 
     if (!prefEntry->key || !key) return false;
 
     const char *otherKey = reinterpret_cast<const char*>(key);
     return (strcmp(prefEntry->key, otherKey) == 0);
 }
 
-PLDHashTable2*      gHashTable;
+PLDHashTable*       gHashTable;
 static PLArenaPool  gPrefNameArena;
 bool                gDirty = false;
 
 static struct CallbackNode* gCallbacks = nullptr;
 static bool         gIsAnyPrefLocked = false;
 // These are only used during the call to pref_DoCallback
 static bool         gCallbacksInProgress = false;
 static bool         gShouldCleanupDeadNodes = false;
@@ -145,19 +145,19 @@ enum {
 };
 static nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, uint32_t flags);
 
 #define PREF_HASHTABLE_INITIAL_LENGTH   1024
 
 nsresult PREF_Init()
 {
     if (!gHashTable) {
-        gHashTable = new PLDHashTable2(&pref_HashTableOps,
-                                       sizeof(PrefHashEntry),
-                                       PREF_HASHTABLE_INITIAL_LENGTH);
+        gHashTable = new PLDHashTable(&pref_HashTableOps,
+                                      sizeof(PrefHashEntry),
+                                      PREF_HASHTABLE_INITIAL_LENGTH);
 
         PL_INIT_ARENA_POOL(&gPrefNameArena, "PrefNameArena",
                            PREFNAME_ARENA_SIZE);
     }
     return NS_OK;
 }
 
 /* Frees the callback list. */
--- a/modules/libpref/prefapi_private_data.h
+++ b/modules/libpref/prefapi_private_data.h
@@ -5,17 +5,17 @@
 
 /* Data shared between prefapi.c and nsPref.cpp */
 
 #ifndef prefapi_private_data_h
 #define prefapi_private_data_h
 
 #include "mozilla/MemoryReporting.h"
 
-extern PLDHashTable2* gHashTable;
+extern PLDHashTable* gHashTable;
 extern bool gDirty;
 
 namespace mozilla {
 namespace dom {
 class PrefSetting;
 }
 }
 
--- a/netwerk/base/moz.build
+++ b/netwerk/base/moz.build
@@ -62,16 +62,17 @@ XPIDL_SOURCES += [
     'nsINetworkInterceptController.idl',
     'nsINetworkLinkService.idl',
     'nsINetworkPredictor.idl',
     'nsINetworkPredictorVerifier.idl',
     'nsINetworkProperties.idl',
     'nsINSSErrorsService.idl',
     'nsINullChannel.idl',
     'nsIPACGenerator.idl',
+    'nsIPackagedAppService.idl',
     'nsIParentChannel.idl',
     'nsIParentRedirectingChannel.idl',
     'nsIPermission.idl',
     'nsIPermissionManager.idl',
     'nsIPrivateBrowsingChannel.idl',
     'nsIProgressEventSink.idl',
     'nsIPrompt.idl',
     'nsIProtocolHandler.idl',
new file mode 100644
--- /dev/null
+++ b/netwerk/base/nsIPackagedAppService.idl
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 2; 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 "nsISupports.idl"
+
+interface nsIURI;
+interface nsILoadContextInfo;
+interface nsICacheEntryOpenCallback;
+
+%{C++
+  #define PACKAGED_APP_TOKEN "!//"
+%}
+
+/**
+ * nsIPackagedAppService
+ */
+[scriptable, builtinclass, uuid(77f9a34d-d082-43f1-9f83-e852d0173cd5)]
+interface nsIPackagedAppService : nsISupports
+{
+  /**
+   * @aURI is a URL to a packaged resource
+   *   - format:  package_url + PACKAGED_APP_TOKEN + resource_path
+   *   - example: http://test.com/path/to/package!//resource.html
+   * @aCallback is an object implementing nsICacheEntryOpenCallback
+   *   - this is the target of the async result of the operation
+   *   - aCallback->OnCacheEntryCheck() is called to verify the entry is valid
+   *   - aCallback->OnCacheEntryAvailable() is called with a pointer to the
+   *     the cached entry, if one exists, or an error code otherwise
+   *   - aCallback is kept alive using an nsCOMPtr until OnCacheEntryAvailable
+   *     is called
+   * @aInfo is an object used to determine the cache jar this resource goes in.
+   *   - usually created by calling GetLoadContextInfo(requestingChannel)
+   *
+   * Calling this method will either download the package containing the given
+   * resource URI, store it in the cache and pass the cache entry to aCallback,
+   * or if that resource has already been downloaded it will be served from
+   * the cache.
+   */
+  void requestURI(in nsIURI aURI, in nsILoadContextInfo aInfo, in nsICacheEntryOpenCallback aCallback);
+};
--- a/netwerk/base/nsLoadGroup.h
+++ b/netwerk/base/nsLoadGroup.h
@@ -65,17 +65,17 @@ protected:
     uint32_t                        mLoadFlags;
     uint32_t                        mDefaultLoadFlags;
 
     nsCOMPtr<nsILoadGroup>          mLoadGroup; // load groups can contain load groups
     nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
     nsCOMPtr<nsILoadGroupConnectionInfo> mConnectionInfo;
 
     nsCOMPtr<nsIRequest>            mDefaultLoadRequest;
-    PLDHashTable2                   mRequests;
+    PLDHashTable                    mRequests;
 
     nsWeakPtr                       mObserver;
     nsWeakPtr                       mParentLoadGroup;
     
     nsresult                        mStatus;
     int32_t                         mPriority;
     bool                            mIsCanceling;
 
--- a/netwerk/build/nsNetCID.h
+++ b/netwerk/build/nsNetCID.h
@@ -863,16 +863,26 @@
 #define NS_DASHBOARD_CID                               \
 {   /*c79eb3c6-091a-45a6-8544-5a8d1ab79537 */          \
     0xc79eb3c6,                                        \
     0x091a,                                            \
     0x45a6,                                            \
     { 0x85, 0x44, 0x5a, 0x8d, 0x1a, 0xb7, 0x95, 0x37 } \
 }
 
+#define NS_PACKAGEDAPPSERVICE_CONTRACTID \
+    "@mozilla.org/network/packaged-app-service;1"
+#define NS_PACKAGEDAPPSERVICE_CID                      \
+{   /* adef6762-41b9-4470-a06a-dc29cf8de381 */         \
+    0xadef6762,                                        \
+    0x41b9,                                            \
+    0x4470,                                            \
+  { 0xa0, 0x6a, 0xdc, 0x29, 0xcf, 0x8d, 0xe3, 0x81 }   \
+}
+
 
 /******************************************************************************
  * netwerk/cookie classes
  */
 
 // service implementing nsICookieManager and nsICookieManager2.
 #define NS_COOKIEMANAGER_CONTRACTID \
     "@mozilla.org/cookiemanager;1"
--- a/netwerk/build/nsNetModule.cpp
+++ b/netwerk/build/nsNetModule.cpp
@@ -246,19 +246,21 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpCha
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpActivityDistributor)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpBasicAuth)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHttpDigestAuth)
 }
 }
 #endif // !NECKO_PROTOCOL_http
 
 #include "mozilla/net/Dashboard.h"
+#include "mozilla/net/PackagedAppService.h"
 namespace mozilla {
 namespace net {
   NS_GENERIC_FACTORY_CONSTRUCTOR(Dashboard)
+  NS_GENERIC_FACTORY_CONSTRUCTOR(PackagedAppService)
 }
 }
 #include "AppProtocolHandler.h"
 
 #ifdef NECKO_PROTOCOL_res
 // resource
 #include "nsResProtocolHandler.h"
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsResProtocolHandler, Init)
@@ -704,16 +706,17 @@ NS_DEFINE_NAMED_CID(NS_NOAUTHURLPARSER_C
 NS_DEFINE_NAMED_CID(NS_AUTHURLPARSER_CID);
 NS_DEFINE_NAMED_CID(NS_STANDARDURL_CID);
 NS_DEFINE_NAMED_CID(NS_ARRAYBUFFERINPUTSTREAM_CID);
 NS_DEFINE_NAMED_CID(NS_BUFFEREDINPUTSTREAM_CID);
 NS_DEFINE_NAMED_CID(NS_BUFFEREDOUTPUTSTREAM_CID);
 NS_DEFINE_NAMED_CID(NS_MIMEINPUTSTREAM_CID);
 NS_DEFINE_NAMED_CID(NS_PROTOCOLPROXYSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_STREAMCONVERTERSERVICE_CID);
+NS_DEFINE_NAMED_CID(NS_PACKAGEDAPPSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_DASHBOARD_CID);
 #ifdef NECKO_PROTOCOL_ftp
 NS_DEFINE_NAMED_CID(NS_FTPDIRLISTINGCONVERTER_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_NSINDEXEDTOHTMLCONVERTER_CID);
 NS_DEFINE_NAMED_CID(NS_DIRINDEXPARSER_CID);
 NS_DEFINE_NAMED_CID(NS_MULTIMIXEDCONVERTER_CID);
 NS_DEFINE_NAMED_CID(NS_UNKNOWNDECODER_CID);
@@ -848,16 +851,17 @@ static const mozilla::Module::CIDEntry k
     { &kNS_AUTHURLPARSER_CID, false, nullptr, nsAuthURLParserConstructor },
     { &kNS_STANDARDURL_CID, false, nullptr, nsStandardURLConstructor },
     { &kNS_ARRAYBUFFERINPUTSTREAM_CID, false, nullptr, ArrayBufferInputStreamConstructor },
     { &kNS_BUFFEREDINPUTSTREAM_CID, false, nullptr, nsBufferedInputStream::Create },
     { &kNS_BUFFEREDOUTPUTSTREAM_CID, false, nullptr, nsBufferedOutputStream::Create },
     { &kNS_MIMEINPUTSTREAM_CID, false, nullptr, nsMIMEInputStreamConstructor },
     { &kNS_PROTOCOLPROXYSERVICE_CID, true, nullptr, nsProtocolProxyServiceConstructor },
     { &kNS_STREAMCONVERTERSERVICE_CID, false, nullptr, CreateNewStreamConvServiceFactory },
+    { &kNS_PACKAGEDAPPSERVICE_CID, false, NULL, mozilla::net::PackagedAppServiceConstructor },
     { &kNS_DASHBOARD_CID, false, nullptr, mozilla::net::DashboardConstructor },
 #ifdef NECKO_PROTOCOL_ftp
     { &kNS_FTPDIRLISTINGCONVERTER_CID, false, nullptr, CreateNewFTPDirListingConv },
 #endif
     { &kNS_NSINDEXEDTOHTMLCONVERTER_CID, false, nullptr, nsIndexedToHTML::Create },
     { &kNS_DIRINDEXPARSER_CID, false, nullptr, nsDirIndexParserConstructor },
     { &kNS_MULTIMIXEDCONVERTER_CID, false, nullptr, CreateNewMultiMixedConvFactory },
     { &kNS_UNKNOWNDECODER_CID, false, nullptr, CreateNewUnknownDecoderFactory },
@@ -994,16 +998,17 @@ static const mozilla::Module::ContractID
     { NS_AUTHURLPARSER_CONTRACTID, &kNS_AUTHURLPARSER_CID },
     { NS_STANDARDURL_CONTRACTID, &kNS_STANDARDURL_CID },
     { NS_ARRAYBUFFERINPUTSTREAM_CONTRACTID, &kNS_ARRAYBUFFERINPUTSTREAM_CID },
     { NS_BUFFEREDINPUTSTREAM_CONTRACTID, &kNS_BUFFEREDINPUTSTREAM_CID },
     { NS_BUFFEREDOUTPUTSTREAM_CONTRACTID, &kNS_BUFFEREDOUTPUTSTREAM_CID },
     { NS_MIMEINPUTSTREAM_CONTRACTID, &kNS_MIMEINPUTSTREAM_CID },
     { NS_PROTOCOLPROXYSERVICE_CONTRACTID, &kNS_PROTOCOLPROXYSERVICE_CID },
     { NS_STREAMCONVERTERSERVICE_CONTRACTID, &kNS_STREAMCONVERTERSERVICE_CID },
+    { NS_PACKAGEDAPPSERVICE_CONTRACTID, &kNS_PACKAGEDAPPSERVICE_CID },
     { NS_DASHBOARD_CONTRACTID, &kNS_DASHBOARD_CID },
 #ifdef NECKO_PROTOCOL_ftp
     { NS_ISTREAMCONVERTER_KEY FTP_TO_INDEX, &kNS_FTPDIRLISTINGCONVERTER_CID },
 #endif
     { NS_ISTREAMCONVERTER_KEY INDEX_TO_HTML, &kNS_NSINDEXEDTOHTMLCONVERTER_CID },
     { NS_DIRINDEXPARSER_CONTRACTID, &kNS_DIRINDEXPARSER_CID },
     { NS_ISTREAMCONVERTER_KEY MULTI_MIXED_X, &kNS_MULTIMIXEDCONVERTER_CID },
     { NS_ISTREAMCONVERTER_KEY MULTI_BYTERANGES, &kNS_MULTIMIXEDCONVERTER_CID },
--- a/netwerk/cache/nsCacheEntry.h
+++ b/netwerk/cache/nsCacheEntry.h
@@ -300,15 +300,15 @@ private:
     static
     PLDHashOperator       VisitEntry(PLDHashTable *         table,
                                      PLDHashEntryHdr *      hdr,
                                      uint32_t               number,
                                      void *                 arg);
 
     // member variables
     static const PLDHashTableOps ops;
-    PLDHashTable2                table;
+    PLDHashTable                 table;
     bool                         initialized;
 
     static const uint32_t kInitialTableLength = 256;
 };
 
 #endif // _nsCacheEntry_h_
--- a/netwerk/cache/nsDiskCacheBinding.h
+++ b/netwerk/cache/nsDiskCacheBinding.h
@@ -109,15 +109,15 @@ public:
 
     size_t                 SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf);
 
 private:
     nsresult                AddBinding(nsDiskCacheBinding * binding);
 
     // member variables
     static const PLDHashTableOps ops;
-    PLDHashTable2          table;
+    PLDHashTable           table;
     bool                   initialized;
 
     static const uint32_t kInitialTableLength = 0;
 };
 
 #endif /* _nsDiskCacheBinding_h_ */
--- a/netwerk/dns/nsHostResolver.h
+++ b/netwerk/dns/nsHostResolver.h
@@ -340,17 +340,17 @@ private:
     uint32_t      mMaxCacheEntries;
     uint32_t      mDefaultCacheLifetime; // granularity seconds
     uint32_t      mDefaultGracePeriod; // granularity seconds
     mutable Mutex mLock;    // mutable so SizeOfIncludingThis can be const
     CondVar       mIdleThreadCV;
     uint32_t      mNumIdleThreads;
     uint32_t      mThreadCount;
     uint32_t      mActiveAnyThreadCount;
-    PLDHashTable2 mDB;
+    PLDHashTable  mDB;
     PRCList       mHighQ;
     PRCList       mMediumQ;
     PRCList       mLowQ;
     PRCList       mEvictionQ;
     uint32_t      mEvictionQSize;
     uint32_t      mPendingCount;
     PRTime        mCreationTime;
     bool          mShutdown;
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/PackagedAppService.cpp
@@ -0,0 +1,554 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 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 "PackagedAppService.h"
+#include "nsICacheStorage.h"
+#include "LoadContextInfo.h"
+#include "nsICacheStorageService.h"
+#include "nsIResponseHeadProvider.h"
+#include "nsIMultiPartChannel.h"
+#include "../../cache2/CacheFileUtils.h"
+#include "nsStreamUtils.h"
+
+namespace mozilla {
+namespace net {
+
+static PackagedAppService *gPackagedAppService = nullptr;
+
+NS_IMPL_ISUPPORTS(PackagedAppService, nsIPackagedAppService)
+
+NS_IMPL_ISUPPORTS(PackagedAppService::CacheEntryWriter, nsIStreamListener)
+
+/* static */ nsresult
+PackagedAppService::CacheEntryWriter::Create(nsIURI *aURI,
+                                             nsICacheStorage *aStorage,
+                                             CacheEntryWriter **aResult)
+{
+  nsRefPtr<CacheEntryWriter> writer = new CacheEntryWriter();
+  nsresult rv = aStorage->OpenTruncate(aURI, EmptyCString(),
+                                       getter_AddRefs(writer->mEntry));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  rv = writer->mEntry->ForceValidFor(PR_UINT32_MAX);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  writer.forget(aResult);
+  return NS_OK;
+}
+
+NS_METHOD
+PackagedAppService::CacheEntryWriter::ConsumeData(nsIInputStream *aStream,
+                                                  void *aClosure,
+                                                  const char *aFromRawSegment,
+                                                  uint32_t aToOffset,
+                                                  uint32_t aCount,
+                                                  uint32_t *aWriteCount)
+{
+  MOZ_ASSERT(aClosure, "The closure must not be null");
+  CacheEntryWriter *self = static_cast<CacheEntryWriter*>(aClosure);
+  MOZ_ASSERT(self->mOutputStream, "The stream should not be null");
+  return self->mOutputStream->Write(aFromRawSegment, aCount, aWriteCount);
+}
+
+NS_IMETHODIMP
+PackagedAppService::CacheEntryWriter::OnStartRequest(nsIRequest *aRequest,
+                                                     nsISupports *aContext)
+{
+  nsCOMPtr<nsIResponseHeadProvider> provider(do_QueryInterface(aRequest));
+  if (!provider) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  nsHttpResponseHead *responseHead = provider->GetResponseHead();
+  if (!responseHead) {
+    return NS_ERROR_FAILURE;
+  }
+
+  mEntry->SetPredictedDataSize(responseHead->TotalEntitySize());
+
+  nsAutoCString head;
+  responseHead->Flatten(head, true);
+  nsresult rv = mEntry->SetMetaDataElement("response-head", head.get());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mEntry->SetMetaDataElement("request-method", "GET");
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = mEntry->OpenOutputStream(0, getter_AddRefs(mOutputStream));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PackagedAppService::CacheEntryWriter::OnStopRequest(nsIRequest *aRequest,
+                                                    nsISupports *aContext,
+                                                    nsresult aStatusCode)
+{
+  if (mOutputStream) {
+    mOutputStream->Close();
+    mOutputStream = nullptr;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PackagedAppService::CacheEntryWriter::OnDataAvailable(nsIRequest *aRequest,
+                                                      nsISupports *aContext,
+                                                      nsIInputStream *aInputStream,
+                                                      uint64_t aOffset,
+                                                      uint32_t aCount)
+{
+  if (!aInputStream) {
+    return NS_ERROR_INVALID_ARG;
+  }
+  // Calls ConsumeData to read the data into the cache entry
+  uint32_t n;
+  return aInputStream->ReadSegments(ConsumeData, this, aCount, &n);
+}
+
+
+NS_IMPL_ISUPPORTS(PackagedAppService::PackagedAppDownloader, nsIStreamListener)
+
+nsresult
+PackagedAppService::PackagedAppDownloader::Init(nsILoadContextInfo* aInfo,
+                                                const nsCString& aKey)
+{
+  nsresult rv;
+  nsCOMPtr<nsICacheStorageService> cacheStorageService =
+    do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  rv = cacheStorageService->DiskCacheStorage(aInfo, false,
+                                             getter_AddRefs(mCacheStorage));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  mPackageKey = aKey;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PackagedAppService::PackagedAppDownloader::OnStartRequest(nsIRequest *aRequest,
+                                                          nsISupports *aContext)
+{
+  // In case an error occurs in this method mWriter should be null
+  // so we don't accidentally write to the previous resource's cache entry.
+  mWriter = nullptr;
+
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = GetSubresourceURI(aRequest, getter_AddRefs(uri));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return NS_OK;
+  }
+
+  rv = CacheEntryWriter::Create(uri, mCacheStorage, getter_AddRefs(mWriter));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(mWriter);
+  rv = mWriter->OnStartRequest(aRequest, aContext);
+  NS_WARN_IF(NS_FAILED(rv));
+  return NS_OK;
+}
+
+nsresult
+PackagedAppService::PackagedAppDownloader::GetSubresourceURI(nsIRequest * aRequest,
+                                                             nsIURI ** aResult)
+{
+  nsresult rv;
+  nsCOMPtr<nsIResponseHeadProvider> provider(do_QueryInterface(aRequest));
+  nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
+
+  if (NS_WARN_IF(!provider || !chan)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  nsHttpResponseHead *responseHead = provider->GetResponseHead();
+  if (NS_WARN_IF(!responseHead)) {
+    return NS_ERROR_FAILURE;
+  }
+  nsAutoCString contentLocation;
+  rv = responseHead->GetHeader(nsHttp::ResolveAtom("Content-Location"), contentLocation);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  rv = chan->GetURI(getter_AddRefs(uri));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  nsAutoCString path;
+  rv = uri->GetPath(path);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  path += PACKAGED_APP_TOKEN;
+
+  // TODO: make sure the path is normalized
+  if (StringBeginsWith(contentLocation, NS_LITERAL_CSTRING("/"))) {
+    contentLocation = Substring(contentLocation, 1);
+  }
+
+  path += contentLocation;
+
+  nsCOMPtr<nsIURI> partURI;
+  rv = uri->CloneIgnoringRef(getter_AddRefs(partURI));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  rv = partURI->SetPath(path);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  partURI.forget(aResult);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PackagedAppService::PackagedAppDownloader::OnStopRequest(nsIRequest *aRequest,
+                                                         nsISupports *aContext,
+                                                         nsresult aStatusCode)
+{
+  nsCOMPtr<nsIMultiPartChannel> multiChannel(do_QueryInterface(aRequest));
+  nsresult rv;
+
+
+  // The request is normally a multiPartChannel. If it isn't, it generally means
+  // an error has occurred in nsMultiMixedConv.
+  // If an error occurred in OnStartRequest, mWriter could be null.
+  if (multiChannel && mWriter) {
+    mWriter->OnStopRequest(aRequest, aContext, aStatusCode);
+
+    nsCOMPtr<nsIURI> uri;
+    rv = GetSubresourceURI(aRequest, getter_AddRefs(uri));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return NS_OK;
+    }
+
+    nsCOMPtr<nsICacheEntry> entry;
+    mWriter->mEntry.swap(entry);
+
+    // We don't need the writer anymore - this will close its stream
+    mWriter = nullptr;
+    CallCallbacks(uri, entry, aStatusCode);
+  }
+
+  bool lastPart = false;
+  if (multiChannel) {
+    rv = multiChannel->GetIsLastPart(&lastPart);
+    if (NS_SUCCEEDED(rv) && !lastPart) {
+      // If this isn't the last part, we don't do the cleanup yet
+      return NS_OK;
+    }
+  }
+
+  // If this is the last part of the package, it means the requested resources
+  // have not been found in the package so we return an appropriate error.
+  if (NS_SUCCEEDED(aStatusCode) && lastPart) {
+    aStatusCode = NS_ERROR_FILE_NOT_FOUND;
+  }
+
+  nsRefPtr<PackagedAppDownloader> kungFuDeathGrip(this);
+  // NotifyPackageDownloaded removes the ref from the array. Keep a temp ref
+  if (gPackagedAppService) {
+    gPackagedAppService->NotifyPackageDownloaded(mPackageKey);
+  }
+  ClearCallbacks(aStatusCode);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PackagedAppService::PackagedAppDownloader::OnDataAvailable(nsIRequest *aRequest,
+                                                           nsISupports *aContext,
+                                                           nsIInputStream *aInputStream,
+                                                           uint64_t aOffset,
+                                                           uint32_t aCount)
+{
+  if (!mWriter) {
+    uint32_t n;
+    return aInputStream->ReadSegments(NS_DiscardSegment, nullptr, aCount, &n);
+  }
+  return mWriter->OnDataAvailable(aRequest, aContext, aInputStream, aOffset,
+                                  aCount);
+}
+
+nsresult
+PackagedAppService::PackagedAppDownloader::AddCallback(nsIURI *aURI,
+                                                       nsICacheEntryOpenCallback *aCallback)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mCallbacks hashtable is not thread safe");
+  nsAutoCString spec;
+  aURI->GetAsciiSpec(spec);
+
+  // Check if we already have a resource waiting for this resource
+  nsCOMArray<nsICacheEntryOpenCallback>* array = mCallbacks.Get(spec);
+  if (array) {
+    // Add this resource to the callback array
+    array->AppendObject(aCallback);
+  } else {
+    // This is the first callback for this URI.
+    // Create a new array and add the callback
+    nsCOMArray<nsICacheEntryOpenCallback>* newArray =
+      new nsCOMArray<nsICacheEntryOpenCallback>();
+    newArray->AppendObject(aCallback);
+    mCallbacks.Put(spec, newArray);
+  }
+  return NS_OK;
+}
+
+nsresult
+PackagedAppService::PackagedAppDownloader::CallCallbacks(nsIURI *aURI,
+                                                         nsICacheEntry *aEntry,
+                                                         nsresult aResult)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mCallbacks hashtable is not thread safe");
+  // Hold on to this entry while calling the callbacks
+  nsCOMPtr<nsICacheEntry> handle(aEntry);
+
+  nsAutoCString spec;
+  aURI->GetSpec(spec);
+
+  nsCOMArray<nsICacheEntryOpenCallback>* array = mCallbacks.Get(spec);
+  if (array) {
+    // Call all the callbacks for this URI
+    for (uint32_t i = 0; i < array->Length(); ++i) {
+      nsCOMPtr<nsICacheEntryOpenCallback> callback(array->ObjectAt(i));
+      // We call to AsyncOpenURI which automatically calls the callback.
+      mCacheStorage->AsyncOpenURI(aURI, EmptyCString(),
+                                  nsICacheStorage::OPEN_READONLY, callback);
+    }
+    // Clear the array and remove it from the hashtable
+    array->Clear();
+    mCallbacks.Remove(spec);
+    aEntry->ForceValidFor(0);
+  }
+  return NS_OK;
+}
+
+PLDHashOperator
+PackagedAppService::PackagedAppDownloader::ClearCallbacksEnumerator(const nsACString& key,
+  nsAutoPtr<nsCOMArray<nsICacheEntryOpenCallback> >& callbackArray,
+  void* arg)
+{
+  MOZ_ASSERT(arg, "The void* parameter should be a pointer to nsresult");
+  nsresult *result = static_cast<nsresult*>(arg);
+  for (uint32_t i = 0; i < callbackArray->Length(); ++i) {
+    nsCOMPtr<nsICacheEntryOpenCallback> callback = callbackArray->ObjectAt(i);
+    callback->OnCacheEntryAvailable(nullptr, false, nullptr, *result);
+  }
+  // Remove entry from hashtable
+  return PL_DHASH_REMOVE;
+}
+
+nsresult
+PackagedAppService::PackagedAppDownloader::ClearCallbacks(nsresult aResult)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mCallbacks hashtable is not thread safe");
+  mCallbacks.Enumerate(ClearCallbacksEnumerator, &aResult);
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(PackagedAppService::CacheEntryChecker, nsICacheEntryOpenCallback)
+
+NS_IMETHODIMP
+PackagedAppService::CacheEntryChecker::OnCacheEntryCheck(nsICacheEntry *aEntry,
+                                                         nsIApplicationCache *aApplicationCache,
+                                                         uint32_t *_retval)
+{
+  return mCallback->OnCacheEntryCheck(aEntry, aApplicationCache, _retval);
+}
+
+NS_IMETHODIMP
+PackagedAppService::CacheEntryChecker::OnCacheEntryAvailable(nsICacheEntry *aEntry,
+                                                             bool aNew,
+                                                             nsIApplicationCache *aApplicationCache,
+                                                             nsresult aResult)
+{
+  if (aResult == NS_ERROR_CACHE_KEY_NOT_FOUND) {
+    MOZ_ASSERT(!aEntry, "No entry");
+    // trigger download
+    // download checks if package download is already in progress
+    gPackagedAppService->OpenNewPackageInternal(mURI, mCallback,
+                                                mLoadContextInfo);
+  } else {
+    // TODO: if aResult is another error code, should we pass it off to the
+    // consumer, or should we try to download the package again?
+    mCallback->OnCacheEntryAvailable(aEntry, aNew, aApplicationCache, aResult);
+    // TODO: update last access entry for the entire package
+  }
+  return NS_OK;
+}
+
+PackagedAppService::PackagedAppService()
+{
+  gPackagedAppService = this;
+}
+
+PackagedAppService::~PackagedAppService()
+{
+  gPackagedAppService = nullptr;
+}
+
+NS_IMETHODIMP
+PackagedAppService::RequestURI(nsIURI *aURI,
+                               nsILoadContextInfo *aInfo,
+                               nsICacheEntryOpenCallback *aCallback)
+{
+  // Check arguments are not null
+  if (!aURI || !aCallback || !aInfo) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  nsAutoCString path;
+  aURI->GetPath(path);
+  int32_t pos = path.Find(PACKAGED_APP_TOKEN);
+  if (pos == kNotFound) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  nsresult rv;
+  nsCOMPtr<nsICacheStorageService> cacheStorageService =
+    do_GetService("@mozilla.org/netwerk/cache-storage-service;1", &rv);
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsICacheStorage> cacheStorage;
+
+  rv = cacheStorageService->DiskCacheStorage(aInfo, false,
+                                             getter_AddRefs(cacheStorage));
+
+  nsRefPtr<CacheEntryChecker> checker = new CacheEntryChecker(aURI, aCallback, aInfo);
+  return cacheStorage->AsyncOpenURI(aURI, EmptyCString(),
+                                    nsICacheStorage::OPEN_READONLY, checker);
+}
+
+nsresult
+PackagedAppService::NotifyPackageDownloaded(nsCString aKey)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mDownloadingPackages hashtable is not thread safe");
+  mDownloadingPackages.Remove(aKey);
+  return NS_OK;
+}
+
+nsresult
+PackagedAppService::OpenNewPackageInternal(nsIURI *aURI,
+                                           nsICacheEntryOpenCallback *aCallback,
+                                           nsILoadContextInfo *aInfo)
+{
+  MOZ_RELEASE_ASSERT(NS_IsMainThread(), "mDownloadingPackages hashtable is not thread safe");
+
+  nsAutoCString path;
+  nsresult rv = aURI->GetPath(path);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  int32_t pos = path.Find(PACKAGED_APP_TOKEN);
+  MOZ_ASSERT(pos != kNotFound,
+             "This should never be called if the token is missing");
+
+  nsCOMPtr<nsIURI> packageURI;
+  rv = aURI->CloneIgnoringRef(getter_AddRefs(packageURI));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  rv = packageURI->SetPath(Substring(path, 0, pos));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  nsAutoCString key;
+  CacheFileUtils::AppendKeyPrefix(aInfo, key);
+
+  {
+    nsAutoCString spec;
+    packageURI->GetAsciiSpec(spec);
+    key += ":";
+    key += spec;
+  }
+
+  nsRefPtr<PackagedAppDownloader> downloader;
+  if (mDownloadingPackages.Get(key, getter_AddRefs(downloader))) {
+    // We have determined that the file is not in the cache.
+    // If we find that the package that the file belongs to is currently being
+    // downloaded, we will add the callback to the package's queue, and it will
+    // be called once the file is processed and saved in the cache.
+
+    downloader->AddCallback(aURI, aCallback);
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIChannel> channel;
+  rv = NS_NewChannel(
+    getter_AddRefs(channel), packageURI, nsContentUtils::GetSystemPrincipal(),
+    nsILoadInfo::SEC_NORMAL, nsIContentPolicy::TYPE_OTHER, nullptr, nullptr,
+    nsIRequest::LOAD_NORMAL);
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsICachingChannel> cacheChan(do_QueryInterface(channel));
+  if (cacheChan) {
+    // Each resource in the package will be put in its own cache entry
+    // during the first load of the package, so we only want the channel to
+    // cache the response head, not the entire content of the package.
+    cacheChan->SetCacheOnlyMetadata(true);
+  }
+
+  downloader = new PackagedAppDownloader();
+  rv = downloader->Init(aInfo, key);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  downloader->AddCallback(aURI, aCallback);
+
+  nsCOMPtr<nsIStreamConverterService> streamconv =
+    do_GetService("@mozilla.org/streamConverters;1", &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIStreamListener> mimeConverter;
+  rv = streamconv->AsyncConvertData("multipart/mixed", "*/*", downloader, nullptr,
+                                    getter_AddRefs(mimeConverter));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  // Add the package to the hashtable.
+  mDownloadingPackages.Put(key, downloader);
+
+  return channel->AsyncOpen(mimeConverter, nullptr);
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/PackagedAppService.h
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 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/. */
+
+#ifndef mozilla_net_PackagedAppService_h
+#define mozilla_net_PackagedAppService_h
+
+#include "nsIPackagedAppService.h"
+#include "nsILoadContextInfo.h"
+#include "nsICacheStorage.h"
+
+namespace mozilla {
+namespace net {
+
+// This service is used to download packages from the web.
+// Individual resources in the package are saved in the browser cache. It also
+// provides an interface to asynchronously request resources from packages,
+// which are either returned from the cache if they exist and are valid,
+// or downloads the package.
+// The package format is defined at:
+//     https://w3ctag.github.io/packaging-on-the-web/#streamable-package-format
+// Downloading the package is triggered by calling requestURI(aURI, aInfo, aCallback)
+//     aURI is the subresource uri - http://domain.com/path/package!//resource.html
+//     aInfo is a nsILoadContextInfo used to pick the cache jar the resource goes into
+//     aCallback is the target of the async call to requestURI
+// When requestURI is called, a CacheEntryChecker is created to verify if the
+// resource is already in the cache. If it is, it passes it to the callback.
+// Otherwise, it starts downloading the package. When the packaged resource has
+// been downloaded, its cache entry gets passed to the callback.
+class PackagedAppService final
+  : public nsIPackagedAppService
+{
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIPACKAGEDAPPSERVICE
+
+  PackagedAppService();
+
+private:
+  ~PackagedAppService();
+
+  // This method is called if an entry wasn't found in the cache.
+  // It checks to see if the package is currently being downloaded.
+  // If so, then it simply adds the callback to that PackageAppDownloader
+  // Else it begins downloading the new package and adds it to mDownloadingPackages
+  // - aURI is the packaged resource's URL
+  // - aCallback is the listener which gets called when the requested
+  //   resource is available.
+  // - aInfo is needed because cache entries are located in separate cache jars
+  //   If a resource isn't found in the package, aCallback->OnCacheEntryAvailable
+  //   will be called with a null entry and an error result as a status.
+  nsresult OpenNewPackageInternal(nsIURI *aURI,
+                                  nsICacheEntryOpenCallback *aCallback,
+                                  nsILoadContextInfo *aInfo);
+
+  // Called by PackageAppDownloader once the download has finished
+  // (or encountered an error) to remove the package from mDownloadingPackages
+  // Should be called on the main thread.
+  nsresult NotifyPackageDownloaded(nsCString aKey);
+
+  // This class is used to write data into the cache entry corresponding to the
+  // packaged resource being downloaded.
+  // The PackagedAppDownloader will hold a ref to a CacheEntryWriter that
+  // corresponds to the entry that is currently being downloaded.
+  class CacheEntryWriter final
+    : public nsIStreamListener
+  {
+  public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSISTREAMLISTENER
+    NS_DECL_NSIREQUESTOBSERVER
+
+    // If successful, calling this static method will create a new
+    // CacheEntryWriter and will create the cache entry associated to the
+    // resource and open an output stream which we use for writing the resource's
+    // content into the cache entry.
+    static nsresult Create(nsIURI*, nsICacheStorage*, CacheEntryWriter**);
+
+    nsCOMPtr<nsICacheEntry> mEntry;
+  private:
+    CacheEntryWriter() { }
+    ~CacheEntryWriter() { }
+
+    // Static method used to write data into the cache entry
+    // Called from OnDataAvailable
+    static NS_METHOD ConsumeData(nsIInputStream *in, void *closure,
+                                 const char *fromRawSegment, uint32_t toOffset,
+                                 uint32_t count, uint32_t *writeCount);
+    // We write the data we read from the network into this stream which goes
+    // to the cache entry.
+    nsCOMPtr<nsIOutputStream> mOutputStream;
+  };
+
+  // This class is used to download a packaged app. It acts as a listener
+  // for the nsMultiMixedConv object that parses the package.
+  // There is an OnStartRequest, OnDataAvailable*, OnStopRequest sequence called
+  // for each resource
+  // The PackagedAppService holds a hash-table of the PackagedAppDownloaders
+  // that are in progress to coalesce same loads.
+  // Once the downloading is completed, it should call
+  // NotifyPackageDownloaded(packageURI), so the service releases the ref.
+  class PackagedAppDownloader final
+    : public nsIStreamListener
+  {
+  public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSISTREAMLISTENER
+    NS_DECL_NSIREQUESTOBSERVER
+
+    // Initializes mCacheStorage and saves aKey as mPackageKey which is later
+    // used to remove this object from PackagedAppService::mDownloadingPackages
+    // - aKey is a string which uniquely identifies this package within the
+    //   packagedAppService
+    nsresult Init(nsILoadContextInfo* aInfo, const nsCString &aKey);
+    // Registers a callback which gets called when the given nsIURI is downloaded
+    // aURI is the full URI of a subresource, composed of packageURI + !// + subresourcePath
+    nsresult AddCallback(nsIURI *aURI, nsICacheEntryOpenCallback *aCallback);
+
+  private:
+    ~PackagedAppDownloader() { }
+
+    // Calls all the callbacks registered for the given URI.
+    // aURI is the full URI of a subresource, composed of packageURI + !// + subresourcePath
+    // It passes the cache entry and the result when calling OnCacheEntryAvailable
+    nsresult CallCallbacks(nsIURI *aURI, nsICacheEntry *aEntry, nsresult aResult);
+    // Clears all the callbacks for this package
+    // This would get called at the end of downloading the package and would
+    // cause us to call OnCacheEntryAvailable with a null entry. This would be
+    // equivalent to a 404 when loading from the net.
+    nsresult ClearCallbacks(nsresult aResult);
+    static PLDHashOperator ClearCallbacksEnumerator(const nsACString& key,
+      nsAutoPtr<nsCOMArray<nsICacheEntryOpenCallback>>& callbackArray,
+      void* arg);
+    // Returns a URI with the subresource's full URI
+    // The request must be QIable to nsIResponseHeadProvider since it looks
+    // at the Content-Location header to compute the full path.
+    static nsresult GetSubresourceURI(nsIRequest * aRequest, nsIURI **aResult);
+    // Used to write data into the cache entry of the resource currently being
+    // downloaded. It is kept alive until the downloader receives OnStopRequest
+    nsRefPtr<CacheEntryWriter> mWriter;
+    // Cached value of nsICacheStorage
+    nsCOMPtr<nsICacheStorage> mCacheStorage;
+    // A hastable containing all the consumers which requested a resource and need
+    // to be notified once it is inserted into the cache.
+    // The key is a subresource URI - http://example.com/package.pak!//res.html
+    // Should only be used on the main thread.
+    nsClassHashtable<nsCStringHashKey, nsCOMArray<nsICacheEntryOpenCallback>> mCallbacks;
+    // The key with which this package is inserted in
+    // PackagedAppService::mDownloadingPackages
+    nsCString mPackageKey;
+  };
+
+  // This class is used to check if a packaged resource has already been
+  // downloaded and saved into the cache.
+  // It calls aCallback->OnCacheEntryAvailable if the resource exists in the
+  // cache or PackagedAppService::OpenNewPackageInternal if it needs
+  // to be downloaded
+  class CacheEntryChecker final
+    : public nsICacheEntryOpenCallback
+  {
+  public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSICACHEENTRYOPENCALLBACK
+
+    CacheEntryChecker(nsIURI *aURI, nsICacheEntryOpenCallback * aCallback,
+                      nsILoadContextInfo *aInfo)
+      : mURI(aURI)
+      , mCallback(aCallback)
+      , mLoadContextInfo(aInfo)
+    {
+    }
+  private:
+    ~CacheEntryChecker() { }
+
+    nsCOMPtr<nsIURI> mURI;
+    nsCOMPtr<nsICacheEntryOpenCallback> mCallback;
+    nsCOMPtr<nsILoadContextInfo> mLoadContextInfo;
+  };
+
+  // A hashtable of packages that are currently being downloaded.
+  // The key is a string formed by concatenating LoadContextInfo and package URI
+  // Should only be used on the main thread.
+  nsRefPtrHashtable<nsCStringHashKey, PackagedAppDownloader> mDownloadingPackages;
+};
+
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_PackagedAppService_h
\ No newline at end of file
--- a/netwerk/protocol/http/moz.build
+++ b/netwerk/protocol/http/moz.build
@@ -29,16 +29,17 @@ EXPORTS += [
 ]
 
 EXPORTS.mozilla.net += [
     'HttpBaseChannel.h',
     'HttpChannelChild.h',
     'HttpChannelParent.h',
     'HttpInfo.h',
     'NullHttpChannel.h',
+    'PackagedAppService.h',
     'PHttpChannelParams.h',
     'PSpdyPush.h',
     'TimingStruct.h',
 ]
 
 # ASpdySession.cpp and nsHttpAuthCache cannot be built in unified mode because
 # they use plarena.h.
 SOURCES += [
@@ -73,16 +74,17 @@ UNIFIED_SOURCES += [
     'nsHttpHeaderArray.cpp',
     'nsHttpNTLMAuth.cpp',
     'nsHttpPipeline.cpp',
     'nsHttpRequestHead.cpp',
     'nsHttpResponseHead.cpp',
     'nsHttpTransaction.cpp',
     'NullHttpChannel.cpp',
     'NullHttpTransaction.cpp',
+    'PackagedAppService.cpp',
     'SpdyPush31.cpp',
     'SpdySession31.cpp',
     'SpdyStream31.cpp',
     'SpdyZlibReporter.cpp',
     'TunnelUtils.cpp',
 ]
 
 # These files cannot be built in unified mode because of OS X headers.
--- a/netwerk/protocol/http/nsHttp.cpp
+++ b/netwerk/protocol/http/nsHttp.cpp
@@ -35,17 +35,17 @@ enum {
 // the atom table is destroyed.  The structure and value string are allocated
 // as one contiguous block.
 
 struct HttpHeapAtom {
     struct HttpHeapAtom *next;
     char                 value[1];
 };
 
-static PLDHashTable2       *sAtomTable;
+static PLDHashTable        *sAtomTable;
 static struct HttpHeapAtom *sHeapAtoms = nullptr;
 static Mutex               *sLock = nullptr;
 
 HttpHeapAtom *
 NewHeapAtom(const char *value) {
     int len = strlen(value);
 
     HttpHeapAtom *a =
@@ -98,18 +98,18 @@ nsHttp::CreateAtomTable()
 
     if (!sLock) {
         sLock = new Mutex("nsHttp.sLock");
     }
 
     // The initial length for this table is a value greater than the number of
     // known atoms (NUM_HTTP_ATOMS) because we expect to encounter a few random
     // headers right off the bat.
-    sAtomTable = new PLDHashTable2(&ops, sizeof(PLDHashEntryStub),
-                                   NUM_HTTP_ATOMS + 10);
+    sAtomTable = new PLDHashTable(&ops, sizeof(PLDHashEntryStub),
+                                  NUM_HTTP_ATOMS + 10);
 
     // fill the table with our known atoms
     const char *const atoms[] = {
 #define HTTP_ATOM(_name, _value) nsHttp::_name._val,
 #include "nsHttpAtomList.h"
 #undef HTTP_ATOM
         nullptr
     };
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -74,16 +74,17 @@
 #include "CacheObserver.h"
 #include "mozilla/Telemetry.h"
 #include "AlternateServices.h"
 #include "InterceptedChannel.h"
 #include "nsIHttpPushListener.h"
 #include "nsIX509Cert.h"
 #include "ScopedNSSTypes.h"
 #include "nsNullPrincipal.h"
+#include "nsIPackagedAppService.h"
 
 namespace mozilla { namespace net {
 
 namespace {
 
 // Monotonically increasing ID for generating unique cache entries per
 // intercepted channel.
 static uint64_t gNumIntercepted = 0;
@@ -244,16 +245,17 @@ nsHttpChannel::nsHttpChannel()
     , mRequestTimeInitialized(false)
     , mCacheEntryIsReadOnly(false)
     , mCacheEntryIsWriteOnly(false)
     , mCacheEntriesToWaitFor(0)
     , mHasQueryString(0)
     , mConcurentCacheAccess(0)
     , mIsPartialRequest(0)
     , mHasAutoRedirectVetoNotifier(0)
+    , mIsPackagedAppResource(0)
     , mPushedStream(nullptr)
     , mLocalBlocklist(false)
     , mWarningReporter(nullptr)
     , mDidReval(false)
 {
     LOG(("Creating nsHttpChannel [this=%p]\n", this));
     mChannelCreationTime = PR_Now();
     mChannelCreationTimestamp = TimeStamp::Now();
@@ -3334,16 +3336,23 @@ nsHttpChannel::OnCacheEntryAvailableInte
     }
 
     if (NS_FAILED(rv) && (mLoadFlags & LOAD_ONLY_FROM_CACHE)) {
         // If we have a fallback URI (and we're not already
         // falling back), process the fallback asynchronously.
         if (!mFallbackChannel && !mFallbackKey.IsEmpty()) {
             return AsyncCall(&nsHttpChannel::HandleAsyncFallback);
         }
+
+        if (mIsPackagedAppResource) {
+            // We need to return FILE_NOT_FOUND in case an error occurs
+            // or we will take the user to the <you're offline> screen.
+            return NS_ERROR_FILE_NOT_FOUND;
+        }
+
         return NS_ERROR_DOCUMENT_NOT_CACHED;
     }
 
     if (NS_FAILED(rv)) {
         return rv;
     }
 
     // We may be waiting for more callbacks...
@@ -4765,16 +4774,23 @@ nsHttpChannel::AsyncOpen(nsIStreamListen
     LOG(("nsHttpChannel::AsyncOpen [this=%p]\n", this));
 
     NS_ENSURE_ARG_POINTER(listener);
     NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
     NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
 
     nsresult rv;
 
+    MOZ_ASSERT(NS_IsMainThread());
+    if (gHttpHandler->PackagedAppsEnabled()) {
+        nsAutoCString path;
+        mURI->GetPath(path);
+        mIsPackagedAppResource = path.Find(PACKAGED_APP_TOKEN) != kNotFound;
+    }
+
     rv = NS_CheckPortSafety(mURI);
     if (NS_FAILED(rv)) {
         ReleaseListeners();
         return rv;
     }
 
     if (ShouldIntercept()) {
         mInterceptCache = MAYBE_INTERCEPT;
@@ -4977,16 +4993,32 @@ nsHttpChannel::BeginConnect()
         }
     }
 
     // If mTimingEnabled flag is not set after OnModifyRequest() then
     // clear the already recorded AsyncOpen value for consistency.
     if (!mTimingEnabled)
         mAsyncOpenTime = TimeStamp();
 
+    if (mIsPackagedAppResource) {
+        // If this is a packaged app resource, the content will be fetched
+        // by the packaged app service into the cache, and the cache entry will
+        // be passed to OnCacheEntryAvailable.
+        mLoadFlags |= LOAD_ONLY_FROM_CACHE;
+        mLoadFlags |= LOAD_FROM_CACHE;
+        mLoadFlags &= ~VALIDATE_ALWAYS;
+        nsCOMPtr<nsIPackagedAppService> pas =
+          do_GetService("@mozilla.org/network/packaged-app-service;1", &rv);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+          return rv;
+        }
+
+        return pas->RequestURI(mURI, GetLoadContextInfo(this), this);
+    }
+
     // when proxying only use the pipeline bit if ProxyPipelining() allows it.
     if (!mConnectionInfo->UsingConnect() && mConnectionInfo->UsingHttpProxy()) {
         mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
         if (gHttpHandler->ProxyPipelining())
             mCaps |= NS_HTTP_ALLOW_PIPELINING;
     }
 
     // if this somehow fails we can go on without it
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -470,16 +470,21 @@ private:
     // when true, after we finish read from cache we must check all data
     // had been loaded from cache. If not, then an error has to be propagated
     // to the consumer.
     uint32_t                          mConcurentCacheAccess : 1;
     // whether the request is setup be byte-range
     uint32_t                          mIsPartialRequest : 1;
     // true iff there is AutoRedirectVetoNotifier on the stack
     uint32_t                          mHasAutoRedirectVetoNotifier : 1;
+    // Whether fetching the content is meant to be handled by the
+    // packaged app service, which behaves like a caching layer.
+    // Upon successfully fetching the package, the resource will be placed in
+    // the cache, and served by calling OnCacheEntryAvailable.
+    uint32_t                          mIsPackagedAppResource : 1;
 
     nsTArray<nsContinueRedirectionFunc> mRedirectFuncStack;
 
     // Needed for accurate DNS timing
     nsRefPtr<nsDNSPrefetch>           mDNSPrefetch;
 
     Http2PushedStream                 *mPushedStream;
     // True if the channel's principal was found on a phishing, malware, or
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -279,16 +279,22 @@ nsHttpHandler::Init()
         prefBranch->AddObserver(TELEMETRY_ENABLED, this, true);
         prefBranch->AddObserver(H2MANDATORY_SUITE, this, true);
         prefBranch->AddObserver(HTTP_PREF("tcp_keepalive.short_lived_connections"), this, true);
         prefBranch->AddObserver(HTTP_PREF("tcp_keepalive.long_lived_connections"), this, true);
         prefBranch->AddObserver(SAFE_HINT_HEADER_VALUE, this, true);
         PrefsChanged(prefBranch, nullptr);
     }
 
+    rv = Preferences::AddBoolVarCache(&mPackagedAppsEnabled,
+        "network.http.enable-packaged-apps", false);
+    if (NS_FAILED(rv)) {
+        mPackagedAppsEnabled = false;
+    }
+
     nsHttpChannelAuthProvider::InitializePrefs();
 
     mMisc.AssignLiteral("rv:" MOZILLA_UAVERSION);
 
     mCompatFirefox.AssignLiteral("Firefox/" MOZILLA_UAVERSION);
 
     nsCOMPtr<nsIXULAppInfo> appInfo =
         do_GetService("@mozilla.org/xre/app-info;1");
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -74,16 +74,17 @@ public:
 
     nsHttpVersion  HttpVersion()             { return mHttpVersion; }
     nsHttpVersion  ProxyHttpVersion()        { return mProxyHttpVersion; }
     uint8_t        ReferrerLevel()           { return mReferrerLevel; }
     bool           SpoofReferrerSource()     { return mSpoofReferrerSource; }
     uint8_t        ReferrerTrimmingPolicy()  { return mReferrerTrimmingPolicy; }
     uint8_t        ReferrerXOriginPolicy()   { return mReferrerXOriginPolicy; }
     bool           SendSecureXSiteReferrer() { return mSendSecureXSiteReferrer; }
+    bool           PackagedAppsEnabled()     { return mPackagedAppsEnabled; }
     uint8_t        RedirectionLimit()        { return mRedirectionLimit; }
     PRIntervalTime IdleTimeout()             { return mIdleTimeout; }
     PRIntervalTime SpdyTimeout()             { return mSpdyTimeout; }
     PRIntervalTime ResponseTimeout() {
       return mResponseTimeoutEnabled ? mResponseTimeout : 0;
     }
     PRIntervalTime ResponseTimeoutEnabled()  { return mResponseTimeoutEnabled; }
     uint32_t       NetworkChangedTimeout()   { return mNetworkChangedTimeout; }
@@ -403,16 +404,19 @@ private:
     uint16_t mMaxOptimisticPipelinedRequests;
     bool     mPipelineAggressive;
     int64_t  mMaxPipelineObjectSize;
     bool     mPipelineRescheduleOnTimeout;
     PRIntervalTime mPipelineRescheduleTimeout;
     PRIntervalTime mPipelineReadTimeout;
     nsCOMPtr<nsITimer> mPipelineTestTimer;
 
+    // Whether a URL containing !// should be interpreted as a packaged app channel
+    bool mPackagedAppsEnabled = false;
+
     uint8_t  mRedirectionLimit;
 
     // we'll warn the user if we load an URL containing a userpass field
     // unless its length is less than this threshold.  this warning is
     // intended to protect the user against spoofing attempts that use
     // the userpass field of the URL to obscure the actual origin server.
     uint8_t  mPhishyUserPassLength;
 
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_packaged_app_channel.js
@@ -0,0 +1,138 @@
+//
+// This tests checks that a regular http channel can be used to load a
+// packaged app resource. It sets the network.http.enable-packaged-apps pref
+// to enable this feature, then creates a channel and loads the resource.
+// reset_pref resets the pref at the end of the test.
+
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "uri", function() {
+  return "http://localhost:" + httpserver.identity.primaryPort;
+});
+
+const nsIBinaryInputStream = Components.Constructor("@mozilla.org/binaryinputstream;1",
+                              "nsIBinaryInputStream",
+                              "setInputStream"
+                              );
+
+
+function make_channel(url) {
+  var ios = Cc["@mozilla.org/network/io-service;1"].
+            getService(Ci.nsIIOService);
+  return ios.newChannel2(url,
+                         "",
+                         null,
+                         null,      // aLoadingNode
+                         Services.scriptSecurityManager.getSystemPrincipal(),
+                         null,      // aTriggeringPrincipal
+                         Ci.nsILoadInfo.SEC_NORMAL,
+                         Ci.nsIContentPolicy.TYPE_OTHER);
+}
+
+function Listener(callback) {
+    this._callback = callback;
+}
+
+Listener.prototype = {
+    gotStartRequest: false,
+    available: -1,
+    gotStopRequest: false,
+    QueryInterface: function(iid) {
+        if (iid.equals(Ci.nsISupports) ||
+            iid.equals(Ci.nsIRequestObserver))
+            return this;
+        throw Cr.NS_ERROR_NO_INTERFACE;
+    },
+    onDataAvailable: function(request, ctx, stream, offset, count) {
+        try {
+            this.available = stream.available();
+            do_check_eq(this.available, count);
+            // Need to consume stream to avoid assertion
+            var str = new nsIBinaryInputStream(stream).readBytes(count);
+            equal(str, "<html>\r\n  <head>\r\n    <script src=\"/scripts/app.js\"></script>\r\n    ...\r\n  </head>\r\n  ...\r\n</html>\r\n", "check proper content");
+        }
+        catch (ex) {
+            do_throw(ex);
+        }
+    },
+    onStartRequest: function(request, ctx) {
+        this.gotStartRequest = true;
+    },
+    onStopRequest: function(request, ctx, status) {
+        this.gotStopRequest = true;
+        do_check_eq(status, 0);
+        if (this._callback) {
+            this._callback.call(null, this);
+        }
+    }
+};
+
+// The package content
+// getData formats it as described at http://www.w3.org/TR/web-packaging/#streamable-package-format
+var testData = {
+  content: [
+   { headers: ["Content-Location: /index.html", "Content-Type: text/html"], data: "<html>\r\n  <head>\r\n    <script src=\"/scripts/app.js\"></script>\r\n    ...\r\n  </head>\r\n  ...\r\n</html>\r\n", type: "text/html" },
+   { headers: ["Content-Location: /scripts/app.js", "Content-Type: text/javascript"], data: "module Math from '/scripts/helpers/math.js';\r\n...\r\n", type: "text/javascript" },
+   { headers: ["Content-Location: /scripts/helpers/math.js", "Content-Type: text/javascript"], data: "export function sum(nums) { ... }\r\n...\r\n", type: "text/javascript" }
+  ],
+  token : "gc0pJq0M:08jU534c0p",
+  getData: function() {
+    var str = "";
+    for (var i in this.content) {
+      str += "--" + this.token + "\r\n";
+      for (var j in this.content[i].headers) {
+        str += this.content[i].headers[j] + "\r\n";
+      }
+      str += "\r\n";
+      str += this.content[i].data + "\r\n";
+    }
+
+    str += "--" + this.token + "--";
+    return str;
+  }
+}
+
+function contentHandler(metadata, response)
+{
+  response.setHeader("Content-Type", 'application/package');
+  var body = testData.getData();
+  response.bodyOutputStream.write(body, body.length);
+}
+
+var httpserver = null;
+var originalPref = false;
+
+function run_test()
+{
+  // setup test
+  httpserver = new HttpServer();
+  httpserver.registerPathHandler("/package", contentHandler);
+  httpserver.start(-1);
+
+  // Enable the feature and save the original pref value
+  originalPref = Services.prefs.getBoolPref("network.http.enable-packaged-apps");
+  Services.prefs.setBoolPref("network.http.enable-packaged-apps", true);
+  do_register_cleanup(reset_pref);
+
+  add_test(test_channel);
+
+  // run tests
+  run_next_test();
+}
+
+function test_channel() {
+  var channel = make_channel(uri+"/package!//index.html");
+  channel.asyncOpen(new Listener(function(l) {
+    // XXX: no content length available for this resource
+    //do_check_true(channel.contentLength > 0);
+    do_check_true(l.gotStartRequest);
+    do_check_true(l.gotStopRequest);
+    run_next_test();
+  }), null);
+}
+
+function reset_pref() {
+  // Set the pref to its original value
+  Services.prefs.setBoolPref("network.http.enable-packaged-apps", originalPref);
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_packaged_app_service.js
@@ -0,0 +1,255 @@
+//
+// This file tests the packaged app service - nsIPackagedAppService
+// NOTE: The order in which tests are run is important
+//       If you need to add more tests, it's best to define them at the end
+//       of the file and to add them at the end of run_test
+//
+// ----------------------------------------------------------------------------
+//
+// test_bad_args
+//     - checks that calls to nsIPackagedAppService::requestURI do not accept a null argument
+// test_callback_gets_called
+//     - checks the regular use case -> requesting a resource should asynchronously return an entry
+// test_same_content
+//     - makes another request for the same file, and checks that the same content is returned
+// test_request_number
+//     - this test does not make a request, but checks that the package has only
+//       been requested once. The entry returned by the call to requestURI in
+//       test_same_content should be returned from the cache.
+//
+// test_package_does_not_exist
+//     - checks that requesting a file from a <package that does not exist>
+//       calls the listener with an error code
+// test_file_does_not_exist
+//     - checks that requesting a <subresource that doesn't exist> inside a
+//       package calls the listener with an error code
+//
+// test_bad_package
+//    - tests that a package with missing headers for some of the files
+//      will still return files that are correct
+// test_bad_package_404
+//    - tests that a request for a missing subresource doesn't hang if
+//      if the last file in the package is missing some headers
+
+Cu.import('resource://gre/modules/LoadContextInfo.jsm');
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://gre/modules/Services.jsm");
+
+// The number of times this package has been requested
+// This number might be reset by tests that use it
+var packagedAppRequestsMade = 0;
+// The default content handler. It just responds by sending the package data
+// with an application/package content type
+function packagedAppContentHandler(metadata, response)
+{
+  packagedAppRequestsMade++;
+  response.setHeader("Content-Type", 'application/package');
+  var body = testData.getData();
+  response.bodyOutputStream.write(body, body.length);
+}
+
+// The package content
+// getData formats it as described at http://www.w3.org/TR/web-packaging/#streamable-package-format
+var testData = {
+  content: [
+   { headers: ["Content-Location: /index.html", "Content-Type: text/html"], data: "<html>\r\n  <head>\r\n    <script src=\"/scripts/app.js\"></script>\r\n    ...\r\n  </head>\r\n  ...\r\n</html>\r\n", type: "text/html" },
+   { headers: ["Content-Location: /scripts/app.js", "Content-Type: text/javascript"], data: "module Math from '/scripts/helpers/math.js';\r\n...\r\n", type: "text/javascript" },
+   { headers: ["Content-Location: /scripts/helpers/math.js", "Content-Type: text/javascript"], data: "export function sum(nums) { ... }\r\n...\r\n", type: "text/javascript" }
+  ],
+  token : "gc0pJq0M:08jU534c0p",
+  getData: function() {
+    var str = "";
+    for (var i in this.content) {
+      str += "--" + this.token + "\r\n";
+      for (var j in this.content[i].headers) {
+        str += this.content[i].headers[j] + "\r\n";
+      }
+      str += "\r\n";
+      str += this.content[i].data + "\r\n";
+    }
+
+    str += "--" + this.token + "--";
+    return str;
+  }
+}
+
+XPCOMUtils.defineLazyGetter(this, "uri", function() {
+  return "http://localhost:" + httpserver.identity.primaryPort;
+});
+
+// The active http server initialized in run_test
+var httpserver = null;
+// The packaged app service initialized in run_test
+var paservice = null;
+// This variable is set before requestURI is called. The listener uses this variable
+// to check the correct resource path for the returned entry
+var packagePath = null;
+
+function run_test()
+{
+  // setup test
+  httpserver = new HttpServer();
+  httpserver.registerPathHandler("/package", packagedAppContentHandler);
+  httpserver.registerPathHandler("/304Package", packagedAppContentHandler);
+  httpserver.registerPathHandler("/badPackage", packagedAppBadContentHandler);
+  httpserver.start(-1);
+
+  paservice = Cc["@mozilla.org/network/packaged-app-service;1"]
+                     .getService(Ci.nsIPackagedAppService);
+  ok(!!paservice, "test service exists");
+
+  add_test(test_bad_args);
+
+  add_test(test_callback_gets_called);
+  add_test(test_same_content);
+  add_test(test_request_number);
+
+  add_test(test_package_does_not_exist);
+  add_test(test_file_does_not_exist);
+
+  add_test(test_bad_package);
+  add_test(test_bad_package_404);
+
+  // run tests
+  run_next_test();
+}
+
+// This checks the proper metadata is on the entry
+var metadataListener = {
+  onMetaDataElement: function(key, value) {
+    if (key == 'response-head')
+      equal(value, "HTTP/1.1 200 \r\nContent-Location: /index.html\r\nContent-Type: text/html\r\n");
+    else if (key == 'request-method')
+      equal(value, "GET");
+    else
+      ok(false, "unexpected metadata key")
+  }
+}
+
+// A listener we use to check the proper cache entry is returned by the service
+// NOTE: this listener only checks the content of index.html
+//       Don't use it when requesting other packaged resources! :)
+var cacheListener = {
+  onCacheEntryCheck: function() { return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED; },
+  onCacheEntryAvailable: function (entry, isnew, appcache, status) {
+    ok(!!entry, "Needs to have an entry");
+    equal(status, Cr.NS_OK, "status is NS_OK");
+    equal(entry.key, uri + packagePath + "!//index.html", "Check entry has correct name");
+    entry.visitMetaData(metadataListener);
+    var inputStream = entry.openInputStream(0);
+    pumpReadStream(inputStream, function(read) {
+        inputStream.close();
+        equal(read,"<html>\r\n  <head>\r\n    <script src=\"/scripts/app.js\"></script>\r\n    ...\r\n  </head>\r\n  ...\r\n</html>\r\n"); // not using do_check_eq since logger will fail for the 1/4MB string
+    });
+    run_next_test();
+  }
+};
+
+// ----------------------------------------------------------------------------
+
+// These calls should fail, since one of the arguments is invalid or null
+function test_bad_args() {
+  Assert.throws(() => { paservice.requestURI(createURI("http://test.com"), LoadContextInfo.default, cacheListener); }, "url's with no !// aren't allowed");
+  Assert.throws(() => { paservice.requestURI(createURI("http://test.com/package!//test"), LoadContextInfo.default, null); }, "should have a callback");
+  Assert.throws(() => { paservice.requestURI(null, LoadContextInfo.default, cacheListener); }, "should have a URI");
+  Assert.throws(() => { paservice.requestURI(createURI("http://test.com/package!//test"), null, cacheListener); }, "should have a LoadContextInfo");
+  run_next_test();
+}
+
+// ----------------------------------------------------------------------------
+
+// This tests that the callback gets called, and the cacheListener gets the proper content.
+function test_callback_gets_called() {
+  packagePath = "/package";
+  paservice.requestURI(createURI(uri + packagePath + "!//index.html"), LoadContextInfo.default, cacheListener);
+}
+
+// Tests that requesting the same resource returns the same content
+function test_same_content() {
+  packagePath = "/package";
+  paservice.requestURI(createURI(uri + packagePath + "!//index.html"), LoadContextInfo.default, cacheListener);
+}
+
+// Check the package has only been requested once.
+function test_request_number() {
+  equal(packagedAppRequestsMade, 1, "only one request should be made. Second should be loaded from cache");
+  run_next_test();
+}
+
+// ----------------------------------------------------------------------------
+
+// This listener checks that the requested resources are not returned
+// either because the package does not exist, or because the requested resource
+// is not contained in the package.
+var listener404 = {
+  onCacheEntryCheck: function() { return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED; },
+  onCacheEntryAvailable: function (entry, isnew, appcache, status) {
+    // XXX: it returns NS_ERROR_FAILURE for a missing package
+    // and NS_ERROR_FILE_NOT_FOUND for a missing file from the package.
+    // Maybe make them both return NS_ERROR_FILE_NOT_FOUND?
+    notEqual(status, Cr.NS_OK, "NOT FOUND");
+    ok(!entry, "There should be no entry");
+    run_next_test();
+  }
+};
+
+// Tests that an error is returned for a non existing package
+function test_package_does_not_exist() {
+  packagePath = "/package_non_existent";
+  paservice.requestURI(createURI(uri + packagePath + "!//index.html"), LoadContextInfo.default, listener404);
+}
+
+// Tests that an error is returned for a non existing resource in a package
+function test_file_does_not_exist() {
+  packagePath = "/package"; // This package exists
+  paservice.requestURI(createURI(uri + packagePath + "!//file_non_existent.html"), LoadContextInfo.default, listener404);
+}
+
+// ----------------------------------------------------------------------------
+
+// Broken package. The first and last resources do not contain a "Content-Location" header
+// and should be ignored.
+var badTestData = {
+  content: [
+   { headers: ["Content-Type: text/javascript"], data: "module Math from '/scripts/helpers/math.js';\r\n...\r\n", type: "text/javascript" },
+   { headers: ["Content-Location: /index.html", "Content-Type: text/html"], data: "<html>\r\n  <head>\r\n    <script src=\"/scripts/app.js\"></script>\r\n    ...\r\n  </head>\r\n  ...\r\n</html>\r\n", type: "text/html" },
+   { headers: ["Content-Type: text/javascript"], data: "export function sum(nums) { ... }\r\n...\r\n", type: "text/javascript" }
+  ],
+  token : "gc0pJq0M:08jU534c0p",
+  getData: function() {
+    var str = "";
+    for (var i in this.content) {
+      str += "--" + this.token + "\r\n";
+      for (var j in this.content[i].headers) {
+        str += this.content[i].headers[j] + "\r\n";
+      }
+      str += "\r\n";
+      str += this.content[i].data + "\r\n";
+    }
+
+    str += "--" + this.token + "--";
+    return str;
+  }
+}
+
+// Returns the content of the package with "Content-Location" headers missing for the first and last resource
+function packagedAppBadContentHandler(metadata, response)
+{
+  response.setHeader("Content-Type", 'application/package');
+  var body = badTestData.getData();
+  response.bodyOutputStream.write(body, body.length);
+}
+
+// Checks that the resource with the proper headers inside the bad package is still returned
+function test_bad_package() {
+  packagePath = "/badPackage";
+  paservice.requestURI(createURI(uri + packagePath + "!//index.html"), LoadContextInfo.default, cacheListener);
+}
+
+// Checks that the request for a non-existent resource doesn't hang for a bad package
+function test_bad_package_404() {
+  packagePath = "/badPackage";
+  paservice.requestURI(createURI(uri + packagePath + "!//file_non_existent.html"), LoadContextInfo.default, listener404);
+}
+
+// ----------------------------------------------------------------------------
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -16,16 +16,17 @@ support-files =
   data/test_readline7.txt
   data/test_readline8.txt
   data/signed_win.exe
   socks_client_subprocess.js
   test_link.desktop
   test_link.url
   ../../dns/effective_tld_names.dat
 
+[test_packaged_app_channel.js]
 [test_nsIBufferedOutputStream_writeFrom_block.js]
 [test_cache2-00-service-get.js]
 [test_cache2-01-basic.js]
 [test_cache2-01a-basic-readonly.js]
 [test_cache2-01b-basic-datasize.js]
 [test_cache2-01c-basic-hasmeta-only.js]
 [test_cache2-01d-basic-not-wanted.js]
 [test_cache2-01e-basic-bypass-if-busy.js]
@@ -310,8 +311,9 @@ skip-if = os != "win"
 [test_reply_without_content_type.js]
 [test_websocket_offline.js]
 [test_tls_server.js]
 # The local cert service used by this test is not currently shipped on Android
 skip-if = os == "android"