Merge m-c to fx-team, a=merge
authorWes Kocher <wkocher@mozilla.com>
Thu, 07 Jan 2016 17:17:01 -0800
changeset 279134 b6d733b1a688a55548d4754de99bbc6e8636fbdb
parent 279133 acc2927bf3a30345a0b13ddfce24814f4e61f7c5 (current diff)
parent 279059 8a7cc8b7aa556bd50c930f50770fa26661e41c03 (diff)
child 279135 ad7d6390e0f5e807e026384d9ef970b3bb24421c
push id69986
push usercbook@mozilla.com
push dateFri, 08 Jan 2016 14:23:26 +0000
treeherdermozilla-inbound@63fd92a836f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone46.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to fx-team, a=merge
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/sources.xml
@@ -16,31 +16,31 @@
   <remote fetch="https://git.mozilla.org/external/linaro" name="linaro"/>
   <!--original fetch url was git://github.com/mozilla/-->
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!--
     B2G repositories for all targets
     -->
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="676237f80cf72182500356fabc49365d3471c0e6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="62900f1e42d596c3faa2787a8bd18a8be034b558"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="edc099cff55a6a3f9ad191acfbc8cc39f36228db"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5e96ed318db1ba8037eb402724bc052240ac9e05"/>
   <!-- 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"/>
@@ -115,17 +115,17 @@
   <project name="platform/frameworks/wilhelm" path="frameworks/wilhelm" revision="f0c3b4edf597c40aae4ea311575f39c8bcf203df"/>
   <project name="platform/libcore" path="libcore" revision="baf7d8068dd501cfa338d3a8b1b87216d6ce0571"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="50c4430e32849530ced32680fd6ee98963b3f7ac"/>
   <project name="platform/ndk" path="ndk" revision="e58ef003be4306bb53a8c11331146f39e4eab31f"/>
   <project name="platform_prebuilts_misc" path="prebuilts/misc" remote="b2g" revision="c739cffa6394c06e099ea48879a20341b6163338"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="c792f0bd9fff7aea2887c60bbb3a9bbdb534ffa3"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="69d524e80cdf3981006627c65ac85f3a871238a3"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5a48c04c4bb5f079bc757e29864a42427378e051"/>
-  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="570531db9e81a871c421b27025ae558022b1015a"/>
+  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="bb356d6505f914347690c8143dbd03af427dd07e"/>
   <project name="platform/system/extras" path="system/extras" revision="576f57b6510de59c08568b53c0fb60588be8689e"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="34adfb400e031f3dd3353d92413572db5e3a7376"/>
   <project name="platform/system/netd" path="system/netd" revision="a6531f7befb49b1c81bc0de7e51c5482b308e1c5"/>
   <project name="platform/system/security" path="system/security" revision="ee8068b9e7bfb2770635062fc9c2035be2142bd8"/>
   <project name="platform/system/vold" path="system/vold" revision="42fa2a0f14f965970a4b629a176bbd2666edf017"/>
   <project name="platform/external/curl" path="external/curl" revision="e68addd988448959ea8157c5de637346b4180c33"/>
   <project name="platform/external/icu4c" path="external/icu4c" revision="d3ec7428eb276db43b7ed0544e09344a6014806c"/>
   <project name="platform/hardware/libhardware_legacy" path="hardware/libhardware_legacy" revision="76c4bf4bc430a1b8317f2f21ef735867733e50cc"/>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -16,31 +16,31 @@
   <remote fetch="https://git.mozilla.org/external/linaro" name="linaro"/>
   <!--original fetch url was git://github.com/mozilla/-->
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!--
     B2G repositories for all targets
     -->
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="676237f80cf72182500356fabc49365d3471c0e6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="62900f1e42d596c3faa2787a8bd18a8be034b558"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="edc099cff55a6a3f9ad191acfbc8cc39f36228db"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5e96ed318db1ba8037eb402724bc052240ac9e05"/>
   <!-- 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"/>
@@ -121,17 +121,17 @@
   <project name="platform/hardware/libhardware_legacy" path="hardware/libhardware_legacy" revision="68b0c269fd1889f47ecfb9119c05281e9b6db0af"/>
   <project name="platform/libcore" path="libcore" revision="e195beab082c09217318fc19250caeaf4c1bd800"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="feeb36c2bd4adfe285f98f5de92e0f3771b2c115"/>
   <project name="platform/ndk" path="ndk" revision="e58ef003be4306bb53a8c11331146f39e4eab31f"/>
   <project name="platform_prebuilts_misc" path="prebuilts/misc" remote="b2g" revision="c739cffa6394c06e099ea48879a20341b6163338"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="c792f0bd9fff7aea2887c60bbb3a9bbdb534ffa3"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="cfcef469537869947abb9aa1d656774cc2678d4c"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5a48c04c4bb5f079bc757e29864a42427378e051"/>
-  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="570531db9e81a871c421b27025ae558022b1015a"/>
+  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="bb356d6505f914347690c8143dbd03af427dd07e"/>
   <project name="platform/system/extras" path="system/extras" revision="10e78a05252b3de785f88c2d0b9ea8a428009c50"/>
   <project name="platform/system/media" path="system/media" revision="188b3e51e0a2ce1e16dc8067edef7be3d2365ad9"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="34adfb400e031f3dd3353d92413572db5e3a7376"/>
   <project name="platform/system/netd" path="system/netd" revision="3ae56364946d4a5bf5a5f83f12f9a45a30398e33"/>
   <project name="platform/system/security" path="system/security" revision="ee8068b9e7bfb2770635062fc9c2035be2142bd8"/>
   <project name="platform/system/vold" path="system/vold" revision="fe12a9e2268da653d1cd4c39ec89a42211d22f25"/>
   <!--original fetch url was http://sprdsource.spreadtrum.com:8085/b2g/android-->
   <remote fetch="https://git.mozilla.org/external/sprd-aosp" name="sprd-aosp"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -16,17 +16,17 @@
   <remote fetch="https://git.mozilla.org/external/linaro" name="linaro"/>
   <!--original fetch url was git://github.com/mozilla/-->
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!--
     B2G repositories for all targets
     -->
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="676237f80cf72182500356fabc49365d3471c0e6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="62900f1e42d596c3faa2787a8bd18a8be034b558"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk-specific things and forks -->
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -16,27 +16,27 @@
   <remote fetch="https://git.mozilla.org/external/linaro" name="linaro"/>
   <!--original fetch url was git://github.com/mozilla/-->
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!--
     B2G repositories for all targets
     -->
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="676237f80cf72182500356fabc49365d3471c0e6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="62900f1e42d596c3faa2787a8bd18a8be034b558"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <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="apitrace" path="external/apitrace" remote="apitrace" revision="edc099cff55a6a3f9ad191acfbc8cc39f36228db"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5e96ed318db1ba8037eb402724bc052240ac9e05"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <!-- 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"/>
@@ -123,17 +123,17 @@
   <project name="platform/libcore" path="libcore" revision="3552ed1686d04a65b85e56ccc24ff3fcf77725e6"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="4792069e90385889b0638e97ae62c67cdf274e22"/>
   <project name="platform/ndk" path="ndk" revision="7666b97bbaf1d645cdd6b4430a367b7a2bb53369"/>
   <project name="platform/prebuilts/misc" path="prebuilts/misc" revision="f6ab40b3257abc07741188fd173ac392575cc8d2"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="e52099755d0bd3a579130eefe8e58066cc6c0cb6"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="842e33e43a55ea44833b9e23e4d180fa17c843af"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5db24726f0f42124304195a6bdea129039eeeaeb"/>
   <project name="platform/system/bluetooth" path="system/bluetooth" revision="930ae098543881f47eac054677726ee4b998b2f8"/>
-  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="570531db9e81a871c421b27025ae558022b1015a"/>
+  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="bb356d6505f914347690c8143dbd03af427dd07e"/>
   <project name="platform_system_core" path="system/core" remote="b2g" revision="542d1f59dc331b472307e5bd043101d14d5a3a3e"/>
   <project name="platform/system/extras" path="system/extras" revision="18c1180e848e7ab8691940481f5c1c8d22c37b3e"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="34adfb400e031f3dd3353d92413572db5e3a7376"/>
   <project name="platform/system/media" path="system/media" revision="d90b836f66bf1d9627886c96f3a2d9c3007fbb80"/>
   <project name="platform/system/netd" path="system/netd" revision="56112dd7b811301b718d0643a82fd5cac9522073"/>
   <project name="platform/system/security" path="system/security" revision="f48ff68fedbcdc12b570b7699745abb6e7574907"/>
   <project name="platform/system/vold" path="system/vold" revision="8de05d4a52b5a91e7336e6baa4592f945a6ddbea"/>
   <default remote="caf" revision="refs/tags/android-4.3_r2.1" sync-j="4"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -16,30 +16,30 @@
   <remote fetch="https://git.mozilla.org/external/linaro" name="linaro"/>
   <!--original fetch url was git://github.com/mozilla/-->
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!--
     B2G repositories for all targets
     -->
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="676237f80cf72182500356fabc49365d3471c0e6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="62900f1e42d596c3faa2787a8bd18a8be034b558"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="edc099cff55a6a3f9ad191acfbc8cc39f36228db"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5e96ed318db1ba8037eb402724bc052240ac9e05"/>
   <!-- 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"/>
@@ -119,17 +119,17 @@
   <project name="platform/hardware/libhardware_legacy" path="hardware/libhardware_legacy" revision="01f436c51dc68aec7cc1c85fda6e6792b2a95066"/>
   <project name="platform/libcore" path="libcore" revision="9877ade9617bb0db6e59aa2a54719a9bc92600f3"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="46c96ace65eb1ccab05bf15b9bf8e53e443039af"/>
   <project name="platform/ndk" path="ndk" revision="cb5519af32ae7b4a9c334913a612462ecd04c5d0"/>
   <project name="platform_prebuilts_misc" path="prebuilts/misc" remote="b2g" revision="c739cffa6394c06e099ea48879a20341b6163338"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="6aa61f8557a22039a30b42b7f283996381fd625d"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="b562b01c93de9578d5db537b6a602a38e1aaa0ce"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="387f03e815f57d536dd922706db1622bddba8d81"/>
-  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="570531db9e81a871c421b27025ae558022b1015a"/>
+  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="bb356d6505f914347690c8143dbd03af427dd07e"/>
   <project name="platform/system/extras" path="system/extras" revision="5356165f67f4a81c2ef28671c13697f1657590df"/>
   <project name="platform/system/media" path="system/media" revision="be0e2fe59a8043fa5200f75697df9220a99abe9d"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="34adfb400e031f3dd3353d92413572db5e3a7376"/>
   <project name="platform/system/netd" path="system/netd" revision="36704b0da24debcab8090156568ac236315036bb"/>
   <project name="platform/system/security" path="system/security" revision="583374f69f531ba68fc3dcbff1f74893d2a96406"/>
   <project name="platform/system/vold" path="system/vold" revision="d4455b8cf361f8353e8aebac15ffd64b4aedd2b9"/>
   <project name="platform/external/icu4c" path="external/icu4c" remote="aosp" revision="b4c6379528887dc25ca9991a535a8d92a61ad6b6"/>
   <project name="platform_frameworks_av" path="frameworks/av" remote="b2g" revision="2da3a2d5100f8afa1229bb50aa2a29ea0aaf8417"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -16,30 +16,30 @@
   <remote fetch="https://git.mozilla.org/external/linaro" name="linaro"/>
   <!--original fetch url was git://github.com/mozilla/-->
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!--
     B2G repositories for all targets
     -->
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="676237f80cf72182500356fabc49365d3471c0e6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="62900f1e42d596c3faa2787a8bd18a8be034b558"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="be4b291a90b371b41b62ade68c31ad173bb87baa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="edc099cff55a6a3f9ad191acfbc8cc39f36228db"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5e96ed318db1ba8037eb402724bc052240ac9e05"/>
   <!-- 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"/>
@@ -131,17 +131,17 @@
   <project name="platform/hardware/libhardware_legacy" path="hardware/libhardware_legacy" revision="8d075b4d5e9e032b18fbc8b5def63827d1b4a30d"/>
   <project name="platform/libcore" path="libcore" revision="bdec7d684c083760bef7bc4ba2429cceccaaf7d0"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="27bcc086236cedd31c056303e255c6d0ea3d4a50"/>
   <project name="platform/ndk" path="ndk" revision="42e85f81cc6c74af145056ee80b06e520cccb9a7"/>
   <project name="platform_prebuilts_misc" path="prebuilts/misc" remote="b2g" revision="25b96077aeae7bd0e3a5e7c284fb636664337013"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="1d080491f26dfdfd76d5bbc3e6b40c660e8565af"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="61a10cbd19d6b7fc052a8cb92dfa1b37b93754f3"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="9e892a67a01671f312c76b0880dedaa6ba478148"/>
-  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="570531db9e81a871c421b27025ae558022b1015a"/>
+  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="bb356d6505f914347690c8143dbd03af427dd07e"/>
   <project name="platform/system/extras" path="system/extras" revision="47fa016e2248b80aebd5928402c7409f8e0ca64e"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="34adfb400e031f3dd3353d92413572db5e3a7376"/>
   <project name="platform/system/media" path="system/media" revision="70bfebc66d9c6a4c614a8c7efde90e8e7e1d8641"/>
   <project name="platform/system/netd" path="system/netd" revision="d113f0ceefa9ce29eb3c86e2d23c7417a70b4048"/>
   <project name="platform/system/security" path="system/security" revision="94e1617f6f2bc2286d005e79cffa6bf0721b06b3"/>
   <project name="platform/system/vold" path="system/vold" revision="c065e301e38ea0c241164e2a373e1ecefbeaf2ec"/>
   <project name="platform_frameworks_av" path="frameworks/av" remote="b2g" revision="479a404164986b3e95212eecdae7e67da4fba9ed"/>
   <project name="platform_frameworks_base" path="frameworks/base" remote="b2g" revision="10592803994aa01fa3dc0d0bd36d0d29f006d779"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -16,17 +16,17 @@
   <remote fetch="https://git.mozilla.org/external/linaro" name="linaro"/>
   <!--original fetch url was git://github.com/mozilla/-->
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!--
     B2G repositories for all targets
     -->
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="676237f80cf72182500356fabc49365d3471c0e6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="62900f1e42d596c3faa2787a8bd18a8be034b558"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk-specific things and forks -->
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -16,31 +16,31 @@
   <remote fetch="https://git.mozilla.org/external/linaro" name="linaro"/>
   <!--original fetch url was git://github.com/mozilla/-->
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!--
     B2G repositories for all targets
     -->
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="676237f80cf72182500356fabc49365d3471c0e6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="62900f1e42d596c3faa2787a8bd18a8be034b558"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="edc099cff55a6a3f9ad191acfbc8cc39f36228db"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5e96ed318db1ba8037eb402724bc052240ac9e05"/>
   <!-- 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"/>
@@ -114,17 +114,17 @@
   <project name="platform/frameworks/wilhelm" path="frameworks/wilhelm" revision="f0c3b4edf597c40aae4ea311575f39c8bcf203df"/>
   <project name="platform/libcore" path="libcore" revision="baf7d8068dd501cfa338d3a8b1b87216d6ce0571"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="50c4430e32849530ced32680fd6ee98963b3f7ac"/>
   <project name="platform/ndk" path="ndk" revision="e58ef003be4306bb53a8c11331146f39e4eab31f"/>
   <project name="platform_prebuilts_misc" path="prebuilts/misc" remote="b2g" revision="c739cffa6394c06e099ea48879a20341b6163338"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="c792f0bd9fff7aea2887c60bbb3a9bbdb534ffa3"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="69d524e80cdf3981006627c65ac85f3a871238a3"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5a48c04c4bb5f079bc757e29864a42427378e051"/>
-  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="570531db9e81a871c421b27025ae558022b1015a"/>
+  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="bb356d6505f914347690c8143dbd03af427dd07e"/>
   <project name="platform/system/extras" path="system/extras" revision="576f57b6510de59c08568b53c0fb60588be8689e"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="34adfb400e031f3dd3353d92413572db5e3a7376"/>
   <project name="platform/system/netd" path="system/netd" revision="a6531f7befb49b1c81bc0de7e51c5482b308e1c5"/>
   <project name="platform/system/security" path="system/security" revision="ee8068b9e7bfb2770635062fc9c2035be2142bd8"/>
   <project name="platform/system/vold" path="system/vold" revision="42fa2a0f14f965970a4b629a176bbd2666edf017"/>
   <project name="platform/external/curl" path="external/curl" revision="e68addd988448959ea8157c5de637346b4180c33"/>
   <project name="platform/external/icu4c" path="external/icu4c" revision="d3ec7428eb276db43b7ed0544e09344a6014806c"/>
   <project name="platform/hardware/libhardware_legacy" path="hardware/libhardware_legacy" revision="76c4bf4bc430a1b8317f2f21ef735867733e50cc"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "676237f80cf72182500356fabc49365d3471c0e6", 
+        "git_revision": "62900f1e42d596c3faa2787a8bd18a8be034b558", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "8e1054115b6145d46f0f2a8fa1b7ef8434b19f95", 
+    "revision": "e51159a1a86de1eb0e0dc2a3b84dfd03e8be8b8b", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4-kk/sources.xml
+++ b/b2g/config/nexus-4-kk/sources.xml
@@ -16,31 +16,31 @@
   <remote fetch="https://git.mozilla.org/external/linaro" name="linaro"/>
   <!--original fetch url was git://github.com/mozilla/-->
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!--
     B2G repositories for all targets
     -->
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="676237f80cf72182500356fabc49365d3471c0e6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="62900f1e42d596c3faa2787a8bd18a8be034b558"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="edc099cff55a6a3f9ad191acfbc8cc39f36228db"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5e96ed318db1ba8037eb402724bc052240ac9e05"/>
   <!-- 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"/>
@@ -121,17 +121,17 @@
   <project name="platform/hardware/libhardware_legacy" path="hardware/libhardware_legacy" revision="01f436c51dc68aec7cc1c85fda6e6792b2a95066"/>
   <project name="platform/libcore" path="libcore" revision="9877ade9617bb0db6e59aa2a54719a9bc92600f3"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="46c96ace65eb1ccab05bf15b9bf8e53e443039af"/>
   <project name="platform/ndk" path="ndk" revision="cb5519af32ae7b4a9c334913a612462ecd04c5d0"/>
   <project name="platform_prebuilts_misc" path="prebuilts/misc" remote="b2g" revision="c739cffa6394c06e099ea48879a20341b6163338"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="6aa61f8557a22039a30b42b7f283996381fd625d"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="b562b01c93de9578d5db537b6a602a38e1aaa0ce"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="387f03e815f57d536dd922706db1622bddba8d81"/>
-  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="570531db9e81a871c421b27025ae558022b1015a"/>
+  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="bb356d6505f914347690c8143dbd03af427dd07e"/>
   <project name="platform/system/extras" path="system/extras" revision="5356165f67f4a81c2ef28671c13697f1657590df"/>
   <project name="platform/system/media" path="system/media" revision="be0e2fe59a8043fa5200f75697df9220a99abe9d"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="34adfb400e031f3dd3353d92413572db5e3a7376"/>
   <project name="platform/system/netd" path="system/netd" revision="36704b0da24debcab8090156568ac236315036bb"/>
   <project name="platform/system/security" path="system/security" revision="583374f69f531ba68fc3dcbff1f74893d2a96406"/>
   <project name="platform/system/vold" path="system/vold" revision="d4455b8cf361f8353e8aebac15ffd64b4aedd2b9"/>
   <project name="platform/external/icu4c" path="external/icu4c" remote="aosp" revision="b4c6379528887dc25ca9991a535a8d92a61ad6b6"/>
   <project name="platform_frameworks_av" path="frameworks/av" remote="b2g" revision="2da3a2d5100f8afa1229bb50aa2a29ea0aaf8417"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -16,28 +16,28 @@
   <remote fetch="https://git.mozilla.org/external/linaro" name="linaro"/>
   <!--original fetch url was git://github.com/mozilla/-->
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!--
     B2G repositories for all targets
     -->
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="676237f80cf72182500356fabc49365d3471c0e6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="62900f1e42d596c3faa2787a8bd18a8be034b558"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <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="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="edc099cff55a6a3f9ad191acfbc8cc39f36228db"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5e96ed318db1ba8037eb402724bc052240ac9e05"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <!-- 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"/>
@@ -124,17 +124,17 @@
   <project name="platform/libcore" path="libcore" revision="3552ed1686d04a65b85e56ccc24ff3fcf77725e6"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="4792069e90385889b0638e97ae62c67cdf274e22"/>
   <project name="platform/ndk" path="ndk" revision="7666b97bbaf1d645cdd6b4430a367b7a2bb53369"/>
   <project name="platform/prebuilts/misc" path="prebuilts/misc" revision="f6ab40b3257abc07741188fd173ac392575cc8d2"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="e52099755d0bd3a579130eefe8e58066cc6c0cb6"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="842e33e43a55ea44833b9e23e4d180fa17c843af"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5db24726f0f42124304195a6bdea129039eeeaeb"/>
   <project name="platform/system/bluetooth" path="system/bluetooth" revision="930ae098543881f47eac054677726ee4b998b2f8"/>
-  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="570531db9e81a871c421b27025ae558022b1015a"/>
+  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="bb356d6505f914347690c8143dbd03af427dd07e"/>
   <project name="platform_system_core" path="system/core" remote="b2g" revision="542d1f59dc331b472307e5bd043101d14d5a3a3e"/>
   <project name="platform/system/extras" path="system/extras" revision="18c1180e848e7ab8691940481f5c1c8d22c37b3e"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="34adfb400e031f3dd3353d92413572db5e3a7376"/>
   <project name="platform/system/media" path="system/media" revision="d90b836f66bf1d9627886c96f3a2d9c3007fbb80"/>
   <project name="platform/system/netd" path="system/netd" revision="56112dd7b811301b718d0643a82fd5cac9522073"/>
   <project name="platform/system/security" path="system/security" revision="f48ff68fedbcdc12b570b7699745abb6e7574907"/>
   <project name="platform/system/vold" path="system/vold" revision="8de05d4a52b5a91e7336e6baa4592f945a6ddbea"/>
   <default remote="caf" revision="refs/tags/android-4.3_r2.1" sync-j="4"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -16,31 +16,31 @@
   <remote fetch="https://git.mozilla.org/external/linaro" name="linaro"/>
   <!--original fetch url was git://github.com/mozilla/-->
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!--
     B2G repositories for all targets
     -->
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="676237f80cf72182500356fabc49365d3471c0e6"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="62900f1e42d596c3faa2787a8bd18a8be034b558"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="99c333dab00ed79baff9e1cf76b320aee8e1c123"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="be4b291a90b371b41b62ade68c31ad173bb87baa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="edc099cff55a6a3f9ad191acfbc8cc39f36228db"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5e96ed318db1ba8037eb402724bc052240ac9e05"/>
   <!-- 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"/>
@@ -132,17 +132,17 @@
   <project name="platform/hardware/libhardware_legacy" path="hardware/libhardware_legacy" revision="8d075b4d5e9e032b18fbc8b5def63827d1b4a30d"/>
   <project name="platform/libcore" path="libcore" revision="bdec7d684c083760bef7bc4ba2429cceccaaf7d0"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="27bcc086236cedd31c056303e255c6d0ea3d4a50"/>
   <project name="platform/ndk" path="ndk" revision="42e85f81cc6c74af145056ee80b06e520cccb9a7"/>
   <project name="platform_prebuilts_misc" path="prebuilts/misc" remote="b2g" revision="25b96077aeae7bd0e3a5e7c284fb636664337013"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="1d080491f26dfdfd76d5bbc3e6b40c660e8565af"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="61a10cbd19d6b7fc052a8cb92dfa1b37b93754f3"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="9e892a67a01671f312c76b0880dedaa6ba478148"/>
-  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="570531db9e81a871c421b27025ae558022b1015a"/>
+  <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="bb356d6505f914347690c8143dbd03af427dd07e"/>
   <project name="platform/system/extras" path="system/extras" revision="47fa016e2248b80aebd5928402c7409f8e0ca64e"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="34adfb400e031f3dd3353d92413572db5e3a7376"/>
   <project name="platform/system/media" path="system/media" revision="70bfebc66d9c6a4c614a8c7efde90e8e7e1d8641"/>
   <project name="platform/system/netd" path="system/netd" revision="d113f0ceefa9ce29eb3c86e2d23c7417a70b4048"/>
   <project name="platform/system/security" path="system/security" revision="94e1617f6f2bc2286d005e79cffa6bf0721b06b3"/>
   <project name="platform/system/vold" path="system/vold" revision="c065e301e38ea0c241164e2a373e1ecefbeaf2ec"/>
   <project name="platform_frameworks_av" path="frameworks/av" remote="b2g" revision="479a404164986b3e95212eecdae7e67da4fba9ed"/>
   <project name="platform_frameworks_base" path="frameworks/base" remote="b2g" revision="10592803994aa01fa3dc0d0bd36d0d29f006d779"/>
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -32,17 +32,16 @@
 #include "nsISMILAttr.h"
 #include "mozilla/dom/DOMRect.h"
 #include "nsAttrValue.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "mozilla/dom/ElementBinding.h"
 #include "Units.h"
-#include "nsContentListDeclarations.h"
 
 class nsIFrame;
 class nsIDOMMozNamedAttrMap;
 class nsIURI;
 class nsIScrollableFrame;
 class nsAttrValueOrString;
 class nsContentList;
 class nsDOMSettableTokenList;
@@ -57,16 +56,21 @@ class nsDocument;
 namespace mozilla {
 namespace dom {
   struct ScrollIntoViewOptions;
   struct ScrollToOptions;
 } // namespace dom
 } // namespace mozilla
 
 
+already_AddRefed<nsContentList>
+NS_GetContentList(nsINode* aRootNode,
+                  int32_t  aMatchNameSpaceId,
+                  const nsAString& aTagname);
+
 #define ELEMENT_FLAG_BIT(n_) NODE_FLAG_BIT(NODE_TYPE_SPECIFIC_BITS_OFFSET + (n_))
 
 // Element-specific flags
 enum {
   // Set if the element has a pending style change.
   ELEMENT_HAS_PENDING_RESTYLE =                 ELEMENT_FLAG_BIT(0),
 
   // Set if the element is a potential restyle root (that is, has a style
--- a/dom/base/File.cpp
+++ b/dom/base/File.cpp
@@ -424,25 +424,16 @@ File::Create(nsISupports* aParent, const
 {
   RefPtr<File> file = new File(aParent,
     new BlobImplBase(aName, aContentType, aLength, aLastModifiedDate,
                      aDirState));
   return file.forget();
 }
 
 /* static */ already_AddRefed<File>
-File::Create(nsISupports* aParent, const nsAString& aName,
-             const nsAString& aContentType, uint64_t aLength)
-{
-  RefPtr<File> file = new File(aParent,
-    new BlobImplBase(aName, aContentType, aLength));
-  return file.forget();
-}
-
-/* static */ already_AddRefed<File>
 File::CreateMemoryFile(nsISupports* aParent, void* aMemoryBuffer,
                        uint64_t aLength, const nsAString& aName,
                        const nsAString& aContentType,
                        int64_t aLastModifiedDate)
 {
   RefPtr<File> file = new File(aParent,
     new BlobImplMemory(aMemoryBuffer, aLength, aName,
                        aContentType, aLastModifiedDate));
--- a/dom/base/File.h
+++ b/dom/base/File.h
@@ -190,20 +190,16 @@ public:
   static File*
   Create(nsISupports* aParent, BlobImpl* aImpl);
 
   static already_AddRefed<File>
   Create(nsISupports* aParent, const nsAString& aName,
          const nsAString& aContentType, uint64_t aLength,
          int64_t aLastModifiedDate, BlobDirState aDirState);
 
-  static already_AddRefed<File>
-  Create(nsISupports* aParent, const nsAString& aName,
-         const nsAString& aContentType, uint64_t aLength);
-
   // The returned File takes ownership of aMemoryBuffer. aMemoryBuffer will be
   // freed by free so it must be allocated by malloc or something
   // compatible with it.
   static already_AddRefed<File>
   CreateMemoryFile(nsISupports* aParent, void* aMemoryBuffer, uint64_t aLength,
                    const nsAString& aName, const nsAString& aContentType,
                    int64_t aLastModifiedDate);
 
--- a/dom/base/nsContentList.cpp
+++ b/dom/base/nsContentList.cpp
@@ -840,29 +840,34 @@ nsContentList::Match(Element *aElement)
   if (mFunc) {
     return (*mFunc)(aElement, mMatchNameSpaceId, mXMLMatchAtom, mData);
   }
 
   if (!mXMLMatchAtom)
     return false;
 
   NodeInfo *ni = aElement->NodeInfo();
-
-  bool wildcard = mMatchNameSpaceId == kNameSpaceID_Wildcard ||
-                  mMatchNameSpaceId == kNameSpaceID_Unknown;
+ 
+  bool unknown = mMatchNameSpaceId == kNameSpaceID_Unknown;
+  bool wildcard = mMatchNameSpaceId == kNameSpaceID_Wildcard;
   bool toReturn = mMatchAll;
-  if (!wildcard)
+  if (!unknown && !wildcard)
     toReturn &= ni->NamespaceEquals(mMatchNameSpaceId);
 
   if (toReturn)
     return toReturn;
 
   bool matchHTML =
     mIsHTMLDocument && aElement->GetNameSpaceID() == kNameSpaceID_XHTML;
 
+  if (unknown) {
+    return matchHTML ? ni->QualifiedNameEquals(mHTMLMatchAtom) :
+                       ni->QualifiedNameEquals(mXMLMatchAtom);
+  }
+
   if (wildcard) {
     return matchHTML ? ni->Equals(mHTMLMatchAtom) :
                        ni->Equals(mXMLMatchAtom);
   }
   
   return matchHTML ? ni->Equals(mHTMLMatchAtom, mMatchNameSpaceId) :
                      ni->Equals(mXMLMatchAtom, mMatchNameSpaceId);
 }
--- a/dom/base/nsContentList.h
+++ b/dom/base/nsContentList.h
@@ -213,17 +213,17 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
 
   /**
    * @param aRootNode The node under which to limit our search.
    * @param aMatchAtom An atom whose meaning depends on aMatchNameSpaceId.
    *                   The special value "*" always matches whatever aMatchAtom
    *                   is matched against.
    * @param aMatchNameSpaceId If kNameSpaceID_Unknown, then aMatchAtom is the
-   *                          localName to match.
+   *                          tagName to match.
    *                          If kNameSpaceID_Wildcard, then aMatchAtom is the
    *                          localName to match.
    *                          Otherwise we match nodes whose namespace is
    *                          aMatchNameSpaceId and localName matches
    *                          aMatchAtom.
    * @param aDeep If false, then look only at children of the root, nothing
    *              deeper.  If true, then look at the whole subtree rooted at
    *              our root.
@@ -241,18 +241,17 @@ public:
    *              The nsContentList implementation guarantees that everything
    *              passed to the function will be IsElement().
    * @param aDestroyFunc the function that will be called to destroy aData
    * @param aData closure data that will need to be passed back to aFunc
    * @param aDeep If false, then look only at children of the root, nothing
    *              deeper.  If true, then look at the whole subtree rooted at
    *              our root.
    * @param aMatchAtom an atom to be passed back to aFunc
-   * @param aMatchNameSpaceId a namespace id to be passed back to aFunc.  Is
-                              allowed to be kNameSpaceID_Unknown.
+   * @param aMatchNameSpaceId a namespace id to be passed back to aFunc
    * @param aFuncMayDependOnAttr a boolean that indicates whether this list is
    *                             sensitive to attribute changes.
    */  
   nsContentList(nsINode* aRootNode,
                 nsContentListMatchFunc aFunc,
                 nsContentListDestroyFunc aDestroyFunc,
                 void* aData,
                 bool aDeep = true,
--- a/dom/base/nsContentListDeclarations.h
+++ b/dom/base/nsContentListDeclarations.h
@@ -39,19 +39,18 @@ typedef void (*nsContentListDestroyFunc)
  * that case the destructor function should be a no-op.
  */
 typedef void* (*nsFuncStringContentListDataAllocator)(nsINode* aRootNode,
                                                       const nsString* aString);
 
 // If aMatchNameSpaceId is kNameSpaceID_Unknown, this will return a
 // content list which matches ASCIIToLower(aTagname) against HTML
 // elements in HTML documents and aTagname against everything else.
-// The comparison is done to the element's localName.  For any
-// other value of aMatchNameSpaceId, the list will match aTagname
-// against all elements, again comparing to the localName.
+// For any other value of aMatchNameSpaceId, the list will match
+// aTagname against all elements.
 already_AddRefed<nsContentList>
 NS_GetContentList(nsINode* aRootNode,
                   int32_t aMatchNameSpaceId,
                   const nsAString& aTagname);
 
 already_AddRefed<nsContentList>
 NS_GetFuncStringNodeList(nsINode* aRootNode,
                          nsContentListMatchFunc aFunc,
--- a/dom/base/nsFormData.cpp
+++ b/dom/base/nsFormData.cpp
@@ -251,16 +251,40 @@ nsFormData::GetKeyAtIndex(uint32_t aInde
 
 const OwningFileOrUSVString&
 nsFormData::GetValueAtIndex(uint32_t aIndex) const
 {
   MOZ_ASSERT(aIndex < mFormData.Length());
   return mFormData[aIndex].value;
 }
 
+void
+nsFormData::SetNameValuePair(FormDataTuple* aData,
+                             const nsAString& aName,
+                             const nsAString& aValue)
+{
+  MOZ_ASSERT(aData);
+  aData->name = aName;
+  aData->value.SetAsUSVString() = aValue;
+}
+
+void
+nsFormData::SetNameFilePair(FormDataTuple* aData,
+                            const nsAString& aName,
+                            File* aFile)
+{
+  MOZ_ASSERT(aData);
+  aData->name = aName;
+  if (aFile) {
+    aData->value.SetAsFile() = aFile;
+  } else {
+    aData->value.SetAsUSVString() = EmptyString();
+  }
+}
+
 // -------------------------------------------------------------------------
 // nsIDOMFormData
 
 NS_IMETHODIMP
 nsFormData::Append(const nsAString& aName, nsIVariant* aValue)
 {
   uint16_t dataType;
   nsresult rv = aValue->GetDataType(&dataType);
--- a/dom/base/nsFormData.h
+++ b/dom/base/nsFormData.h
@@ -47,33 +47,21 @@ private:
 
   // Returns the FormDataTuple to modify. This may be null, in which case
   // no element with aName was found.
   FormDataTuple*
   RemoveAllOthersAndGetFirstFormDataTuple(const nsAString& aName);
 
   void SetNameValuePair(FormDataTuple* aData,
                         const nsAString& aName,
-                        const nsAString& aValue)
-  {
-    MOZ_ASSERT(aData);
-    aData->name = aName;
-    aData->value.SetAsUSVString() = aValue;
-  }
+                        const nsAString& aValue);
 
   void SetNameFilePair(FormDataTuple* aData,
                        const nsAString& aName,
-                       File* aFile)
-  {
-    MOZ_ASSERT(aData);
-    aData->name = aName;
-    if (aFile) {
-      aData->value.SetAsFile() = aFile;
-    }
-  }
+                       File* aFile);
 
 public:
   explicit nsFormData(nsISupports* aOwner = nullptr);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsFormData,
                                                          nsIDOMFormData)
 
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -770,17 +770,17 @@ skip-if = (os != 'b2g' && os != 'android
 [test_meta_viewport6.html]
 skip-if = (os != 'b2g' && os != 'android')    # meta-viewport tag support is mobile-only
 [test_meta_viewport7.html]
 skip-if = (os != 'b2g' && os != 'android')    # meta-viewport tag support is mobile-only
 [test_mozfiledataurl.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s #TIMED_OUT
 [test_mozMatchesSelector.html]
 [test_mutationobservers.html]
-skip-if = buildapp == 'b2g' || e10s # b2g(bug 901385, showmodaldialog) b2g-debug(bug 901385, showmodaldialog) b2g-desktop(bug 901385, showmodaldialog)
+skip-if = buildapp == 'b2g' # b2g(bug 901385, showmodaldialog) b2g-debug(bug 901385, showmodaldialog) b2g-desktop(bug 901385, showmodaldialog)
 [test_nodelist_holes.html]
 [test_object.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s # b2g(needs plugin support) b2g-debug(needs plugin support) b2g-desktop(needs plugin support)
 [test_plugin_freezing.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #CLICK_TO_PLAY
 [test_processing_instruction_update_stylesheet.xhtml]
 [test_progress_events_for_gzip_data.html]
 [test_range_bounds.html]
@@ -857,8 +857,9 @@ support-files = worker_postMessages.js
 [test_frameLoader_switchProcess.html]
 skip-if = e10s || os != 'linux' || buildapp != 'browser'
 [test_explicit_user_agent.html]
 [test_change_policy.html]
 skip-if = buildapp == 'b2g' #no ssl support
 [test_document.all_iteration.html]
 [test_performance_translate.html]
 [test_bug1198095.html]
+[test_bug1187157.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_bug1187157.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=789315
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 789315</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=789315">Mozilla Bug 789315</a>
+<form id="a"><input name="b" type="file"/></form>
+
+<script type="text/javascript">
+  is(new FormData(document.getElementById('a')).get('b'), "", "This should return an empty string.");
+</script>
+
+</body>
+</html>
--- a/dom/base/test/unit/test_nodelist.js
+++ b/dom/base/test/unit/test_nodelist.js
@@ -27,44 +27,44 @@ function test_getElementsByTagName()
   do_check_true(root.getElementsByTagName("*") instanceof nsIDOMNodeList);
   
   // Check that getElementsByTagName excludes the element it's called on.
   do_check_eq(doc.getElementsByTagName("*").length,
               root.getElementsByTagName("*").length + 1);
   do_check_eq(doc.getElementById("test2").getElementsByTagName("*").length,
               8);
   do_check_eq(doc.getElementById("test2").getElementsByTagName("test").length,
-              7);
+              3);
 
   // Check that the first element of getElementsByTagName on the document is
   // the right thing.
   do_check_eq(doc.getElementsByTagName("*").item(0), root);
 
   // Check that we get the right things in the right order
   var numTests = doc.getElementsByTagName("test").length;
-  do_check_eq(numTests, 14);
+  do_check_eq(numTests, 5);
 
   for (var i = 1; i <= numTests; ++i) {
     do_check_true(doc.getElementById("test" + i) instanceof nsIDOMElement);
     do_check_eq(doc.getElementById("test" + i),
                 doc.getElementsByTagName("test").item(i-1));
   }
 
   // Check that we handle tagnames containing ':' correctly
   do_check_true(doc.getElementsByTagName("foo:test")
                 instanceof nsIDOMNodeList);
-  do_check_eq(doc.getElementsByTagName("foo:test").length, 0);
+  do_check_eq(doc.getElementsByTagName("foo:test").length, 2);
 
   do_check_true(doc.getElementsByTagName("foo2:test")
                 instanceof nsIDOMNodeList);
-  do_check_eq(doc.getElementsByTagName("foo2:test").length, 0);
+  do_check_eq(doc.getElementsByTagName("foo2:test").length, 3);
 
   do_check_true(doc.getElementsByTagName("bar:test")
                 instanceof nsIDOMNodeList);
-  do_check_eq(doc.getElementsByTagName("bar:test").length, 0);
+  do_check_eq(doc.getElementsByTagName("bar:test").length, 4);
 }
 
 function test_getElementsByTagNameNS()
 {
   var doc = ParseFile("nodelist_data_1.xml");
   var root = doc.documentElement;
 
   // Check that getElementsByTagNameNS returns a nodelist.
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -450,17 +450,21 @@ EventListenerManager::EnableDevice(Event
 {
   nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
   if (!window) {
     return;
   }
 
   switch (aEventMessage) {
     case eDeviceOrientation:
+#ifdef MOZ_WIDGET_ANDROID
+      window->EnableDeviceSensor(SENSOR_ROTATION_VECTOR);
+#else
       window->EnableDeviceSensor(SENSOR_ORIENTATION);
+#endif
       break;
     case eDeviceProximity:
     case eUserProximity:
       window->EnableDeviceSensor(SENSOR_PROXIMITY);
       break;
     case eDeviceLight:
       window->EnableDeviceSensor(SENSOR_LIGHT);
       break;
@@ -485,17 +489,21 @@ EventListenerManager::DisableDevice(Even
 {
   nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
   if (!window) {
     return;
   }
 
   switch (aEventMessage) {
     case eDeviceOrientation:
+#ifdef MOZ_WIDGET_ANDROID
+      window->DisableDeviceSensor(SENSOR_ROTATION_VECTOR);
+#else
       window->DisableDeviceSensor(SENSOR_ORIENTATION);
+#endif
       break;
     case eDeviceMotion:
       window->DisableDeviceSensor(SENSOR_ACCELERATION);
       window->DisableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
       window->DisableDeviceSensor(SENSOR_GYROSCOPE);
       break;
     case eDeviceProximity:
     case eUserProximity:
--- a/dom/events/test/test_bug742376.html
+++ b/dom/events/test/test_bug742376.html
@@ -17,17 +17,19 @@ https://bugzilla.mozilla.org/show_bug.cg
 /** Test for Bug 742376 **/
 
 function hasListeners() {
 
   var Cc = SpecialPowers.Cc;
   var Ci = SpecialPowers.Ci;
   var dss = Cc["@mozilla.org/devicesensors;1"].getService(Ci.nsIDeviceSensors);
 
-  return dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_ORIENTATION, window);
+  return dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_ORIENTATION, window) ||
+         dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_ROTATION_VECTOR, window) ||
+         dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_GAME_ROTATION_VECTOR, window);
 }
 
 is(hasListeners(), false, "Must not have listeners before tests start");
 
 function dumbListener(event) {}
 function dumbListener2(event) {}
 
 window.addEventListener("deviceorientation", dumbListener, false);
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -125,16 +125,25 @@ FetchDriver::HttpFetch()
   if (mRequest->Mode() == RequestMode::No_cors &&
       mRequest->UnsafeRequest() &&
       (!mRequest->HasSimpleMethod() ||
        !mRequest->Headers()->HasOnlySimpleHeaders())) {
     MOZ_ASSERT(false, "The API should have caught this");
     return NS_ERROR_DOM_BAD_URI;
   }
 
+  // non-GET requests aren't allowed for blob.
+  if (IsBlobURI(uri)) {
+    nsAutoCString method;
+    mRequest->GetMethod(method);
+    if (!method.EqualsLiteral("GET")) {
+      return NS_ERROR_DOM_NETWORK_ERR;
+    }
+  }
+
   // Step 2 deals with letting ServiceWorkers intercept requests. This is
   // handled by Necko after the channel is opened.
   // FIXME(nsm): Bug 1119026: The channel's skip service worker flag should be
   // set based on the Request's flag.
 
   // Step 3.1 "If the CORS preflight flag is set and one of these conditions is
   // true..." is handled by the CORS proxy.
   //
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -5591,17 +5591,16 @@ HTMLInputElement::SubmitNamesValues(nsFo
     for (uint32_t i = 0; i < files.Length(); ++i) {
       aFormSubmission->AddNameFilePair(name, files[i]);
     }
 
     if (files.IsEmpty()) {
       // If no file was selected, pretend we had an empty file with an
       // empty filename.
       aFormSubmission->AddNameFilePair(name, nullptr);
-
     }
 
     return NS_OK;
   }
 
   if (mType == NS_FORM_INPUT_HIDDEN && name.EqualsLiteral("_charset_")) {
     nsCString charset;
     aFormSubmission->GetCharset(charset);
--- a/dom/html/test/test_formSubmission.html
+++ b/dom/html/test/test_formSubmission.html
@@ -253,27 +253,27 @@ https://bugzilla.mozilla.org/show_bug.cg
           <!-- Put UTF-8 value is the "strange" column. -->
           <input type=email name="n20_8" value="foo@mózillä.órg"></td>
     </tr>
   </table>
   
   <p>
     File input:
     <input type=file name="file_1" class="setfile">
-    <input type=file name="file_2">
+    <input type=file name="file_2" class="setfile empty">
     <input type=file name="" class="setfile">
     <input type=file name="">
     <input type=file class="setfile">
     <input type=file>
   </p>
   <p>
     Multifile input:
     <input multiple type=file name="file_3" class="setfile">
     <input multiple type=file name="file_4" class="setfile multi">
-    <input multiple type=file name="file_5">
+    <input multiple type=file name="file_5" class="setfile empty">
     <input multiple type=file name="" class="setfile">
     <input multiple type=file name="" class="setfile multi">
     <input multiple type=file name="">
     <input multiple type=file class="setfile">
     <input multiple type=file class="setfile multi">
     <input multiple type=file>
   </p>
 
@@ -421,38 +421,39 @@ let opener = SpecialPowers.loadChromeScr
 }
 
 function onFilesOpened(files) {
   let [textFile, imageFile] = files;
   opener.destroy();
 
   let singleFile = textFile;
   let multiFile = [textFile, imageFile];
+  emptyFile = new File([], "", { type: "application/octet-stream" });
 
   var addList = document.getElementsByClassName("setfile");
   let i = 0;
   var input;
   while (input = addList[i++]) {
     if (input.classList.contains("multi")) {
       SpecialPowers.wrap(input).mozSetFileArray(multiFile);
-    }
-    else {
+    } else if (input.classList.contains("empty")) {
+      SpecialPowers.wrap(input).mozSetFileArray([emptyFile]);
+    } else {
       SpecialPowers.wrap(input).mozSetFileArray([singleFile]);
     }
   }
 
   input = document.createElement("input");
   input.type = "file";
   input.multiple = true;
   SpecialPowers.wrap(input).mozSetFileArray(multiFile);
   myFile1 = input.files[0];
   myFile2 = input.files[1];
   is(myFile1.size, 20, "File1 size");
   is(myFile2.size, 2711, "File2 size");
-  emptyFile = { name: "", type: "application/octet-stream" };
 
   // Now, actually run the tests; see below.
   onFilesSet();
 };
 
 var expectedSub = [
   // Default input
   { name: "n1_1", value: "v1_1" },
--- a/dom/imptests/failures/html/dom/nodes/mochitest.ini
+++ b/dom/imptests/failures/html/dom/nodes/mochitest.ini
@@ -1,11 +1,13 @@
 # THIS FILE IS AUTOGENERATED BY parseFailures.py - DO NOT EDIT
 [DEFAULT]
 support-files =
 
 
 [test_Document-createElement-namespace.html.json]
 [test_Document-createElementNS.html.json]
+[test_Document-getElementsByTagName.html.json]
 [test_Node-properties.html.json]
 [test_attributes.html.json]
 [test_case.html.json]
+[test_getElementsByClassName-10.xml.json]
 [test_getElementsByClassName-11.xml.json]
new file mode 100644
--- /dev/null
+++ b/dom/imptests/failures/html/dom/nodes/test_Document-getElementsByTagName.html.json
@@ -0,0 +1,4 @@
+{
+  "Document.getElementsByTagName 1": true,
+  "Document.getElementsByTagName 2": true
+}
--- a/dom/imptests/failures/html/dom/nodes/test_case.html.json
+++ b/dom/imptests/failures/html/dom/nodes/test_case.html.json
@@ -1,5 +1,7 @@
 {
   "getElementsByTagName abc": true,
   "getElementsByTagName Abc": true,
-  "getElementsByTagName ABC": true
+  "getElementsByTagName ABC": true,
+  "getElementsByTagName \u00e4": true,
+  "getElementsByTagName \u00c4": true
 }
new file mode 100644
--- /dev/null
+++ b/dom/imptests/failures/html/dom/nodes/test_getElementsByClassName-10.xml.json
@@ -0,0 +1,3 @@
+{
+  "document.getElementsByClassName(): compound": true
+}
--- a/dom/system/nsDeviceSensors.cpp
+++ b/dom/system/nsDeviceSensors.cpp
@@ -20,16 +20,18 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/Services.h"
 #include "nsIPermissionManager.h"
 #include "mozilla/dom/DeviceLightEvent.h"
 #include "mozilla/dom/DeviceOrientationEvent.h"
 #include "mozilla/dom/DeviceProximityEvent.h"
 #include "mozilla/dom/UserProximityEvent.h"
 
+#include <cmath>
+
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace hal;
 
 #undef near
 
 #define DEFAULT_SENSOR_POLL 100
 
@@ -188,26 +190,82 @@ WindowCannotReceiveSensorEvent (nsPIDOMW
     uint32_t permission = nsIPermissionManager::DENY_ACTION;
     permMgr->TestPermissionFromWindow(aWindow, "background-sensors", &permission);
     return permission != nsIPermissionManager::ALLOW_ACTION;
   }
 
   return false;
 }
 
+// Holds the device orientation in Euler angle degrees (azimuth, pitch, roll).
+struct Orientation
+{
+  static Orientation RadToDeg(const Orientation& aOrient)
+  {
+    const static double kRadToDeg = 180.0 / M_PI;
+    return { aOrient.alpha * kRadToDeg,
+             aOrient.beta * kRadToDeg,
+             aOrient.gamma * kRadToDeg };
+  }
+
+  double alpha;
+  double beta;
+  double gamma;
+};
+
+static Orientation
+RotationVectorToOrientation(double aX, double aY, double aZ, double aW)
+{
+  static const double kFuzzyOne = 1.0 - 1e-6;
+  static const double kCircleRad = 2.0 * M_PI;
+
+  Orientation orient = { 2.0 * std::atan2(aY, aW),
+                         M_PI_2,
+                         0.0 };
+
+  const double sqX = aX * aX;
+  const double sqY = aY * aY;
+  const double sqZ = aZ * aZ;
+  const double sqW = aW * aW;
+  const double unitLength = sqX + sqY + sqZ + sqW;
+  const double xwyz = 2.0 * (aX * aW + aY * aZ) / unitLength;
+
+  if (xwyz < -kFuzzyOne) {
+    orient.alpha *= -1.0;
+    orient.beta *= -1.0;
+  } else if (xwyz <= kFuzzyOne) {
+    const double gammaX = -sqX - sqY + sqZ + sqW;
+    const double gammaY = 2.0 * (aY * aW - aX * aZ);
+    const double alphaX = -sqX + sqY - sqZ + sqW;
+    const double alphaY = 2.0 * (aZ * aW - aX * aY);
+    const double fac = gammaX > 0 ? 1.0 : -1.0;
+
+    orient.alpha = std::fmod(kCircleRad + std::atan2(fac * alphaY, fac * alphaX),
+                             kCircleRad);
+    orient.beta = fac * std::asin(xwyz);
+    orient.gamma = std::atan2(fac * gammaY, fac * gammaX);
+    if (fac < 0.0) {
+      orient.beta = fmod(M_PI + orient.beta, M_PI);
+    }
+  }
+
+  return Orientation::RadToDeg(orient);
+}
+
 void
 nsDeviceSensors::Notify(const mozilla::hal::SensorData& aSensorData)
 {
   uint32_t type = aSensorData.sensor();
 
   const InfallibleTArray<float>& values = aSensorData.values();
   size_t len = values.Length();
   double x = len > 0 ? values[0] : 0.0;
   double y = len > 1 ? values[1] : 0.0;
   double z = len > 2 ? values[2] : 0.0;
+  double w = len > 3 ? values[3] : 0.0;
 
   nsCOMArray<nsIDOMWindow> windowListeners;
   for (uint32_t i = 0; i < mWindowListeners[type]->Length(); i++) {
     windowListeners.AppendObject(mWindowListeners[type]->SafeElementAt(i));
   }
 
   for (uint32_t i = windowListeners.Count(); i > 0 ; ) {
     --i;
@@ -216,25 +274,28 @@ nsDeviceSensors::Notify(const mozilla::h
     if (WindowCannotReceiveSensorEvent(pwindow)) {
         continue;
     }
 
     if (nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(pwindow->GetDoc())) {
       nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryInterface(windowListeners[i]);
       if (type == nsIDeviceSensorData::TYPE_ACCELERATION ||
         type == nsIDeviceSensorData::TYPE_LINEAR_ACCELERATION ||
-        type == nsIDeviceSensorData::TYPE_GYROSCOPE)
+        type == nsIDeviceSensorData::TYPE_GYROSCOPE) {
         FireDOMMotionEvent(domDoc, target, type, x, y, z);
-      else if (type == nsIDeviceSensorData::TYPE_ORIENTATION)
+      } else if (type == nsIDeviceSensorData::TYPE_ORIENTATION) {
         FireDOMOrientationEvent(target, x, y, z);
-      else if (type == nsIDeviceSensorData::TYPE_PROXIMITY)
+      } else if (type == nsIDeviceSensorData::TYPE_ROTATION_VECTOR) {
+        const Orientation orient = RotationVectorToOrientation(x, y, z, w);
+        FireDOMOrientationEvent(target, orient.alpha, orient.beta, orient.gamma);
+      } else if (type == nsIDeviceSensorData::TYPE_PROXIMITY) {
         FireDOMProximityEvent(target, x, y, z);
-      else if (type == nsIDeviceSensorData::TYPE_LIGHT)
+      } else if (type == nsIDeviceSensorData::TYPE_LIGHT) {
         FireDOMLightEvent(target, x);
-
+      }
     }
   }
 }
 
 void
 nsDeviceSensors::FireDOMLightEvent(mozilla::dom::EventTarget* aTarget,
                                    double aValue)
 {
--- a/dom/tests/mochitest/dom-level2-core/test_elementsetattributens03.html
+++ b/dom/tests/mochitest/dom-level2-core/test_elementsetattributens03.html
@@ -95,18 +95,17 @@ function elementsetattributens03() {
       var attrName;
       var attrValue;
       
       var docRef = null;
       if (typeof(this.doc) != 'undefined') {
         docRef = this.doc;
       }
       doc = load(docRef, "doc", "staffNS");
-      // Changed for bug 492933.
-      elementList = doc.getElementsByTagName("employee");
+      elementList = doc.getElementsByTagName("emp:employee");
       element = elementList.item(0);
       assertNotNull("empEmployeeNotNull",element);
 element.setAttributeNS("http://www.w3.org/DOM/Test/1","defaultAttr","default1");
       element.setAttributeNS("http://www.w3.org/DOM/Test/2","defaultAttr","default2");
       attribute = element.getAttributeNodeNS("http://www.w3.org/DOM/Test/1","defaultAttr");
       attrName = attribute.nodeName;
 
       attrValue = attribute.nodeValue;
--- a/dom/tests/mochitest/dom-level2-core/test_getAttributeNS02.html
+++ b/dom/tests/mochitest/dom-level2-core/test_getAttributeNS02.html
@@ -100,23 +100,21 @@ function getAttributeNS02() {
       var attrValue;
       
       var docRef = null;
       if (typeof(this.doc) != 'undefined') {
         docRef = this.doc;
       }
       doc = load(docRef, "doc", "staffNS");
       newAttribute = doc.createAttributeNS(namespaceURI,qualifiedName);
-      // Changed for bug 492933.
-      elementList = doc.getElementsByTagName("address");
+      elementList = doc.getElementsByTagName("emp:address");
       testAddr = elementList.item(0);
       assertNotNull("empAddrNotNull",testAddr);
 districtAttr = testAddr.setAttributeNodeNS(newAttribute);
-      // Changed for bug 492933.
-      elementList = doc.getElementsByTagName("address");
+      elementList = doc.getElementsByTagName("emp:address");
       testAddr = elementList.item(0);
       attrValue = testAddr.getAttributeNS(namespaceURI,localName);
       assertEquals("throw_Equals","",attrValue);
        
 }
 
 </script>
 </head>
--- a/dom/tests/mochitest/dom-level2-core/test_getAttributeNS03.html
+++ b/dom/tests/mochitest/dom-level2-core/test_getAttributeNS03.html
@@ -95,18 +95,17 @@ function getAttributeNS03() {
       var testAddr;
       var attrValue;
       
       var docRef = null;
       if (typeof(this.doc) != 'undefined') {
         docRef = this.doc;
       }
       doc = load(docRef, "doc", "staffNS");
-      // Changed for bug 492933.
-      elementList = doc.getElementsByTagName("address");
+      elementList = doc.getElementsByTagName("emp:address");
       testAddr = elementList.item(0);
       assertNotNull("empAddrNotNull",testAddr);
 testAddr.removeAttributeNS(namespaceURI,localName);
       attrValue = testAddr.getAttributeNS(namespaceURI,localName);
       assertEquals("throw_Equals",null,attrValue);
        
 }
 
--- a/dom/tests/mochitest/dom-level2-core/test_getAttributeNS04.html
+++ b/dom/tests/mochitest/dom-level2-core/test_getAttributeNS04.html
@@ -100,18 +100,17 @@ function getAttributeNS04() {
       var attrValue;
       
       var docRef = null;
       if (typeof(this.doc) != 'undefined') {
         docRef = this.doc;
       }
       doc = load(docRef, "doc", "staffNS");
       newAttribute = doc.createAttributeNS(namespaceURI,qualifiedName);
-      // Changed for bug 492933.
-      elementList = doc.getElementsByTagName("address");
+      elementList = doc.getElementsByTagName("emp:address");
       testAddr = elementList.item(0);
       assertNotNull("empAddrNotNull",testAddr);
 testAddr.setAttributeNS(namespaceURI,qualifiedName,"NewValue");
       attrValue = testAddr.getAttributeNS(namespaceURI,localName);
       assertEquals("throw_Equals","NewValue",attrValue);
        
 }
 
--- a/dom/tests/mochitest/dom-level2-core/test_getAttributeNS05.html
+++ b/dom/tests/mochitest/dom-level2-core/test_getAttributeNS05.html
@@ -94,19 +94,18 @@ function getAttributeNS05() {
       var testAddr;
       var attrValue;
       
       var docRef = null;
       if (typeof(this.doc) != 'undefined') {
         docRef = this.doc;
       }
       doc = load(docRef, "doc", "staffNS");
-      // Changed for bug 492933.
-      elementList = doc.getElementsByTagName("address");
-      testAddr = elementList.item(3);
+      elementList = doc.getElementsByTagName("emp:address");
+      testAddr = elementList.item(0);
       assertNotNull("empAddrNotNull",testAddr);
 attrValue = testAddr.getAttributeNS("http://www.nist.gov","domestic");
       assertEquals("attrValue","Yes",attrValue);
        
 }
 
 </script>
 </head>
--- a/dom/tests/mochitest/dom-level2-core/test_getAttributeNodeNS01.html
+++ b/dom/tests/mochitest/dom-level2-core/test_getAttributeNodeNS01.html
@@ -96,18 +96,17 @@ function getAttributeNodeNS01() {
       var testAddr;
       var attribute;
       
       var docRef = null;
       if (typeof(this.doc) != 'undefined') {
         docRef = this.doc;
       }
       doc = load(docRef, "doc", "staffNS");
-      // Changed for bug 492933.
-      elementList = doc.getElementsByTagName("address");
+      elementList = doc.getElementsByTagName("emp:address");
       testAddr = elementList.item(0);
       assertNotNull("empAddrNotNull",testAddr);
 attribute = testAddr.getAttributeNodeNS(namespaceURI,localName);
       assertNull("throw_Null",attribute);
     
 }
 
 </script>
--- a/dom/tests/mochitest/dom-level2-core/test_getAttributeNodeNS02.html
+++ b/dom/tests/mochitest/dom-level2-core/test_getAttributeNodeNS02.html
@@ -95,19 +95,18 @@ function getAttributeNodeNS02() {
       var attribute;
       var attrName;
       
       var docRef = null;
       if (typeof(this.doc) != 'undefined') {
         docRef = this.doc;
       }
       doc = load(docRef, "doc", "staffNS");
-      // Changed for bug 492933.
-      elementList = doc.getElementsByTagName("address");
-      testAddr = elementList.item(3);
+      elementList = doc.getElementsByTagName("emp:address");
+      testAddr = elementList.item(0);
       assertNotNull("empAddrNotNull",testAddr);
 attribute = testAddr.getAttributeNodeNS("http://www.nist.gov","domestic");
       attrName = attribute.nodeName;
 
       assertEquals("attrName","emp:domestic",attrName);
        
 }
 
--- a/dom/tests/mochitest/dom-level2-core/test_hasAttributeNS03.html
+++ b/dom/tests/mochitest/dom-level2-core/test_hasAttributeNS03.html
@@ -95,18 +95,17 @@ function hasAttributeNS03() {
       var testNode;
       var state;
       
       var docRef = null;
       if (typeof(this.doc) != 'undefined') {
         docRef = this.doc;
       }
       doc = load(docRef, "doc", "staffNS");
-      // Changed for bug 492933.
-      elementList = doc.getElementsByTagName("address");
+      elementList = doc.getElementsByTagName("emp:address");
       testNode = elementList.item(0);
       assertNotNull("empAddrNotNull",testNode);
 state = testNode.hasAttributeNS(namespaceURI,localName);
       assertFalse("throw_False",state);
 
 }
 
 </script>
--- a/dom/tests/mochitest/dom-level2-core/test_hasAttributeNS04.html
+++ b/dom/tests/mochitest/dom-level2-core/test_hasAttributeNS04.html
@@ -97,18 +97,17 @@ function hasAttributeNS04() {
       var testNode;
       var state;
       
       var docRef = null;
       if (typeof(this.doc) != 'undefined') {
         docRef = this.doc;
       }
       doc = load(docRef, "doc", "staffNS");
-      // Changed for bug 492933.
-      elementList = doc.getElementsByTagName("address");
+      elementList = doc.getElementsByTagName("emp:address");
       testNode = elementList.item(0);
       assertNotNull("empAddressNotNull",testNode);
 state = testNode.hasAttributeNS(namespaceURI,localName);
       assertTrue("hasAttribute",state);
 
 }
 
 </script>
--- a/dom/tests/mochitest/dom-level2-core/test_importNode05.html
+++ b/dom/tests/mochitest/dom-level2-core/test_importNode05.html
@@ -114,19 +114,18 @@ function importNode05() {
       }
       doc = load(docRef, "doc", "staffNS");
       
       var aNewDocRef = null;
       if (typeof(this.aNewDoc) != 'undefined') {
         aNewDocRef = this.aNewDoc;
       }
       aNewDoc = load(aNewDocRef, "aNewDoc", "staffNS");
-      // Changed for bug 492933.
-      addresses = aNewDoc.getElementsByTagName("address");
-      element = addresses.item(3);
+      addresses = aNewDoc.getElementsByTagName("emp:address");
+      element = addresses.item(0);
       assertNotNull("empAddressNotNull",element);
 aNode = doc.importNode(element,false);
       hasChild = aNode.hasChildNodes();
       assertFalse("hasChild",hasChild);
 ownerDocument = aNode.ownerDocument;
 
       docType = ownerDocument.doctype;
 
--- a/dom/tests/mochitest/dom-level2-core/test_importNode06.html
+++ b/dom/tests/mochitest/dom-level2-core/test_importNode06.html
@@ -112,19 +112,18 @@ function importNode06() {
       }
       doc = load(docRef, "doc", "staffNS");
       
       var aNewDocRef = null;
       if (typeof(this.aNewDoc) != 'undefined') {
         aNewDocRef = this.aNewDoc;
       }
       aNewDoc = load(aNewDocRef, "aNewDoc", "staffNS");
-      // Changed for bug 492933.
-      addresses = aNewDoc.getElementsByTagName("address");
-      element = addresses.item(3);
+      addresses = aNewDoc.getElementsByTagName("emp:address");
+      element = addresses.item(0);
       assertNotNull("empAddressNotNull",element);
 aNode = doc.importNode(element,true);
       hasChild = aNode.hasChildNodes();
       assertTrue("throw_True",hasChild);
 name = aNode.nodeName;
 
       assertEquals("nodeName","emp:address",name);
        child = aNode.firstChild;
--- a/dom/tests/mochitest/dom-level2-core/test_localName01.html
+++ b/dom/tests/mochitest/dom-level2-core/test_localName01.html
@@ -96,19 +96,18 @@ function localName01() {
       var addrAttr;
       var localName;
       
       var docRef = null;
       if (typeof(this.doc) != 'undefined') {
         docRef = this.doc;
       }
       doc = load(docRef, "doc", "staffNS");
-      // Changed for bug 492933.
-      elementList = doc.getElementsByTagName("address");
-      testAddr = elementList.item(3);
+      elementList = doc.getElementsByTagName("emp:address");
+      testAddr = elementList.item(0);
       assertNotNull("empAddrNotNull",testAddr);
 addrAttr = testAddr.getAttributeNode("emp:domestic");
       localName = addrAttr.localName;
 
       assertEquals("localName","domestic",localName);
        
 }
 
--- a/dom/tests/mochitest/dom-level2-core/test_namespaceURI02.html
+++ b/dom/tests/mochitest/dom-level2-core/test_namespaceURI02.html
@@ -94,19 +94,18 @@ function namespaceURI02() {
       var addrAttr;
       var attrNamespaceURI;
       
       var docRef = null;
       if (typeof(this.doc) != 'undefined') {
         docRef = this.doc;
       }
       doc = load(docRef, "doc", "staffNS");
-      // Changed for bug 492933.
-      elementList = doc.getElementsByTagName("address");
-      testAddr = elementList.item(3);
+      elementList = doc.getElementsByTagName("emp:address");
+      testAddr = elementList.item(0);
       assertNotNull("empAddressNotNull",testAddr);
 addrAttr = testAddr.getAttributeNodeNS("http://www.nist.gov","domestic");
       attrNamespaceURI = addrAttr.namespaceURI;
 
       assertEquals("namespaceURI","http://www.nist.gov",attrNamespaceURI);
        
 }
 
--- a/dom/tests/mochitest/dom-level2-core/test_nodehasattributes03.html
+++ b/dom/tests/mochitest/dom-level2-core/test_nodehasattributes03.html
@@ -89,18 +89,17 @@ function nodehasattributes03() {
       var elementList;
       var hasAttributes;
       
       var docRef = null;
       if (typeof(this.doc) != 'undefined') {
         docRef = this.doc;
       }
       doc = load(docRef, "doc", "staffNS");
-      // Changed for bug 492933.
-      elementList = doc.getElementsByTagName("employee");
+      elementList = doc.getElementsByTagName("emp:employee");
       element = elementList.item(0);
       assertNotNull("empEmployeeNotNull",element);
 hasAttributes = element.hasAttributes();
       assertTrue("hasAttributes",hasAttributes);
 
 }
 
 </script>
--- a/dom/tests/mochitest/dom-level2-core/test_prefix02.html
+++ b/dom/tests/mochitest/dom-level2-core/test_prefix02.html
@@ -95,18 +95,17 @@ function prefix02() {
       var textNode;
       var prefix;
       
       var docRef = null;
       if (typeof(this.doc) != 'undefined') {
         docRef = this.doc;
       }
       doc = load(docRef, "doc", "staffNS");
-      // Changed for bug 492933.
-      elementList = doc.getElementsByTagName("employeeId");
+      elementList = doc.getElementsByTagName("emp:employeeId");
       testEmployee = elementList.item(0);
       assertNotNull("empEmployeeNotNull",testEmployee);
 textNode = testEmployee.firstChild;
 
       prefix = textNode.prefix;
 
       assertNull("textNodePrefix",prefix);
     
--- a/dom/tests/mochitest/dom-level2-core/test_prefix03.html
+++ b/dom/tests/mochitest/dom-level2-core/test_prefix03.html
@@ -92,19 +92,18 @@ function prefix03() {
       var testEmployee;
       var prefix;
       
       var docRef = null;
       if (typeof(this.doc) != 'undefined') {
         docRef = this.doc;
       }
       doc = load(docRef, "doc", "staffNS");
-      // Changed for bug 492933.
-      elementList = doc.getElementsByTagName("employee");
-      testEmployee = elementList.item(3);
+      elementList = doc.getElementsByTagName("emp:employee");
+      testEmployee = elementList.item(0);
       assertNotNull("empEmployeeNotNull",testEmployee);
 prefix = testEmployee.prefix;
 
       assertEquals("prefix","emp",prefix);
        
 }
 
 </script>
--- a/dom/tests/mochitest/dom-level2-core/test_setAttributeNS02.html
+++ b/dom/tests/mochitest/dom-level2-core/test_setAttributeNS02.html
@@ -95,18 +95,17 @@ function setAttributeNS02() {
       var elementList;
       var testAddr;
       
       var docRef = null;
       if (typeof(this.doc) != 'undefined') {
         docRef = this.doc;
       }
       doc = load(docRef, "doc", "staffNS");
-      // Changed for bug 492933.
-      elementList = doc.getElementsByTagName("employee");
+      elementList = doc.getElementsByTagName("emp:employee");
       testAddr = elementList.item(0);
       
 	{
 		success = false;
 		try {
             testAddr.setAttributeNS(namespaceURI,qualifiedName,"newValue");
         }
 		catch(ex) {
--- a/dom/tests/mochitest/dom-level2-core/test_setAttributeNS04.html
+++ b/dom/tests/mochitest/dom-level2-core/test_setAttributeNS04.html
@@ -103,18 +103,17 @@ function setAttributeNS04() {
       var resultLocalName;
       var resultPrefix;
       
       var docRef = null;
       if (typeof(this.doc) != 'undefined') {
         docRef = this.doc;
       }
       doc = load(docRef, "doc", "staffNS");
-      // Changed for bug 492933.
-      elementList = doc.getElementsByTagName("address");
+      elementList = doc.getElementsByTagName("emp:address");
       testAddr = elementList.item(0);
       assertNotNull("empAddrNotNull",testAddr);
 testAddr.setAttributeNS("http://www.nist.gov","newprefix:zone","newValue");
       addrAttr = testAddr.getAttributeNodeNS("http://www.nist.gov","zone");
       resultAttr = testAddr.getAttributeNS("http://www.nist.gov","zone");
       assertEquals("attrValue","newValue",resultAttr);
        resultNamespaceURI = addrAttr.namespaceURI;
 
--- a/dom/tests/mochitest/dom-level2-core/test_setAttributeNS05.html
+++ b/dom/tests/mochitest/dom-level2-core/test_setAttributeNS05.html
@@ -101,18 +101,17 @@ function setAttributeNS05() {
       var addrAttr;
       var resultAttr;
       
       var docRef = null;
       if (typeof(this.doc) != 'undefined') {
         docRef = this.doc;
       }
       doc = load(docRef, "doc", "staffNS");
-      // Changed for bug 492933.
-      elementList = doc.getElementsByTagName("address");
+      elementList = doc.getElementsByTagName("emp:address");
       testAddr = elementList.item(0);
       assertNotNull("empAddrNotNull",testAddr);
 testAddr.setAttributeNS(namespaceURI,qualifiedName,"<newValue>");
       resultAttr = testAddr.getAttributeNS(namespaceURI,localName);
       assertEquals("throw_Equals","<newValue>",resultAttr);
        
 }
 
--- a/dom/tests/mochitest/dom-level2-core/test_setAttributeNS09.html
+++ b/dom/tests/mochitest/dom-level2-core/test_setAttributeNS09.html
@@ -103,18 +103,17 @@ function setAttributeNS09() {
       var resultLocalName;
       var resultPrefix;
       
       var docRef = null;
       if (typeof(this.doc) != 'undefined') {
         docRef = this.doc;
       }
       doc = load(docRef, "doc", "staffNS");
-      // Changed for bug 492933.
-      elementList = doc.getElementsByTagName("address");
+      elementList = doc.getElementsByTagName("emp:address");
       testAddr = elementList.item(0);
       assertNotNull("empAddrNotNull",testAddr);
 testAddr.setAttributeNS(namespaceURI,qualifiedName,"newValue");
       addrAttr = testAddr.getAttributeNodeNS(namespaceURI,localName);
       resultAttr = testAddr.getAttributeNS(namespaceURI,localName);
       assertEquals("attrValue","newValue",resultAttr);
        resultNamespaceURI = addrAttr.namespaceURI;
 
--- a/dom/tests/mochitest/dom-level2-core/test_setAttributeNodeNS01.html
+++ b/dom/tests/mochitest/dom-level2-core/test_setAttributeNodeNS01.html
@@ -106,18 +106,17 @@ function setAttributeNodeNS01() {
       var setAttr1;
       var setAttr2;
       
       var docRef = null;
       if (typeof(this.doc) != 'undefined') {
         docRef = this.doc;
       }
       doc = load(docRef, "doc", "staffNS");
-      // Changed for bug 492933.
-      elementList = doc.getElementsByTagName("address");
+      elementList = doc.getElementsByTagName("emp:address");
       testAddr = elementList.item(0);
       assertNotNull("empAddrNotNull",testAddr);
 newElement = doc.createElement("newElement");
       appendedChild = testAddr.appendChild(newElement);
       newAttr = doc.createAttributeNS(namespaceURI,qualifiedName);
       setAttr1 = newElement.setAttributeNodeNS(newAttr);
       
 	{
--- a/dom/tests/mochitest/dom-level2-core/test_setAttributeNodeNS03.html
+++ b/dom/tests/mochitest/dom-level2-core/test_setAttributeNodeNS03.html
@@ -99,18 +99,17 @@ function setAttributeNodeNS03() {
       var newAttr;
       var newAddrAttr;
       
       var docRef = null;
       if (typeof(this.doc) != 'undefined') {
         docRef = this.doc;
       }
       doc = load(docRef, "doc", "staffNS");
-      // Changed for bug 492933.
-      elementList = doc.getElementsByTagName("address");
+      elementList = doc.getElementsByTagName("emp:address");
       testAddr = elementList.item(0);
       assertNotNull("empAddrNotNull",testAddr);
 newAttr = doc.createAttributeNS(namespaceURI,qualifiedName);
       newAddrAttr = testAddr.setAttributeNodeNS(newAttr);
       assertNull("throw_Null",newAddrAttr);
     
 }
 
--- a/dom/tests/mochitest/fetch/test_fetch_basic.js
+++ b/dom/tests/mochitest/fetch/test_fetch_basic.js
@@ -67,15 +67,38 @@ function testSameOriginBlobURL() {
     ok(res.headers.has("content-length"), "Headers must have Content-Length header");
     is(parseInt(res.headers.get("content-length")), 16, "Content-Length should match Blob's size");
     return res.text().then(function(body) {
       is(body, "english sentence", "Blob fetch body should match");
     });
   });
 }
 
+function testNonGetBlobURL() {
+  var blob = new Blob(["english ", "sentence"], { type: "text/plain" });
+  var url = URL.createObjectURL(blob);
+  return Promise.all(
+    [
+      "HEAD",
+      "POST",
+      "PUT",
+      "DELETE"
+    ].map(method => {
+      var req = new Request(url, { method: method });
+      return fetch(req).then(function(res) {
+        ok(false, "Blob URL with non-GET request should not succeed");
+      }).catch(function(e) {
+        ok(e instanceof TypeError, "Blob URL with non-GET request should get a TypeError");
+      });
+    })
+  ).then(function() {
+    URL.revokeObjectURL(url);
+  });
+}
+
 function runTest() {
   return Promise.resolve()
     .then(testAboutURL)
     .then(testDataURL)
     .then(testSameOriginBlobURL)
+    .then(testNonGetBlobURL)
     // Put more promise based tests here.
 }
--- a/dom/webidl/CSS.webidl
+++ b/dom/webidl/CSS.webidl
@@ -15,11 +15,10 @@ interface CSS {
   static boolean supports(DOMString property, DOMString value);
 
   [Throws]
   static boolean supports(DOMString conditionText);
 };
 
 // http://dev.w3.org/csswg/cssom/#the-css.escape%28%29-method
 partial interface CSS {
-  [Throws]
   static DOMString escape(DOMString ident);
 };
--- a/dom/workers/WorkerDebuggerManager.cpp
+++ b/dom/workers/WorkerDebuggerManager.cpp
@@ -7,54 +7,80 @@
 #include "WorkerDebuggerManager.h"
 
 #include "nsISimpleEnumerator.h"
 
 #include "WorkerPrivate.h"
 
 USING_WORKERS_NAMESPACE
 
-class RegisterDebuggerRunnable final : public nsRunnable
+class RegisterDebuggerMainThreadRunnable final : public nsRunnable
 {
   RefPtr<WorkerDebuggerManager> mManager;
-  RefPtr<WorkerDebugger> mDebugger;
-  bool mHasListeners;
+  WorkerPrivate* mWorkerPrivate;
+  bool mNotifyListeners;
 
 public:
-  RegisterDebuggerRunnable(WorkerDebuggerManager* aManager,
-                           WorkerDebugger* aDebugger,
-                           bool aHasListeners)
-  : mManager(aManager), mDebugger(aDebugger), mHasListeners(aHasListeners)
+  RegisterDebuggerMainThreadRunnable(WorkerDebuggerManager* aManager,
+                                     WorkerPrivate* aWorkerPrivate,
+                                     bool aNotifyListeners)
+  : mManager(aManager),
+    mWorkerPrivate(aWorkerPrivate),
+    mNotifyListeners(aNotifyListeners)
   { }
 
 private:
-  ~RegisterDebuggerRunnable()
+  ~RegisterDebuggerMainThreadRunnable()
   { }
 
   NS_IMETHOD
   Run() override
   {
-    mManager->RegisterDebuggerOnMainThread(mDebugger, mHasListeners);
+    mManager->RegisterDebuggerMainThread(mWorkerPrivate, mNotifyListeners);
+
+    return NS_OK;
+  }
+};
+
+class UnregisterDebuggerMainThreadRunnable final : public nsRunnable
+{
+  RefPtr<WorkerDebuggerManager> mManager;
+  WorkerPrivate* mWorkerPrivate;
+
+public:
+  UnregisterDebuggerMainThreadRunnable(WorkerDebuggerManager* aManager,
+                                       WorkerPrivate* aWorkerPrivate)
+  : mManager(aManager), mWorkerPrivate(aWorkerPrivate)
+  { }
+
+private:
+  ~UnregisterDebuggerMainThreadRunnable()
+  { }
+
+  NS_IMETHOD
+  Run() override
+  {
+    mManager->UnregisterDebuggerMainThread(mWorkerPrivate);
 
     return NS_OK;
   }
 };
 
 BEGIN_WORKERS_NAMESPACE
 
 class WorkerDebuggerEnumerator final : public nsISimpleEnumerator
 {
-  nsTArray<nsCOMPtr<nsISupports>> mDebuggers;
+  nsTArray<RefPtr<WorkerDebugger>> mDebuggers;
   uint32_t mIndex;
 
 public:
-  explicit WorkerDebuggerEnumerator(const nsTArray<WorkerDebugger*>& aDebuggers)
-  : mIndex(0)
+  explicit WorkerDebuggerEnumerator(
+                             const nsTArray<RefPtr<WorkerDebugger>>& aDebuggers)
+  : mDebuggers(aDebuggers), mIndex(0)
   {
-    mDebuggers.AppendElements(aDebuggers);
   }
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSISIMPLEENUMERATOR
 
 private:
   ~WorkerDebuggerEnumerator() {}
 };
@@ -70,18 +96,17 @@ WorkerDebuggerEnumerator::HasMoreElement
 
 NS_IMETHODIMP
 WorkerDebuggerEnumerator::GetNext(nsISupports** aResult)
 {
   if (mIndex == mDebuggers.Length()) {
     return NS_ERROR_FAILURE;
   }
 
-  nsCOMPtr<nsISupports> element = mDebuggers.ElementAt(mIndex++);
-  element.forget(aResult);
+  mDebuggers.ElementAt(mIndex++).forget(aResult);
   return NS_OK;
 };
 
 WorkerDebuggerManager::WorkerDebuggerManager()
 : mMutex("WorkerDebuggerManager::mMutex")
 {
   AssertIsOnMainThread();
 }
@@ -94,18 +119,16 @@ WorkerDebuggerManager::~WorkerDebuggerMa
 NS_IMPL_ISUPPORTS(WorkerDebuggerManager, nsIWorkerDebuggerManager);
 
 NS_IMETHODIMP
 WorkerDebuggerManager::GetWorkerDebuggerEnumerator(
                                                   nsISimpleEnumerator** aResult)
 {
   AssertIsOnMainThread();
 
-  MutexAutoLock lock(mMutex);
-
   RefPtr<WorkerDebuggerEnumerator> enumerator =
     new WorkerDebuggerEnumerator(mDebuggers);
   enumerator.forget(aResult);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WorkerDebuggerManager::AddListener(nsIWorkerDebuggerManagerListener* aListener)
@@ -144,100 +167,131 @@ WorkerDebuggerManager::ClearListeners()
   AssertIsOnMainThread();
 
   MutexAutoLock lock(mMutex);
 
   mListeners.Clear();
 }
 
 void
-WorkerDebuggerManager::RegisterDebugger(WorkerDebugger* aDebugger)
+WorkerDebuggerManager::RegisterDebugger(WorkerPrivate* aWorkerPrivate)
 {
-  // May be called on any thread!
-
-  bool hasListeners = false;
-
-  {
-    MutexAutoLock lock(mMutex);
-
-    hasListeners = !mListeners.IsEmpty();
-  }
+  aWorkerPrivate->AssertIsOnParentThread();
 
   if (NS_IsMainThread()) {
-    RegisterDebuggerOnMainThread(aDebugger, hasListeners);
+    // When the parent thread is the main thread, it will always block until all
+    // register liseners have been called, since it cannot continue until the
+    // call to RegisterDebuggerMainThread returns.
+    //
+    // In this case, it is always safe to notify all listeners on the main
+    // thread, even if there were no listeners at the time this method was
+    // called, so we can always pass true for the value of aNotifyListeners.
+    // This avoids having to lock mMutex to check whether mListeners is empty.
+    RegisterDebuggerMainThread(aWorkerPrivate, true);
   } else {
+    // We guarantee that if any register listeners are called, the worker does
+    // not start running until all register listeners have been called. To
+    // guarantee this, the parent thread should block until all register
+    // listeners have been called.
+    //
+    // However, to avoid overhead when the debugger is not being used, the
+    // parent thread will only block if there were any listeners at the time
+    // this method was called. As a result, we should not notify any listeners
+    // on the main thread if there were no listeners at the time this method was
+    // called, because the parent will not be blocking in that case.
+    bool hasListeners = false;
+    {
+      MutexAutoLock lock(mMutex);
+
+      hasListeners = !mListeners.IsEmpty();
+    }
+
     nsCOMPtr<nsIRunnable> runnable =
-      new RegisterDebuggerRunnable(this, aDebugger, hasListeners);
+      new RegisterDebuggerMainThreadRunnable(this, aWorkerPrivate,
+                                             hasListeners);
     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
       NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL)));
 
     if (hasListeners) {
-      aDebugger->WaitIsEnabled(true);
+      aWorkerPrivate->WaitForIsDebuggerRegistered(true);
     }
   }
 }
 
 void
-WorkerDebuggerManager::UnregisterDebugger(WorkerDebugger* aDebugger)
+WorkerDebuggerManager::UnregisterDebugger(WorkerPrivate* aWorkerPrivate)
 {
-  // May be called on any thread!
+  aWorkerPrivate->AssertIsOnParentThread();
 
   if (NS_IsMainThread()) {
-    UnregisterDebuggerOnMainThread(aDebugger);
+    UnregisterDebuggerMainThread(aWorkerPrivate);
   } else {
     nsCOMPtr<nsIRunnable> runnable =
-      NS_NewRunnableMethodWithArg<RefPtr<WorkerDebugger>>(this,
-        &WorkerDebuggerManager::UnregisterDebuggerOnMainThread, aDebugger);
+      new UnregisterDebuggerMainThreadRunnable(this, aWorkerPrivate);
     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
       NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL)));
 
-    aDebugger->WaitIsEnabled(false);
+    aWorkerPrivate->WaitForIsDebuggerRegistered(false);
   }
 }
 
 void
-WorkerDebuggerManager::RegisterDebuggerOnMainThread(WorkerDebugger* aDebugger,
-                                                    bool aHasListeners)
+WorkerDebuggerManager::RegisterDebuggerMainThread(WorkerPrivate* aWorkerPrivate,
+                                                  bool aNotifyListeners)
 {
   AssertIsOnMainThread();
 
-  MOZ_ASSERT(!mDebuggers.Contains(aDebugger));
-  mDebuggers.AppendElement(aDebugger);
+  RefPtr<WorkerDebugger> debugger = new WorkerDebugger(aWorkerPrivate);
+  mDebuggers.AppendElement(debugger);
+
+  aWorkerPrivate->SetDebugger(debugger);
+
+  if (aNotifyListeners) {
+    nsTArray<nsCOMPtr<nsIWorkerDebuggerManagerListener>> listeners;
+    {
+      MutexAutoLock lock(mMutex);
+
+      listeners = mListeners;
+    }
+
+    for (size_t index = 0; index < listeners.Length(); ++index) {
+      listeners[index]->OnRegister(debugger);
+    }
+  }
+
+  aWorkerPrivate->SetIsDebuggerRegistered(true);
+}
+
+void
+WorkerDebuggerManager::UnregisterDebuggerMainThread(
+                                                  WorkerPrivate* aWorkerPrivate)
+{
+  AssertIsOnMainThread();
+
+  // There is nothing to do here if the debugger was never succesfully
+  // registered. We need to check this on the main thread because the worker
+  // does not wait for the registration to complete if there were no listeners
+  // installed when it started.
+  if (!aWorkerPrivate->IsDebuggerRegistered()) {
+    return;
+  }
+
+  RefPtr<WorkerDebugger> debugger = aWorkerPrivate->Debugger();
+  mDebuggers.RemoveElement(debugger);
+
+  aWorkerPrivate->SetDebugger(nullptr);
 
   nsTArray<nsCOMPtr<nsIWorkerDebuggerManagerListener>> listeners;
   {
     MutexAutoLock lock(mMutex);
 
-    listeners.AppendElements(mListeners);
-  }
-
-  if (aHasListeners) {
-    for (size_t index = 0; index < listeners.Length(); ++index) {
-      listeners[index]->OnRegister(aDebugger);
-    }
-  }
-
-  aDebugger->Enable();
-}
-
-void
-WorkerDebuggerManager::UnregisterDebuggerOnMainThread(WorkerDebugger* aDebugger)
-{
-  AssertIsOnMainThread();
-
-  MOZ_ASSERT(mDebuggers.Contains(aDebugger));
-  mDebuggers.RemoveElement(aDebugger);
-
-  nsTArray<nsCOMPtr<nsIWorkerDebuggerManagerListener>> listeners;
-  {
-    MutexAutoLock lock(mMutex);
-
-    listeners.AppendElements(mListeners);
+    listeners = mListeners;
   }
 
   for (size_t index = 0; index < listeners.Length(); ++index) {
-    listeners[index]->OnUnregister(aDebugger);
+    listeners[index]->OnUnregister(debugger);
   }
 
-  aDebugger->Disable();
+  debugger->Close();
+  aWorkerPrivate->SetIsDebuggerRegistered(false);
 }
 
 END_WORKERS_NAMESPACE
--- a/dom/workers/WorkerDebuggerManager.h
+++ b/dom/workers/WorkerDebuggerManager.h
@@ -15,33 +15,29 @@
 #include "nsTArray.h"
 
 #define WORKERDEBUGGERMANAGER_CID \
   { 0x62ec8731, 0x55ad, 0x4246, \
     { 0xb2, 0xea, 0xf2, 0x6c, 0x1f, 0xe1, 0x9d, 0x2d } }
 #define WORKERDEBUGGERMANAGER_CONTRACTID \
   "@mozilla.org/dom/workers/workerdebuggermanager;1"
 
-class RegisterDebuggerRunnable;
-
 BEGIN_WORKERS_NAMESPACE
 
 class WorkerDebugger;
 
 class WorkerDebuggerManager final : public nsIWorkerDebuggerManager
 {
-  friend class ::RegisterDebuggerRunnable;
-
-  mozilla::Mutex mMutex;
+  Mutex mMutex;
 
   // Protected by mMutex.
   nsTArray<nsCOMPtr<nsIWorkerDebuggerManagerListener>> mListeners;
 
   // Only touched on the main thread.
-  nsTArray<WorkerDebugger*> mDebuggers;
+  nsTArray<RefPtr<WorkerDebugger>> mDebuggers;
 
 public:
   static WorkerDebuggerManager*
   GetOrCreateService()
   {
     nsCOMPtr<nsIWorkerDebuggerManager> manager =
       do_GetService(WORKERDEBUGGERMANAGER_CONTRACTID);
     return static_cast<WorkerDebuggerManager*>(manager.get());
@@ -49,63 +45,63 @@ public:
 
   WorkerDebuggerManager();
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIWORKERDEBUGGERMANAGER
 
   void ClearListeners();
 
-  void RegisterDebugger(WorkerDebugger* aDebugger);
+  void RegisterDebugger(WorkerPrivate* aWorkerPrivate);
+
+  void UnregisterDebugger(WorkerPrivate* aWorkerPrivate);
 
-  void UnregisterDebugger(WorkerDebugger* aDebugger);
+  void RegisterDebuggerMainThread(WorkerPrivate* aWorkerPrivate,
+                                  bool aNotifyListeners);
+
+  void UnregisterDebuggerMainThread(WorkerPrivate* aWorkerPrivate);
 
 private:
   virtual ~WorkerDebuggerManager();
-
-  void RegisterDebuggerOnMainThread(WorkerDebugger* aDebugger,
-                                    bool aHasListeners);
-
-  void UnregisterDebuggerOnMainThread(WorkerDebugger* aDebugger);
 };
 
 inline nsresult
 ClearWorkerDebuggerManagerListeners()
 {
   RefPtr<WorkerDebuggerManager> manager =
     WorkerDebuggerManager::GetOrCreateService();
   if (!manager) {
     return NS_ERROR_FAILURE;
   }
 
   manager->ClearListeners();
   return NS_OK;
 }
 
 inline nsresult
-RegisterWorkerDebugger(WorkerDebugger* aDebugger)
+RegisterWorkerDebugger(WorkerPrivate* aWorkerPrivate)
 {
   RefPtr<WorkerDebuggerManager> manager =
     WorkerDebuggerManager::GetOrCreateService();
   if (!manager) {
     return NS_ERROR_FAILURE;
   }
 
-  manager->RegisterDebugger(aDebugger);
+  manager->RegisterDebugger(aWorkerPrivate);
   return NS_OK;
 }
 
 inline nsresult
-UnregisterWorkerDebugger(WorkerDebugger* aDebugger)
+UnregisterWorkerDebugger(WorkerPrivate* aWorkerPrivate)
 {
   RefPtr<WorkerDebuggerManager> manager =
     WorkerDebuggerManager::GetOrCreateService();
   if (!manager) {
     return NS_ERROR_FAILURE;
   }
 
-  manager->UnregisterDebugger(aDebugger);
+  manager->UnregisterDebugger(aWorkerPrivate);
   return NS_OK;
 }
 
 END_WORKERS_NAMESPACE
 
 #endif // mozilla_dom_workers_workerdebuggermanager_h
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -2309,42 +2309,33 @@ WorkerPrivateParent<Derived>::DispatchPr
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::EnableDebugger()
 {
   AssertIsOnParentThread();
 
   WorkerPrivate* self = ParentAsWorkerPrivate();
 
-  MOZ_ASSERT(!self->mDebugger);
-  self->mDebugger = new WorkerDebugger(self);
-
-  if (NS_FAILED(RegisterWorkerDebugger(self->mDebugger))) {
+  if (NS_FAILED(RegisterWorkerDebugger(self))) {
     NS_WARNING("Failed to register worker debugger!");
-    self->mDebugger = nullptr;
+    return;
   }
 }
 
 template <class Derived>
 void
 WorkerPrivateParent<Derived>::DisableDebugger()
 {
   AssertIsOnParentThread();
 
   WorkerPrivate* self = ParentAsWorkerPrivate();
 
-  if (!self->mDebugger) {
-    return;
-  }
-
-  if (NS_FAILED(UnregisterWorkerDebugger(self->mDebugger))) {
+  if (NS_FAILED(UnregisterWorkerDebugger(self))) {
     NS_WARNING("Failed to unregister worker debugger!");
   }
-
-  self->mDebugger = nullptr;
 }
 
 template <class Derived>
 nsresult
 WorkerPrivateParent<Derived>::DispatchControlRunnable(
   already_AddRefed<WorkerControlRunnable>&& aWorkerControlRunnable)
 {
   // May be called on any thread!
@@ -3545,65 +3536,83 @@ WorkerPrivateParent<Derived>::AssertInne
 
   nsPIDOMWindow* outer = mLoadInfo.mWindow->GetOuterWindow();
   NS_ASSERTION(outer && outer->GetCurrentInnerWindow() == mLoadInfo.mWindow,
                "Inner window no longer correct!");
 }
 
 #endif
 
-class ReportDebuggerErrorRunnable final : public nsIRunnable
-{
-  RefPtr<WorkerDebugger> mDebugger;
+class PostDebuggerMessageRunnable final : public nsRunnable
+{
+  WorkerDebugger *mDebugger;
+  nsString mMessage;
+
+public:
+  PostDebuggerMessageRunnable(WorkerDebugger* aDebugger,
+                              const nsAString& aMessage)
+  : mDebugger(aDebugger),
+    mMessage(aMessage)
+  {
+  }
+
+private:
+  ~PostDebuggerMessageRunnable()
+  { }
+
+  NS_IMETHOD
+  Run() override
+  {
+    mDebugger->PostMessageToDebuggerOnMainThread(mMessage);
+
+    return NS_OK;
+  }
+};
+
+class ReportDebuggerErrorRunnable final : public nsRunnable
+{
+  WorkerDebugger *mDebugger;
   nsString mFilename;
   uint32_t mLineno;
   nsString mMessage;
 
 public:
   ReportDebuggerErrorRunnable(WorkerDebugger* aDebugger,
-                                const nsAString& aFilename, uint32_t aLineno,
-                                const nsAString& aMessage)
+                              const nsAString& aFilename, uint32_t aLineno,
+                              const nsAString& aMessage)
   : mDebugger(aDebugger),
     mFilename(aFilename),
     mLineno(aLineno),
     mMessage(aMessage)
   {
   }
 
-  NS_DECL_THREADSAFE_ISUPPORTS
-
 private:
   ~ReportDebuggerErrorRunnable()
   { }
 
   NS_IMETHOD
   Run() override
   {
     mDebugger->ReportErrorToDebuggerOnMainThread(mFilename, mLineno, mMessage);
 
     return NS_OK;
   }
 };
 
-NS_IMPL_ISUPPORTS(ReportDebuggerErrorRunnable, nsIRunnable)
-
 WorkerDebugger::WorkerDebugger(WorkerPrivate* aWorkerPrivate)
-: mMutex("WorkerDebugger::mMutex"),
-  mCondVar(mMutex, "WorkerDebugger::mCondVar"),
-  mWorkerPrivate(aWorkerPrivate),
-  mIsEnabled(false),
+: mWorkerPrivate(aWorkerPrivate),
   mIsInitialized(false)
 {
-  mWorkerPrivate->AssertIsOnParentThread();
+  AssertIsOnMainThread();
 }
 
 WorkerDebugger::~WorkerDebugger()
 {
   MOZ_ASSERT(!mWorkerPrivate);
-  MOZ_ASSERT(!mIsEnabled);
 
   if (!NS_IsMainThread()) {
     nsCOMPtr<nsIThread> mainThread;
     if (NS_FAILED(NS_GetMainThread(getter_AddRefs(mainThread)))) {
       NS_WARNING("Failed to proxy release of listeners, leaking instead!");
     }
 
     for (size_t index = 0; index < mListeners.Length(); ++index) {
@@ -3618,59 +3627,51 @@ WorkerDebugger::~WorkerDebugger()
 
 NS_IMPL_ISUPPORTS(WorkerDebugger, nsIWorkerDebugger)
 
 NS_IMETHODIMP
 WorkerDebugger::GetIsClosed(bool* aResult)
 {
   AssertIsOnMainThread();
 
-  MutexAutoLock lock(mMutex);
-
   *aResult = !mWorkerPrivate;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WorkerDebugger::GetIsChrome(bool* aResult)
 {
   AssertIsOnMainThread();
 
-  MutexAutoLock lock(mMutex);
-
   if (!mWorkerPrivate) {
     return NS_ERROR_UNEXPECTED;
   }
 
   *aResult = mWorkerPrivate->IsChromeWorker();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WorkerDebugger::GetIsInitialized(bool* aResult)
 {
   AssertIsOnMainThread();
 
-  MutexAutoLock lock(mMutex);
-
   if (!mWorkerPrivate) {
     return NS_ERROR_UNEXPECTED;
   }
 
   *aResult = mIsInitialized;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WorkerDebugger::GetParent(nsIWorkerDebugger** aResult)
 {
   AssertIsOnMainThread();
 
-  MutexAutoLock lock(mMutex);
-
   if (!mWorkerPrivate) {
     return NS_ERROR_UNEXPECTED;
   }
 
   WorkerPrivate* parent = mWorkerPrivate->GetParent();
   if (!parent) {
     *aResult = nullptr;
     return NS_OK;
@@ -3683,48 +3684,42 @@ WorkerDebugger::GetParent(nsIWorkerDebug
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WorkerDebugger::GetType(uint32_t* aResult)
 {
   AssertIsOnMainThread();
 
-  MutexAutoLock lock(mMutex);
-
   if (!mWorkerPrivate) {
     return NS_ERROR_UNEXPECTED;
   }
 
   *aResult = mWorkerPrivate->Type();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WorkerDebugger::GetUrl(nsAString& aResult)
 {
   AssertIsOnMainThread();
 
-  MutexAutoLock lock(mMutex);
-
   if (!mWorkerPrivate) {
     return NS_ERROR_UNEXPECTED;
   }
 
   aResult = mWorkerPrivate->ScriptURL();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WorkerDebugger::GetWindow(nsIDOMWindow** aResult)
 {
   AssertIsOnMainThread();
 
-  MutexAutoLock lock(mMutex);
-
   if (!mWorkerPrivate) {
     return NS_ERROR_UNEXPECTED;
   }
 
   if (mWorkerPrivate->GetParent() || !mWorkerPrivate->IsDedicatedWorker()) {
     *aResult = nullptr;
     return NS_OK;
   }
@@ -3764,18 +3759,16 @@ WorkerDebugger::GetServiceWorkerID(uint3
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WorkerDebugger::Initialize(const nsAString& aURL, JSContext* aCx)
 {
   AssertIsOnMainThread();
 
-  MutexAutoLock lock(mMutex);
-
   if (!mWorkerPrivate) {
     return NS_ERROR_UNEXPECTED;
   }
 
   if (!mIsInitialized) {
     RefPtr<CompileDebuggerScriptRunnable> runnable =
       new CompileDebuggerScriptRunnable(mWorkerPrivate, aURL);
     if (!runnable->Dispatch(aCx)) {
@@ -3788,18 +3781,16 @@ WorkerDebugger::Initialize(const nsAStri
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WorkerDebugger::PostMessageMoz(const nsAString& aMessage, JSContext* aCx)
 {
   AssertIsOnMainThread();
 
-  MutexAutoLock lock(mMutex);
-
   if (!mWorkerPrivate || !mIsInitialized) {
     return NS_ERROR_UNEXPECTED;
   }
 
   RefPtr<DebuggerMessageEventRunnable> runnable =
     new DebuggerMessageEventRunnable(mWorkerPrivate, aMessage);
   if (!runnable->Dispatch(aCx)) {
     return NS_ERROR_FAILURE;
@@ -3830,142 +3821,90 @@ WorkerDebugger::RemoveListener(nsIWorker
     return NS_ERROR_INVALID_ARG;
   }
 
   mListeners.RemoveElement(aListener);
   return NS_OK;
 }
 
 void
-WorkerDebugger::WaitIsEnabled(bool aIsEnabled)
-{
-  MutexAutoLock lock(mMutex);
-
-  while (mIsEnabled != aIsEnabled) {
-    mCondVar.Wait();
-  }
-}
-
-void
-WorkerDebugger::NotifyIsEnabled(bool aIsEnabled)
-{
-  mMutex.AssertCurrentThreadOwns();
-
-  MOZ_ASSERT(mIsEnabled != aIsEnabled);
-  mIsEnabled = aIsEnabled;
-  mCondVar.Notify();
-}
-
-void
-WorkerDebugger::Enable()
-{
-  AssertIsOnMainThread();
-
-  MutexAutoLock lock(mMutex);
-
-  MOZ_ASSERT(mWorkerPrivate);
-
-  NotifyIsEnabled(true);
-}
-
-void
-WorkerDebugger::Disable()
-{
-  AssertIsOnMainThread();
-
-  MutexAutoLock lock(mMutex);
-
+WorkerDebugger::Close()
+{
   MOZ_ASSERT(mWorkerPrivate);
   mWorkerPrivate = nullptr;
 
-  {
-    MutexAutoUnlock unlock(mMutex);
-
-    nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> listeners(mListeners);
-    for (size_t index = 0; index < listeners.Length(); ++index) {
+  nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> listeners(mListeners);
+  for (size_t index = 0; index < listeners.Length(); ++index) {
       listeners[index]->OnClose();
-    }
-  }
-
-  NotifyIsEnabled(false);
+  }
 }
 
 void
 WorkerDebugger::PostMessageToDebugger(const nsAString& aMessage)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
-  nsCOMPtr<nsIRunnable> runnable =
-    NS_NewRunnableMethodWithArg<nsString>(this,
-      &WorkerDebugger::PostMessageToDebuggerOnMainThread, nsString(aMessage));
-  NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL);
+  RefPtr<PostDebuggerMessageRunnable> runnable =
+    new PostDebuggerMessageRunnable(this, aMessage);
+  if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
+    NS_WARNING("Failed to post message to debugger on main thread!");
+  }
 }
 
 void
 WorkerDebugger::PostMessageToDebuggerOnMainThread(const nsAString& aMessage)
 {
   AssertIsOnMainThread();
 
-  nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> listeners;
-
-  {
-    MutexAutoLock lock(mMutex);
-
-    listeners.AppendElements(mListeners);
-  }
-
+  nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> listeners(mListeners);
   for (size_t index = 0; index < listeners.Length(); ++index) {
     listeners[index]->OnMessage(aMessage);
   }
 }
 
 void
 WorkerDebugger::ReportErrorToDebugger(const nsAString& aFilename,
                                       uint32_t aLineno,
                                       const nsAString& aMessage)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
-  nsCOMPtr<nsIRunnable> runnable =
+  RefPtr<ReportDebuggerErrorRunnable> runnable =
     new ReportDebuggerErrorRunnable(this, aFilename, aLineno, aMessage);
   if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
     NS_WARNING("Failed to report error to debugger on main thread!");
   }
 }
 
 void
 WorkerDebugger::ReportErrorToDebuggerOnMainThread(const nsAString& aFilename,
                                                   uint32_t aLineno,
                                                   const nsAString& aMessage)
 {
   AssertIsOnMainThread();
 
-  nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> listeners;
-  {
-    MutexAutoLock lock(mMutex);
-
-    listeners.AppendElements(mListeners);
-  }
-
+  nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> listeners(mListeners);
   for (size_t index = 0; index < listeners.Length(); ++index) {
     listeners[index]->OnError(aFilename, aLineno, aMessage);
   }
 
   LogErrorToConsole(aMessage, aFilename, nsString(), aLineno, 0, 0, 0);
 }
 
 WorkerPrivate::WorkerPrivate(JSContext* aCx,
                              WorkerPrivate* aParent,
                              const nsAString& aScriptURL,
                              bool aIsChromeWorker, WorkerType aWorkerType,
                              const nsACString& aWorkerName,
                              WorkerLoadInfo& aLoadInfo)
   : WorkerPrivateParent<WorkerPrivate>(aCx, aParent, aScriptURL,
                                        aIsChromeWorker, aWorkerType,
                                        aWorkerName, aLoadInfo)
+  , mDebuggerRegistered(false)
+  , mDebugger(nullptr)
   , mJSContext(nullptr)
   , mPRThread(nullptr)
   , mDebuggerEventLoopLevel(0)
   , mErrorHandlerRecursionCount(0)
   , mNextTimeoutId(1)
   , mStatus(Pending)
   , mFrozen(false)
   , mTimerRunning(false)
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -57,16 +57,17 @@ class StructuredCloneHolder;
 namespace ipc {
 class PrincipalInfo;
 } // namespace ipc
 } // namespace mozilla
 
 struct PRThread;
 
 class ReportDebuggerErrorRunnable;
+class PostDebuggerMessageRunnable;
 
 BEGIN_WORKERS_NAMESPACE
 
 class AutoSyncLoopHolder;
 class SharedWorker;
 class ServiceWorkerClientInfo;
 class WorkerControlRunnable;
 class WorkerDebugger;
@@ -798,61 +799,46 @@ public:
   void
   AssertInnerWindowIsCorrect() const
   { }
 #endif
 };
 
 class WorkerDebugger : public nsIWorkerDebugger {
   friend class ::ReportDebuggerErrorRunnable;
-
-  mozilla::Mutex mMutex;
-  mozilla::CondVar mCondVar;
+  friend class ::PostDebuggerMessageRunnable;
 
-  // Protected by mMutex
   WorkerPrivate* mWorkerPrivate;
-  bool mIsEnabled;
-
-  // Only touched on the main thread.
   bool mIsInitialized;
   nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> mListeners;
 
 public:
   explicit WorkerDebugger(WorkerPrivate* aWorkerPrivate);
 
-  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_ISUPPORTS
   NS_DECL_NSIWORKERDEBUGGER
 
   void
   AssertIsOnParentThread();
 
   void
-  WaitIsEnabled(bool aIsEnabled);
-
-  void
-  Enable();
-
-  void
-  Disable();
+  Close();
 
   void
   PostMessageToDebugger(const nsAString& aMessage);
 
   void
   ReportErrorToDebugger(const nsAString& aFilename, uint32_t aLineno,
                         const nsAString& aMessage);
 
 private:
   virtual
   ~WorkerDebugger();
 
   void
-  NotifyIsEnabled(bool aIsEnabled);
-
-  void
   PostMessageToDebuggerOnMainThread(const nsAString& aMessage);
 
   void
   ReportErrorToDebuggerOnMainThread(const nsAString& aFilename,
                                     uint32_t aLineno,
                                     const nsAString& aMessage);
 };
 
@@ -871,17 +857,18 @@ class WorkerPrivate : public WorkerPriva
 
   enum GCTimerMode
   {
     PeriodicTimer = 0,
     IdleTimer,
     NoTimer
   };
 
-  RefPtr<WorkerDebugger> mDebugger;
+  bool mDebuggerRegistered;
+  WorkerDebugger* mDebugger;
 
   Queue<WorkerControlRunnable*, 4> mControlQueue;
   Queue<WorkerRunnable*, 4> mDebuggerQueue;
 
   // Touched on multiple threads, protected with mMutex.
   JSContext* mJSContext;
   RefPtr<WorkerCrossThreadDispatcher> mCrossThreadDispatcher;
   nsTArray<nsCOMPtr<nsIRunnable>> mUndispatchedRunnablesForSyncLoop;
@@ -976,25 +963,71 @@ public:
   GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, WorkerPrivate* aParent,
               const nsAString& aScriptURL, bool aIsChromeWorker,
               LoadGroupBehavior aLoadGroupBehavior, WorkerType aWorkerType,
               WorkerLoadInfo* aLoadInfo);
 
   static void
   OverrideLoadInfoLoadGroup(WorkerLoadInfo& aLoadInfo);
 
+  bool
+  IsDebuggerRegistered()
+  {
+    AssertIsOnMainThread();
+
+    // No need to lock here since this is only ever modified by the same thread.
+    return mDebuggerRegistered;
+  }
+
+  void
+  SetIsDebuggerRegistered(bool aDebuggerRegistered)
+  {
+    AssertIsOnMainThread();
+
+    MutexAutoLock lock(mMutex);
+
+    MOZ_ASSERT(mDebuggerRegistered != aDebuggerRegistered);
+    mDebuggerRegistered = aDebuggerRegistered;
+
+    mCondVar.Notify();
+  }
+
+  void
+  WaitForIsDebuggerRegistered(bool aDebuggerRegistered)
+  {
+    AssertIsOnParentThread();
+
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    MutexAutoLock lock(mMutex);
+
+    while (mDebuggerRegistered != aDebuggerRegistered) {
+      mCondVar.Wait();
+    }
+  }
+
   WorkerDebugger*
   Debugger() const
   {
     AssertIsOnMainThread();
+
     MOZ_ASSERT(mDebugger);
     return mDebugger;
   }
 
   void
+  SetDebugger(WorkerDebugger* aDebugger)
+  {
+    AssertIsOnMainThread();
+
+    MOZ_ASSERT(mDebugger != aDebugger);
+    mDebugger = aDebugger;
+  }
+
+  void
   DoRunLoop(JSContext* aCx);
 
   bool
   InterruptCallback(JSContext* aCx);
 
   nsresult
   IsOnCurrentThread(bool* aIsOnCurrentThread);
 
--- a/gfx/layers/IPDLActor.h
+++ b/gfx/layers/IPDLActor.h
@@ -2,16 +2,17 @@
  * 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_LAYERS_IPDLACTOR_H
 #define MOZILLA_LAYERS_IPDLACTOR_H
 
 #include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/layers/CompositableForwarder.h"
 #include "mozilla/unused.h"
 
 namespace mozilla {
 namespace layers {
 
 /// A base class to facilitate the deallocation of IPDL actors.
 ///
 /// This class implements a simple deallocation protocol that avoids races
@@ -43,43 +44,54 @@ public:
 
   /// Check the return of CanSend before sending any message!
   bool CanSend() const { return !mDestroyed; }
 
   /// The normal way to destroy the actor.
   ///
   /// This will asynchronously send a Destroy message to the parent actor, whom
   /// will send the delete message.
-  void Destroy()
+  void Destroy(CompositableForwarder* aFwd = nullptr)
   {
     MOZ_ASSERT(!mDestroyed);
     if (!mDestroyed) {
       mDestroyed = true;
       DestroyManagees();
-      this->SendDestroy();
+      if (!aFwd || !aFwd->DestroyInTransaction(this, false)) {
+        this->SendDestroy();
+      }
     }
   }
 
   /// The ugly and slow way to destroy the actor.
   ///
   /// This will block until the Parent actor has handled the Destroy message,
   /// and then start the asynchronous handshake (and destruction will already
   /// be done on the parent side, when the async part happens).
-  void DestroySynchronously()
+  void DestroySynchronously(CompositableForwarder* aFwd = nullptr)
   {
     MOZ_PERFORMANCE_WARNING("gfx", "IPDL actor requires synchronous deallocation");
     MOZ_ASSERT(!mDestroyed);
     if (!mDestroyed) {
       DestroyManagees();
       mDestroyed = true;
-      this->SendDestroySync();
-      this->SendDestroy();
+      if (!aFwd || !aFwd->DestroyInTransaction(this, true)) {
+        this->SendDestroySync();
+        this->SendDestroy();
+      }
     }
   }
 
+  /// If the transaction that was supposed to destroy the texture fails for
+  /// whatever reason, fallback to destroying the actor synchronously.
+  static bool DestroyFallback(Protocol* aActor)
+  {
+    return aActor->SendDestroySync();
+  }
+
   /// Override this if the protocol manages other protocols, and destroy the
   /// managees from there
   virtual void DestroyManagees() {}
 
 private:
   bool mDestroyed;
 };
 
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -640,18 +640,16 @@ ClientLayerManager::ForwardTransaction(b
 
   if (!sent) {
     // Clear the transaction id so that it doesn't get returned
     // unless we forwarded to somewhere that doesn't actually
     // have a compositor.
     mTransactionIdAllocator->RevokeTransactionId(mLatestTransactionId);
   }
 
-  mForwarder->RemoveTexturesIfNecessary();
-  mForwarder->RemoveCompositablesIfNecessary();
   mForwarder->SendPendingAsyncMessges();
   mPhase = PHASE_NONE;
 
   // this may result in Layers being deleted, which results in
   // PLayer::Send__delete__() and DeallocShmem()
   mKeepAlive.Clear();
 
   TabChild* window = mWidget->GetOwningTabChild();
--- a/gfx/layers/client/ClientTiledPaintedLayer.cpp
+++ b/gfx/layers/client/ClientTiledPaintedLayer.cpp
@@ -407,17 +407,16 @@ void
 ClientTiledPaintedLayer::RenderLayer()
 {
   LayerManager::DrawPaintedLayerCallback callback =
     ClientManager()->GetPaintedLayerCallback();
   void *data = ClientManager()->GetPaintedLayerCallbackData();
 
   IntSize layerSize = mVisibleRegion.ToUnknownRegion().GetBounds().Size();
   if (mContentClient && !mContentClient->SupportsLayerSize(layerSize, ClientManager())) {
-    ClientManager()->AsShadowForwarder()->HoldUntilTransaction(mContentClient);
     mContentClient = nullptr;
     mValidRegion.SetEmpty();
   }
 
   if (!mContentClient) {
     if (mCreationHint == LayerManager::NONE &&
         SingleTiledContentClient::ClientSupportsLayerSize(layerSize, ClientManager()) &&
         gfxPrefs::LayersSingleTileEnabled()) {
--- a/gfx/layers/client/CompositableClient.cpp
+++ b/gfx/layers/client/CompositableClient.cpp
@@ -175,25 +175,32 @@ CompositableClient::IsConnected() const
 }
 
 void
 CompositableClient::Destroy()
 {
   if (!IsConnected()) {
     return;
   }
-  // Send pending AsyncMessages before deleting CompositableChild.
-  // They might have dependency to the mCompositableChild.
+
+  // Send pending AsyncMessages before deleting CompositableChild since the former
+  // might have references to the latter.
   mForwarder->SendPendingAsyncMessges();
-  // Destroy CompositableChild.
+
   mCompositableChild->mCompositableClient = nullptr;
-  mCompositableChild->Destroy();
+  mCompositableChild->Destroy(mForwarder);
   mCompositableChild = nullptr;
 }
 
+bool
+CompositableClient::DestroyFallback(PCompositableChild* aActor)
+{
+  return aActor->SendDestroySync();
+}
+
 uint64_t
 CompositableClient::GetAsyncID() const
 {
   if (mCompositableChild) {
     return mCompositableChild->mAsyncID;
   }
   return 0; // zero is always an invalid async ID
 }
--- a/gfx/layers/client/CompositableClient.h
+++ b/gfx/layers/client/CompositableClient.h
@@ -150,16 +150,18 @@ public:
 
   /**
    * Establishes the connection with compositor side through IPDL
    */
   virtual bool Connect(ImageContainer* aImageContainer = nullptr);
 
   void Destroy();
 
+  static bool DestroyFallback(PCompositableChild* aActor);
+
   bool IsConnected() const;
 
   PCompositableChild* GetIPDLActor() const;
 
   // should only be called by a CompositableForwarder
   virtual void SetIPDLActor(CompositableChild* aChild);
 
   CompositableForwarder* GetForwarder() const
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -295,23 +295,23 @@ DeallocateTextureClient(TextureDeallocPa
     // race causing this function to be called concurrently which is bad!
     gfxCriticalError() << "Racy texture deallocation";
     return;
   }
 
   if (params.syncDeallocation) {
     MOZ_PERFORMANCE_WARNING("gfx",
       "TextureClient/Host pair requires synchronous deallocation");
-    actor->DestroySynchronously();
+    actor->DestroySynchronously(actor->GetForwarder());
     DestroyTextureData(params.data, params.allocator, params.clientDeallocation,
                        actor->mMainThreadOnly);
   } else {
     actor->mTextureData = params.data;
     actor->mOwnsTextureData = params.clientDeallocation;
-    actor->Destroy();
+    actor->Destroy(actor->GetForwarder());
     // DestroyTextureData will be called by TextureChild::ActorDestroy
   }
 }
 
 void TextureClient::Destroy(bool aForceSync)
 {
   MOZ_ASSERT(!IsLocked());
 
@@ -342,16 +342,24 @@ void TextureClient::Destroy(bool aForceS
     // client side, but having asynchronous deallocate in some of the cases will
     // be a worthwhile optimization.
     params.syncDeallocation = !!(mFlags & TextureFlags::DEALLOCATE_CLIENT) || aForceSync;
     DeallocateTextureClient(params);
   }
 }
 
 bool
+TextureClient::DestroyFallback(PTextureChild* aActor)
+{
+  // should not end up here so crash debug builds.
+  MOZ_ASSERT(false);
+  return aActor->SendDestroySync();
+}
+
+bool
 TextureClient::Lock(OpenMode aMode)
 {
   MOZ_ASSERT(IsValid());
   MOZ_ASSERT(!mIsLocked);
   if (mIsLocked) {
     return mOpenMode == aMode;
   }
 
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -416,16 +416,18 @@ public:
    *
    * TextureChild is an implementation detail of TextureClient that is not
    * exposed to the rest of the code base. CreateIPDLActor and DestroyIPDLActor
    * are for use with the managing IPDL protocols only (so that they can
    * implement AllocPextureChild and DeallocPTextureChild).
    */
   static PTextureChild* CreateIPDLActor();
   static bool DestroyIPDLActor(PTextureChild* actor);
+  // call this if the transaction that was supposed to destroy the actor failed.
+  static bool DestroyFallback(PTextureChild* actor);
 
   /**
    * Get the TextureClient corresponding to the actor passed in parameter.
    */
   static TextureClient* AsTextureClient(PTextureChild* actor);
 
   gfx::IntSize GetSize() const;
 
--- a/gfx/layers/composite/CompositableHost.cpp
+++ b/gfx/layers/composite/CompositableHost.cpp
@@ -222,16 +222,22 @@ CompositableHost::DumpTextureHost(std::s
   }
   RefPtr<gfx::DataSourceSurface> dSurf = aTexture->GetAsSurface();
   if (!dSurf) {
     return;
   }
   aStream << gfxUtils::GetAsDataURI(dSurf).get();
 }
 
+void
+CompositableHost::ReceivedDestroy(PCompositableParent* aActor)
+{
+  static_cast<CompositableParent*>(aActor)->RecvDestroy();
+}
+
 namespace CompositableMap {
 
 typedef std::map<uint64_t, PCompositableParent*> CompositableMap_t;
 static CompositableMap_t* sCompositableMap = nullptr;
 bool IsCreated() {
   return sCompositableMap != nullptr;
 }
 PCompositableParent* Get(uint64_t aID)
--- a/gfx/layers/composite/CompositableHost.h
+++ b/gfx/layers/composite/CompositableHost.h
@@ -171,16 +171,19 @@ public:
         aFlags & FORCE_DETACH) {
       SetLayer(nullptr);
       mAttached = false;
       mKeepAttached = false;
     }
   }
   bool IsAttached() { return mAttached; }
 
+  static void
+  ReceivedDestroy(PCompositableParent* aActor);
+
   virtual void Dump(std::stringstream& aStream,
                     const char* aPrefix="",
                     bool aDumpHtml=false) { }
   static void DumpTextureHost(std::stringstream& aStream, TextureHost* aTexture);
 
   virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() { return nullptr; }
 
   virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix) = 0;
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -830,16 +830,22 @@ TextureParent::Destroy()
   // Clear recycle callback.
   mTextureHost->ClearRecycleCallback();
   mWaitForClientRecycle = nullptr;
 
   mTextureHost->mActor = nullptr;
   mTextureHost = nullptr;
 }
 
+void
+TextureHost::ReceivedDestroy(PTextureParent* aActor)
+{
+  static_cast<TextureParent*>(aActor)->RecvDestroy();
+}
+
 bool
 TextureParent::RecvRecycleTexture(const TextureFlags& aTextureFlags)
 {
   if (!mTextureHost) {
     return true;
   }
   mTextureHost->RecycleTexture(aTextureFlags);
   return true;
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -458,16 +458,18 @@ public:
                                          TextureFlags aFlags);
   static bool DestroyIPDLActor(PTextureParent* actor);
 
   /**
    * Destroy the TextureChild/Parent pair.
    */
   static bool SendDeleteIPDLActor(PTextureParent* actor);
 
+  static void ReceivedDestroy(PTextureParent* actor);
+
   /**
    * Get the TextureHost corresponding to the actor passed in parameter.
    */
   static TextureHost* AsTextureHost(PTextureParent* actor);
 
   /**
    * Return a pointer to the IPDLActor.
    *
--- a/gfx/layers/ipc/CompositableForwarder.h
+++ b/gfx/layers/ipc/CompositableForwarder.h
@@ -79,16 +79,19 @@ public:
                                    const nsIntRegion& aUpdatedRegion) = 0;
 
 #ifdef MOZ_WIDGET_GONK
   virtual void UseOverlaySource(CompositableClient* aCompositabl,
                                 const OverlaySource& aOverlay,
                                 const gfx::IntRect& aPictureRect) = 0;
 #endif
 
+  virtual bool DestroyInTransaction(PTextureChild* aTexture, bool synchronously) = 0;
+  virtual bool DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously) = 0;
+
   /**
    * Tell the CompositableHost on the compositor side to remove the texture
    * from the CompositableHost.
    * This function does not delete the TextureHost corresponding to the
    * TextureClient passed in parameter.
    * When the TextureClient has TEXTURE_DEALLOCATE_CLIENT flag,
    * the transaction becomes synchronous.
    */
@@ -103,50 +106,16 @@ public:
    * TextureClient passed in parameter.
    * It is used when the TextureClient recycled.
    * Only ImageBridge implements it.
    */
   virtual void RemoveTextureFromCompositableAsync(AsyncTransactionTracker* aAsyncTransactionTracker,
                                                   CompositableClient* aCompositable,
                                                   TextureClient* aTexture) {}
 
-  /**
-   * Holds a reference to a TextureClient until after the next
-   * compositor transaction, and then drops it.
-   */
-  virtual void HoldUntilTransaction(TextureClient* aClient)
-  {
-    if (aClient) {
-      mTexturesToRemove.AppendElement(aClient);
-    }
-  }
-
-  /**
-   * Forcibly remove texture data from TextureClient
-   * This function needs to be called after a tansaction with Compositor.
-   */
-  virtual void RemoveTexturesIfNecessary()
-  {
-    mTexturesToRemove.Clear();
-  }
-
-  /**
-   * The same as above, but for CompositableClients.
-   */
-  void HoldUntilTransaction(CompositableClient* aClient)
-  {
-    if (aClient) {
-      mCompositableClientsToRemove.AppendElement(aClient);
-    }
-  }
-  void RemoveCompositablesIfNecessary()
-  {
-    mCompositableClientsToRemove.Clear();
-  }
-
   struct TimedTextureClient {
     TimedTextureClient()
         : mTextureClient(nullptr), mFrameID(0), mProducerID(0) {}
 
     TextureClient* mTextureClient;
     TimeStamp mTimeStamp;
     nsIntRect mPictureRect;
     int32_t mFrameID;
--- a/gfx/layers/ipc/CompositableTransactionParent.cpp
+++ b/gfx/layers/ipc/CompositableTransactionParent.cpp
@@ -228,18 +228,43 @@ CompositableParentManager::ReceiveCompos
       MOZ_ASSERT(false, "bad type");
     }
   }
 
   return true;
 }
 
 void
+CompositableParentManager::DestroyActor(const OpDestroy& aOp)
+{
+  switch (aOp.type()) {
+    case OpDestroy::TPTextureParent: {
+      auto actor = aOp.get_PTextureParent();
+      TextureHost::ReceivedDestroy(actor);
+      break;
+    }
+    case OpDestroy::TPCompositableParent: {
+      auto actor = aOp.get_PCompositableParent();
+      CompositableHost::ReceivedDestroy(actor);
+      break;
+    }
+    default: {
+      MOZ_ASSERT(false, "unsupported type");
+    }
+  }
+}
+
+void
 CompositableParentManager::SendPendingAsyncMessages()
 {
+  for (auto& actor : mDestroyedTextures) {
+    TextureHost::SendDeleteIPDLActor(actor);
+  }
+  mDestroyedTextures.clear();
+
   if (mPendingAsyncMessage.empty()) {
     return;
   }
 
   // Some type of AsyncParentMessageData message could have
   // one file descriptor (e.g. OpDeliverFence).
   // A number of file descriptors per gecko ipc message have a limitation
   // on OS_POSIX (MACOSX or LINUX).
--- a/gfx/layers/ipc/CompositableTransactionParent.h
+++ b/gfx/layers/ipc/CompositableTransactionParent.h
@@ -41,25 +41,28 @@ public:
   virtual base::ProcessId GetChildProcessId() = 0;
 
 protected:
   /**
    * Handle the IPDL messages that affect PCompositable actors.
    */
   bool ReceiveCompositableUpdate(const CompositableOperation& aEdit,
                                  EditReplyVector& replyv);
+  void DestroyActor(const OpDestroy& aOp);
+
   bool IsOnCompositorSide() const override { return true; }
 
   /**
    * Return true if this protocol is asynchronous with respect to the content
    * thread (ImageBridge for instance).
    */
   virtual bool IsAsync() const { return false; }
 
   virtual void ReplyRemoveTexture(const OpReplyRemoveTexture& aReply) {}
 
   std::vector<AsyncParentMessageData> mPendingAsyncMessage;
+  std::vector<PTextureParent*> mDestroyedTextures;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -51,16 +51,17 @@ namespace layers {
 
 using base::Thread;
 using base::ProcessId;
 using namespace mozilla::ipc;
 using namespace mozilla::gfx;
 using namespace mozilla::media;
 
 typedef std::vector<CompositableOperation> OpVector;
+typedef nsTArray<OpDestroy> OpDestroyVector;
 
 struct CompositableTransaction
 {
   CompositableTransaction()
   : mSwapRequired(false)
   , mFinished(true)
   {}
   ~CompositableTransaction()
@@ -76,33 +77,59 @@ struct CompositableTransaction
     MOZ_ASSERT(mFinished);
     mFinished = false;
   }
   void End()
   {
     mFinished = true;
     mSwapRequired = false;
     mOperations.clear();
+    mDestroyedActors.Clear();
   }
   bool IsEmpty() const
   {
-    return mOperations.empty();
+    return mOperations.empty() && mDestroyedActors.IsEmpty();
   }
   void AddNoSwapEdit(const CompositableOperation& op)
   {
     MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
     mOperations.push_back(op);
   }
   void AddEdit(const CompositableOperation& op)
   {
     AddNoSwapEdit(op);
+    MarkSyncTransaction();
+  }
+  void MarkSyncTransaction()
+  {
     mSwapRequired = true;
   }
 
+  void FallbackDestroyActors()
+  {
+    for (auto& actor : mDestroyedActors) {
+      switch (actor.type()) {
+      case OpDestroy::TPTextureChild: {
+        DebugOnly<bool> ok = TextureClient::DestroyFallback(actor.get_PTextureChild());
+        MOZ_ASSERT(ok);
+        break;
+      }
+      case OpDestroy::TPCompositableChild: {
+        DebugOnly<bool> ok = CompositableClient::DestroyFallback(actor.get_PCompositableChild());
+        MOZ_ASSERT(ok);
+        break;
+      }
+      default: MOZ_CRASH();
+      }
+    }
+    mDestroyedActors.Clear();
+  }
+
   OpVector mOperations;
+  OpDestroyVector mDestroyedActors;
   bool mSwapRequired;
   bool mFinished;
 };
 
 struct AutoEndTransaction {
   explicit AutoEndTransaction(CompositableTransaction* aTxn) : mTxn(aTxn) {}
   ~AutoEndTransaction() { mTxn->End(); }
   CompositableTransaction* mTxn;
@@ -173,16 +200,22 @@ ImageBridgeChild::UseOverlaySource(Compo
 static StaticRefPtr<ImageBridgeChild> sImageBridgeChildSingleton;
 static StaticRefPtr<ImageBridgeParent> sImageBridgeParentSingleton;
 static Thread *sImageBridgeChildThread = nullptr;
 
 void ReleaseImageBridgeParentSingleton() {
   sImageBridgeParentSingleton = nullptr;
 }
 
+void
+ImageBridgeChild::FallbackDestroyActors() {
+  if (mTxn && !mTxn->mDestroyedActors.IsEmpty()) {
+    mTxn->FallbackDestroyActors();
+  }
+}
 
 // dispatched function
 static void ImageBridgeShutdownStep1(ReentrantMonitor *aBarrier, bool *aDone)
 {
   ReentrantMonitorAutoEnter autoMon(*aBarrier);
 
   MOZ_ASSERT(InImageBridgeChildThread(),
              "Should be in ImageBridgeChild thread.");
@@ -199,16 +232,18 @@ static void ImageBridgeShutdownStep1(Ree
     InfallibleTArray<PTextureChild*> textures;
     sImageBridgeChildSingleton->ManagedPTextureChild(textures);
     for (int i = textures.Length() - 1; i >= 0; --i) {
       RefPtr<TextureClient> client = TextureClient::AsTextureClient(textures[i]);
       if (client) {
         client->Destroy();
       }
     }
+    sImageBridgeChildSingleton->FallbackDestroyActors();
+
     sImageBridgeChildSingleton->SendWillStop();
     sImageBridgeChildSingleton->MarkShutDown();
     // From now on, no message can be sent through the image bridge from the
     // client side except the final Stop message.
   }
 
   *aDone = true;
   aBarrier->NotifyAll();
@@ -602,38 +637,23 @@ void ImageBridgeChild::FlushAllImages(Im
 void
 ImageBridgeChild::BeginTransaction()
 {
   MOZ_ASSERT(!mShuttingDown);
   MOZ_ASSERT(mTxn->Finished(), "uncommitted txn?");
   mTxn->Begin();
 }
 
-class MOZ_STACK_CLASS AutoRemoveTexturesFromImageBridge
-{
-public:
-  explicit AutoRemoveTexturesFromImageBridge(ImageBridgeChild* aImageBridge)
-    : mImageBridge(aImageBridge) {}
-
-  ~AutoRemoveTexturesFromImageBridge()
-  {
-    mImageBridge->RemoveTexturesIfNecessary();
-  }
-private:
-  ImageBridgeChild* mImageBridge;
-};
-
 void
 ImageBridgeChild::EndTransaction()
 {
   MOZ_ASSERT(!mShuttingDown);
   MOZ_ASSERT(!mTxn->Finished(), "forgot BeginTransaction?");
 
   AutoEndTransaction _(mTxn);
-  AutoRemoveTexturesFromImageBridge autoRemoveTextures(this);
 
   if (mTxn->IsEmpty()) {
     return;
   }
 
   AutoInfallibleTArray<CompositableOperation, 10> cset;
   cset.SetCapacity(mTxn->mOperations.size());
   if (!mTxn->mOperations.empty()) {
@@ -642,25 +662,27 @@ ImageBridgeChild::EndTransaction()
 
   if (!IsSameProcess()) {
     ShadowLayerForwarder::PlatformSyncBeforeUpdate();
   }
 
   AutoInfallibleTArray<EditReply, 10> replies;
 
   if (mTxn->mSwapRequired) {
-    if (!SendUpdate(cset, &replies)) {
+    if (!SendUpdate(cset, mTxn->mDestroyedActors, &replies)) {
       NS_WARNING("could not send async texture transaction");
+      mTxn->FallbackDestroyActors();
       return;
     }
   } else {
     // If we don't require a swap we can call SendUpdateNoSwap which
     // assumes that aReplies is empty (DEBUG assertion)
-    if (!SendUpdateNoSwap(cset)) {
+    if (!SendUpdateNoSwap(cset, mTxn->mDestroyedActors)) {
       NS_WARNING("could not send async texture transaction (no swap)");
+      mTxn->FallbackDestroyActors();
       return;
     }
   }
   for (nsTArray<EditReply>::size_type i = 0; i < replies.Length(); ++i) {
     NS_RUNTIMEABORT("not reached");
   }
   SendPendingAsyncMessges();
 }
@@ -1091,16 +1113,45 @@ PTextureChild*
 ImageBridgeChild::CreateTexture(const SurfaceDescriptor& aSharedData,
                                 LayersBackend aLayersBackend,
                                 TextureFlags aFlags)
 {
   MOZ_ASSERT(!mShuttingDown);
   return SendPTextureConstructor(aSharedData, aLayersBackend, aFlags);
 }
 
+static bool
+IBCAddOpDestroy(CompositableTransaction* aTxn, const OpDestroy& op, bool synchronously)
+{
+  if (aTxn->Finished()) {
+    return false;
+  }
+
+  aTxn->mDestroyedActors.AppendElement(op);
+
+  if (synchronously) {
+    aTxn->MarkSyncTransaction();
+  }
+
+  return true;
+}
+
+bool
+ImageBridgeChild::DestroyInTransaction(PTextureChild* aTexture, bool synchronously)
+{
+  return IBCAddOpDestroy(mTxn, OpDestroy(aTexture), synchronously);
+}
+
+bool
+ImageBridgeChild::DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously)
+{
+  return IBCAddOpDestroy(mTxn, OpDestroy(aCompositable), synchronously);
+}
+
+
 void
 ImageBridgeChild::RemoveTextureFromCompositable(CompositableClient* aCompositable,
                                                 TextureClient* aTexture)
 {
   MOZ_ASSERT(!mShuttingDown);
   MOZ_ASSERT(aTexture);
   MOZ_ASSERT(aTexture->IsSharedWithCompositor());
   MOZ_ASSERT(aCompositable->IsConnected());
@@ -1109,18 +1160,16 @@ ImageBridgeChild::RemoveTextureFromCompo
   }
   if (aTexture->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) {
     mTxn->AddEdit(OpRemoveTexture(nullptr, aCompositable->GetIPDLActor(),
                                   nullptr, aTexture->GetIPDLActor()));
   } else {
     mTxn->AddNoSwapEdit(OpRemoveTexture(nullptr, aCompositable->GetIPDLActor(),
                                         nullptr, aTexture->GetIPDLActor()));
   }
-  // Hold texture until transaction complete.
-  HoldUntilTransaction(aTexture);
 }
 
 void
 ImageBridgeChild::RemoveTextureFromCompositableAsync(AsyncTransactionTracker* aAsyncTransactionTracker,
                                                      CompositableClient* aCompositable,
                                                      TextureClient* aTexture)
 {
   MOZ_ASSERT(!mShuttingDown);
--- a/gfx/layers/ipc/ImageBridgeChild.h
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -257,16 +257,19 @@ public:
                                          TextureClient* aClientOnBlack,
                                          TextureClient* aClientOnWhite) override;
 #ifdef MOZ_WIDGET_GONK
   virtual void UseOverlaySource(CompositableClient* aCompositable,
                                 const OverlaySource& aOverlay,
                                 const nsIntRect& aPictureRect) override;
 #endif
 
+  virtual bool DestroyInTransaction(PTextureChild* aTexture, bool synchronously) override;
+  virtual bool DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously) override;
+
   virtual void RemoveTextureFromCompositable(CompositableClient* aCompositable,
                                              TextureClient* aTexture) override;
 
   virtual void RemoveTextureFromCompositableAsync(AsyncTransactionTracker* aAsyncTransactionTracker,
                                                   CompositableClient* aCompositable,
                                                   TextureClient* aTexture) override;
 
   virtual void UseTiledLayerBuffer(CompositableClient* aCompositable,
@@ -313,16 +316,18 @@ public:
                                        LayersBackend aLayersBackend,
                                        TextureFlags aFlags) override;
 
   virtual bool IsSameProcess() const override;
 
   virtual void SendPendingAsyncMessges() override;
 
   void MarkShutDown();
+
+  void FallbackDestroyActors();
 protected:
   ImageBridgeChild();
   bool DispatchAllocShmemInternal(size_t aSize,
                                   SharedMemory::SharedMemoryType aType,
                                   Shmem* aShmem,
                                   bool aUnsafe);
 
   CompositableTransaction* mTxn;
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -124,47 +124,52 @@ public:
   {
     mImageBridge->SendPendingAsyncMessages();
   }
 private:
   ImageBridgeParent* mImageBridge;
 };
 
 bool
-ImageBridgeParent::RecvUpdate(EditArray&& aEdits, EditReplyArray* aReply)
+ImageBridgeParent::RecvUpdate(EditArray&& aEdits, OpDestroyArray&& aToDestroy,
+                              EditReplyArray* aReply)
 {
   AutoImageBridgeParentAsyncMessageSender autoAsyncMessageSender(this);
 
   EditReplyVector replyv;
   for (EditArray::index_type i = 0; i < aEdits.Length(); ++i) {
     if (!ReceiveCompositableUpdate(aEdits[i], replyv)) {
       return false;
     }
   }
 
+  for (const auto& op : aToDestroy) {
+    DestroyActor(op);
+  }
+
   aReply->SetCapacity(replyv.size());
   if (replyv.size() > 0) {
     aReply->AppendElements(&replyv.front(), replyv.size());
   }
 
   if (!IsSameProcess()) {
     // Ensure that any pending operations involving back and front
     // buffers have completed, so that neither process stomps on the
     // other's buffer contents.
     LayerManagerComposite::PlatformSyncBeforeReplyUpdate();
   }
 
   return true;
 }
 
 bool
-ImageBridgeParent::RecvUpdateNoSwap(EditArray&& aEdits)
+ImageBridgeParent::RecvUpdateNoSwap(EditArray&& aEdits, OpDestroyArray&& aToDestroy)
 {
   InfallibleTArray<EditReply> noReplies;
-  bool success = RecvUpdate(Move(aEdits), &noReplies);
+  bool success = RecvUpdate(Move(aEdits), Move(aToDestroy), &noReplies);
   MOZ_ASSERT(noReplies.Length() == 0, "RecvUpdateNoSwap requires a sync Update to carry Edits");
   return success;
 }
 
 static void
 ConnectImageBridgeInParentProcess(ImageBridgeParent* aBridge,
                                   Transport* aTransport,
                                   base::ProcessId aOtherPid)
--- a/gfx/layers/ipc/ImageBridgeParent.h
+++ b/gfx/layers/ipc/ImageBridgeParent.h
@@ -38,16 +38,17 @@ namespace layers {
  * It's purpose is mainly to setup the IPDL connection. Most of the
  * interesting stuff is in ImageContainerParent.
  */
 class ImageBridgeParent final : public PImageBridgeParent,
                                 public CompositableParentManager
 {
 public:
   typedef InfallibleTArray<CompositableOperation> EditArray;
+  typedef InfallibleTArray<OpDestroy> OpDestroyArray;
   typedef InfallibleTArray<EditReply> EditReplyArray;
   typedef InfallibleTArray<AsyncChildMessageData> AsyncChildMessageArray;
 
   ImageBridgeParent(MessageLoop* aLoop, Transport* aTransport, ProcessId aChildProcessId);
   ~ImageBridgeParent();
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
@@ -62,18 +63,19 @@ public:
 
   virtual base::ProcessId GetChildProcessId() override
   {
     return OtherPid();
   }
 
   // PImageBridge
   virtual bool RecvImageBridgeThreadId(const PlatformThreadId& aThreadId) override;
-  virtual bool RecvUpdate(EditArray&& aEdits, EditReplyArray* aReply) override;
-  virtual bool RecvUpdateNoSwap(EditArray&& aEdits) override;
+  virtual bool RecvUpdate(EditArray&& aEdits, OpDestroyArray&& aToDestroy,
+                          EditReplyArray* aReply) override;
+  virtual bool RecvUpdateNoSwap(EditArray&& aEdits, OpDestroyArray&& aToDestroy) override;
 
   virtual bool IsAsync() const override { return true; }
 
   PCompositableParent* AllocPCompositableParent(const TextureInfo& aInfo,
                                                 PImageContainerParent* aImageContainer,
                                                 uint64_t*) override;
   bool DeallocPCompositableParent(PCompositableParent* aActor) override;
 
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -182,27 +182,29 @@ LayerTransactionParent::Destroy()
     RefPtr<TextureHost> tex = TextureHost::AsTextureHost(textures[i]);
     tex->DeallocateDeviceData();
   }
   mDestroyed = true;
 }
 
 bool
 LayerTransactionParent::RecvUpdateNoSwap(InfallibleTArray<Edit>&& cset,
+                                         InfallibleTArray<OpDestroy>&& aToDestroy,
                                          const uint64_t& aTransactionId,
                                          const TargetConfig& targetConfig,
                                          PluginsArray&& aPlugins,
                                          const bool& isFirstPaint,
                                          const bool& scheduleComposite,
                                          const uint32_t& paintSequenceNumber,
                                          const bool& isRepeatTransaction,
                                          const mozilla::TimeStamp& aTransactionStart,
                                          const int32_t& aPaintSyncId)
 {
-  return RecvUpdate(Move(cset), aTransactionId, targetConfig, Move(aPlugins), isFirstPaint,
+  return RecvUpdate(Move(cset), Move(aToDestroy),
+      aTransactionId, targetConfig, Move(aPlugins), isFirstPaint,
       scheduleComposite, paintSequenceNumber, isRepeatTransaction,
       aTransactionStart, aPaintSyncId, nullptr);
 }
 
 class MOZ_STACK_CLASS AutoLayerTransactionParentAsyncMessageSender
 {
 public:
   explicit AutoLayerTransactionParentAsyncMessageSender(LayerTransactionParent* aLayerTransaction)
@@ -214,16 +216,17 @@ public:
     ImageBridgeParent::SendPendingAsyncMessages(mLayerTransaction->GetChildProcessId());
   }
 private:
   LayerTransactionParent* mLayerTransaction;
 };
 
 bool
 LayerTransactionParent::RecvUpdate(InfallibleTArray<Edit>&& cset,
+                                   InfallibleTArray<OpDestroy>&& aToDestroy,
                                    const uint64_t& aTransactionId,
                                    const TargetConfig& targetConfig,
                                    PluginsArray&& aPlugins,
                                    const bool& isFirstPaint,
                                    const bool& scheduleComposite,
                                    const uint32_t& paintSequenceNumber,
                                    const bool& isRepeatTransaction,
                                    const mozilla::TimeStamp& aTransactionStart,
@@ -236,16 +239,20 @@ LayerTransactionParent::RecvUpdate(Infal
 
 #ifdef COMPOSITOR_PERFORMANCE_WARNING
   TimeStamp updateStart = TimeStamp::Now();
 #endif
 
   MOZ_LAYERS_LOG(("[ParentSide] received txn with %d edits", cset.Length()));
 
   if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) {
+    for (const auto& op : aToDestroy) {
+      DestroyActor(op);
+    }
+
     return true;
   }
 
   EditReplyVector replyv;
   AutoLayerTransactionParentAsyncMessageSender autoAsyncMessageSender(this);
 
   {
     AutoResolveRefLayers resolve(mShadowLayersManager->GetCompositionManager(this));
@@ -588,16 +595,20 @@ LayerTransactionParent::RecvUpdate(Infal
       host->SetCompositorID(mLayerManager->GetCompositor()->GetCompositorID());
       break;
     }
     default:
       NS_RUNTIMEABORT("not reached");
     }
   }
 
+  for (const auto& op : aToDestroy) {
+    DestroyActor(op);
+  }
+
   mShadowLayersManager->ShadowLayersUpdated(this, aTransactionId, targetConfig,
                                             aPlugins, isFirstPaint, scheduleComposite,
                                             paintSequenceNumber, isRepeatTransaction,
                                             aPaintSyncId);
 
   {
     AutoResolveRefLayers resolve(mShadowLayersManager->GetCompositionManager(this));
     layer_manager()->EndTransaction(TimeStamp(), LayerManager::END_NO_IMMEDIATE_REDRAW);
--- a/gfx/layers/ipc/LayerTransactionParent.h
+++ b/gfx/layers/ipc/LayerTransactionParent.h
@@ -35,16 +35,17 @@ class ShadowLayerParent;
 class CompositableParent;
 class ShadowLayersManager;
 
 class LayerTransactionParent final : public PLayerTransactionParent,
                                      public CompositableParentManager
 {
   typedef mozilla::layout::RenderFrameParent RenderFrameParent;
   typedef InfallibleTArray<Edit> EditArray;
+  typedef InfallibleTArray<OpDestroy> OpDestroyArray;
   typedef InfallibleTArray<EditReply> EditReplyArray;
   typedef InfallibleTArray<AsyncChildMessageData> AsyncChildMessageArray;
   typedef InfallibleTArray<PluginWindowData> PluginsArray;
 
 public:
   LayerTransactionParent(LayerManagerComposite* aManager,
                          ShadowLayersManager* aLayersManager,
                          uint64_t aId);
@@ -88,28 +89,30 @@ public:
   }
 
   virtual void ReplyRemoveTexture(const OpReplyRemoveTexture& aReply) override;
 
 protected:
   virtual bool RecvShutdown() override;
 
   virtual bool RecvUpdate(EditArray&& cset,
+                          OpDestroyArray&& aToDestroy,
                           const uint64_t& aTransactionId,
                           const TargetConfig& targetConfig,
                           PluginsArray&& aPlugins,
                           const bool& isFirstPaint,
                           const bool& scheduleComposite,
                           const uint32_t& paintSequenceNumber,
                           const bool& isRepeatTransaction,
                           const mozilla::TimeStamp& aTransactionStart,
                           const int32_t& aPaintSyncId,
                           EditReplyArray* reply) override;
 
   virtual bool RecvUpdateNoSwap(EditArray&& cset,
+                                OpDestroyArray&& aToDestroy,
                                 const uint64_t& aTransactionId,
                                 const TargetConfig& targetConfig,
                                 PluginsArray&& aPlugins,
                                 const bool& isFirstPaint,
                                 const bool& scheduleComposite,
                                 const uint32_t& paintSequenceNumber,
                                 const bool& isRepeatTransaction,
                                 const mozilla::TimeStamp& aTransactionStart,
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -466,16 +466,22 @@ union Edit {
   OpRaiseToTopChild;
 
   OpAttachCompositable;
   OpAttachAsyncCompositable;
 
   CompositableOperation;
 };
 
+// Operations related to destroying resources, always handled after the other
+// operations for safety.
+union OpDestroy {
+  PTexture;
+  PCompositable;
+};
 
 // Replies to operations
 
 struct OpContentBufferSwap {
   PCompositable compositable;
   nsIntRegion frontUpdatedRegion;
 };
 
--- a/gfx/layers/ipc/PImageBridge.ipdl
+++ b/gfx/layers/ipc/PImageBridge.ipdl
@@ -37,18 +37,20 @@ sync protocol PImageBridge
 child:
   async ParentAsyncMessages(AsyncParentMessageData[] aMessages);
 
   async DidComposite(ImageCompositeNotification[] aNotifications);
 
 parent:
   async ImageBridgeThreadId(PlatformThreadId aTreahdId);
 
-  sync Update(CompositableOperation[] ops) returns (EditReply[] reply);
-  async UpdateNoSwap(CompositableOperation[] ops);
+  sync Update(CompositableOperation[] ops, OpDestroy[] toDestroy)
+    returns (EditReply[] reply);
+
+  async UpdateNoSwap(CompositableOperation[] ops, OpDestroy[] toDestroy);
 
   // First step of the destruction sequence. This puts ImageBridge
   // in a state in which it can't send asynchronous messages
   // so as to not race with the upcomming Stop message and destruction.
   // In the child side, the Stop message is not sent right after WillStop,
   // it is scheduled in the ImageBridgeChild's message queue in order to ensure
   // that all of the messages from the parent side have been received and processed
   // before sending Stop, and that after Stop returns, there is no message in
--- a/gfx/layers/ipc/PLayerTransaction.ipdl
+++ b/gfx/layers/ipc/PLayerTransaction.ipdl
@@ -50,26 +50,28 @@ child:
 
 parent:
   async PLayer();
   async PCompositable(TextureInfo aTextureInfo);
   async PTexture(SurfaceDescriptor aSharedData, LayersBackend aBackend, TextureFlags aTextureFlags);
 
   // The isFirstPaint flag can be used to indicate that this is the first update
   // for a particular document.
-  sync Update(Edit[] cset, uint64_t id, TargetConfig targetConfig,
+  sync Update(Edit[] cset, OpDestroy[] toDestroy,
+              uint64_t id, TargetConfig targetConfig,
               PluginWindowData[] plugins, bool isFirstPaint,
               bool scheduleComposite, uint32_t paintSequenceNumber,
               bool isRepeatTransaction, TimeStamp transactionStart,
               int32_t paintSyncId)
     returns (EditReply[] reply);
 
   // We don't need to send a sync transaction if
   // no transaction operate require a swap.
-  async UpdateNoSwap(Edit[] cset, uint64_t id, TargetConfig targetConfig,
+  async UpdateNoSwap(Edit[] cset, OpDestroy[] toDestroy,
+                     uint64_t id, TargetConfig targetConfig,
                      PluginWindowData[] plugins, bool isFirstPaint,
                      bool scheduleComposite, uint32_t paintSequenceNumber,
                      bool isRepeatTransaction, TimeStamp transactionStart,
                      int32_t paintSyncId);
 
   // Testing APIs
 
   // Enter test mode, set the sample time to sampleTime, and resample
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -12,16 +12,17 @@
 #include "ISurfaceAllocator.h"          // for IsSurfaceDescriptorValid
 #include "Layers.h"                     // for Layer
 #include "RenderTrace.h"                // for RenderTraceScope
 #include "ShadowLayerChild.h"           // for ShadowLayerChild
 #include "gfx2DGlue.h"                  // for Moz2D transition helpers
 #include "gfxPlatform.h"                // for gfxImageFormat, gfxPlatform
 #include "gfxSharedImageSurface.h"      // for gfxSharedImageSurface
 #include "ipc/IPCMessageUtils.h"        // for gfxContentType, null_t
+#include "IPDLActor.h"
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/layers/CompositableClient.h"  // for CompositableClient, etc
 #include "mozilla/layers/LayersMessages.h"  // for Edit, etc
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor, etc
 #include "mozilla/layers/LayersTypes.h"  // for MOZ_LAYERS_LOG
 #include "mozilla/layers/LayerTransactionChild.h"
 #include "ShadowLayerUtils.h"
@@ -43,16 +44,17 @@ using namespace mozilla::gfx;
 using namespace mozilla::gl;
 using namespace mozilla::ipc;
 
 class ClientTiledLayerBuffer;
 
 typedef nsTArray<SurfaceDescriptor> BufferArray;
 typedef std::vector<Edit> EditVector;
 typedef std::set<ShadowableLayer*> ShadowableLayerSet;
+typedef nsTArray<OpDestroy> OpDestroyVector;
 
 class Transaction
 {
 public:
   Transaction()
     : mTargetRotation(ROTATION_0)
     , mSwapRequired(false)
     , mOpen(false)
@@ -113,33 +115,56 @@ public:
     MOZ_ASSERT(!Finished(), "forgot BeginTransaction?");
     mMutants.insert(aLayer);
   }
   void End()
   {
     mCset.clear();
     mPaints.clear();
     mMutants.clear();
+    mDestroyedActors.Clear();
     mOpen = false;
     mSwapRequired = false;
     mRotationChanged = false;
   }
 
   bool Empty() const {
-    return mCset.empty() && mPaints.empty() && mMutants.empty();
+    return mCset.empty() && mPaints.empty() && mMutants.empty()
+           && mDestroyedActors.IsEmpty();
   }
   bool RotationChanged() const {
     return mRotationChanged;
   }
   bool Finished() const { return !mOpen && Empty(); }
 
   bool Opened() const { return mOpen; }
 
+  void FallbackDestroyActors()
+  {
+    for (auto& actor : mDestroyedActors) {
+      switch (actor.type()) {
+      case OpDestroy::TPTextureChild: {
+        DebugOnly<bool> ok = TextureClient::DestroyFallback(actor.get_PTextureChild());
+        MOZ_ASSERT(ok);
+        break;
+      }
+      case OpDestroy::TPCompositableChild: {
+        DebugOnly<bool> ok = CompositableClient::DestroyFallback(actor.get_PCompositableChild());
+        MOZ_ASSERT(ok);
+        break;
+      }
+      default: MOZ_CRASH();
+      }
+    }
+    mDestroyedActors.Clear();
+  }
+
   EditVector mCset;
   EditVector mPaints;
+  OpDestroyVector mDestroyedActors;
   ShadowableLayerSet mMutants;
   gfx::IntRect mTargetBounds;
   ScreenRotation mTargetRotation;
   dom::ScreenOrientationInternal mTargetOrientation;
   bool mSwapRequired;
 
 private:
   bool mOpen;
@@ -170,16 +195,19 @@ ShadowLayerForwarder::ShadowLayerForward
  , mPaintSyncId(0)
 {
   mTxn = new Transaction();
 }
 
 ShadowLayerForwarder::~ShadowLayerForwarder()
 {
   MOZ_ASSERT(mTxn->Finished(), "unfinished transaction?");
+  if (!mTxn->mDestroyedActors.IsEmpty()) {
+    mTxn->FallbackDestroyActors();
+  }
   delete mTxn;
   if (mShadowManager) {
     mShadowManager->SetForwarder(nullptr);
     mShadowManager->Destroy();
   }
 }
 
 void
@@ -410,16 +438,43 @@ ShadowLayerForwarder::UseOverlaySource(C
   MOZ_ASSERT(aCompositable);
   MOZ_ASSERT(aCompositable->IsConnected());
 
   mTxn->AddEdit(OpUseOverlaySource(nullptr, aCompositable->GetIPDLActor(),
       aOverlay, aPictureRect));
 }
 #endif
 
+static bool
+AddOpDestroy(Transaction* aTxn, const OpDestroy& op, bool synchronously)
+{
+  if (!aTxn->Opened()) {
+    return false;
+  }
+
+  aTxn->mDestroyedActors.AppendElement(op);
+  if (synchronously) {
+    aTxn->MarkSyncTransaction();
+  }
+
+  return true;
+}
+
+bool
+ShadowLayerForwarder::DestroyInTransaction(PTextureChild* aTexture, bool synchronously)
+{
+  return AddOpDestroy(mTxn, OpDestroy(aTexture), synchronously);
+}
+
+bool
+ShadowLayerForwarder::DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously)
+{
+  return AddOpDestroy(mTxn, OpDestroy(aCompositable), synchronously);
+}
+
 void
 ShadowLayerForwarder::RemoveTextureFromCompositable(CompositableClient* aCompositable,
                                                     TextureClient* aTexture)
 {
   MOZ_ASSERT(aCompositable);
   MOZ_ASSERT(aTexture);
   MOZ_ASSERT(aCompositable->IsConnected());
   MOZ_ASSERT(aTexture->GetIPDLActor());
@@ -428,18 +483,16 @@ ShadowLayerForwarder::RemoveTextureFromC
     return;
   }
 
   mTxn->AddEdit(OpRemoveTexture(nullptr, aCompositable->GetIPDLActor(),
                                 nullptr, aTexture->GetIPDLActor()));
   if (aTexture->GetFlags() & TextureFlags::DEALLOCATE_CLIENT) {
     mTxn->MarkSyncTransaction();
   }
-  // Hold texture until transaction complete.
-  HoldUntilTransaction(aTexture);
 }
 
 void
 ShadowLayerForwarder::RemoveTextureFromCompositableAsync(AsyncTransactionTracker* aAsyncTransactionTracker,
                                                      CompositableClient* aCompositable,
                                                      TextureClient* aTexture)
 {
   MOZ_ASSERT(aCompositable);
@@ -642,35 +695,39 @@ ShadowLayerForwarder::EndTransaction(Inf
   }
 
   profiler_tracing("Paint", "Rasterize", TRACING_INTERVAL_END);
   if (mTxn->mSwapRequired) {
     MOZ_LAYERS_LOG(("[LayersForwarder] sending transaction..."));
     RenderTraceScope rendertrace3("Forward Transaction", "000093");
     if (!HasShadowManager() ||
         !mShadowManager->IPCOpen() ||
-        !mShadowManager->SendUpdate(cset, aId, targetConfig, mPluginWindowData,
+        !mShadowManager->SendUpdate(cset, mTxn->mDestroyedActors,
+                                    aId, targetConfig, mPluginWindowData,
                                     mIsFirstPaint, aScheduleComposite,
                                     aPaintSequenceNumber, aIsRepeatTransaction,
                                     aTransactionStart, mPaintSyncId, aReplies)) {
       MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!"));
+      mTxn->FallbackDestroyActors();
       return false;
     }
   } else {
     // If we don't require a swap we can call SendUpdateNoSwap which
     // assumes that aReplies is empty (DEBUG assertion)
     MOZ_LAYERS_LOG(("[LayersForwarder] sending no swap transaction..."));
     RenderTraceScope rendertrace3("Forward NoSwap Transaction", "000093");
     if (!HasShadowManager() ||
         !mShadowManager->IPCOpen() ||
-        !mShadowManager->SendUpdateNoSwap(cset, aId, targetConfig, mPluginWindowData,
+        !mShadowManager->SendUpdateNoSwap(cset, mTxn->mDestroyedActors,
+                                          aId, targetConfig, mPluginWindowData,
                                           mIsFirstPaint, aScheduleComposite,
                                           aPaintSequenceNumber, aIsRepeatTransaction,
                                           aTransactionStart, mPaintSyncId)) {
       MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!"));
+      mTxn->FallbackDestroyActors();
       return false;
     }
   }
 
   *aSent = true;
   mIsFirstPaint = false;
   mPaintSyncId = 0;
   MOZ_LAYERS_LOG(("[LayersForwarder] ... done"));
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -209,16 +209,19 @@ public:
                ShadowableLayer* aMaskLayer);
 
   /**
    * See CompositableForwarder::UseTiledLayerBuffer
    */
   virtual void UseTiledLayerBuffer(CompositableClient* aCompositable,
                                    const SurfaceDescriptorTiles& aTileLayerDescriptor) override;
 
+  virtual bool DestroyInTransaction(PTextureChild* aTexture, bool synchronously) override;
+  virtual bool DestroyInTransaction(PCompositableChild* aCompositable, bool synchronously) override;
+
   virtual void RemoveTextureFromCompositable(CompositableClient* aCompositable,
                                              TextureClient* aTexture) override;
 
   virtual void RemoveTextureFromCompositableAsync(AsyncTransactionTracker* aAsyncTransactionTracker,
                                                   CompositableClient* aCompositable,
                                                   TextureClient* aTexture) override;
 
   /**
--- a/gfx/thebes/gfxDrawable.cpp
+++ b/gfx/thebes/gfxDrawable.cpp
@@ -25,17 +25,19 @@ gfxSurfaceDrawable::gfxSurfaceDrawable(S
  , mTransform(aTransform)
 {
   if (!mSourceSurface) {
     gfxWarning() << "Creating gfxSurfaceDrawable with null SourceSurface";
   }
 }
 
 bool
-gfxSurfaceDrawable::DrawWithSamplingRect(gfxContext* aContext,
+gfxSurfaceDrawable::DrawWithSamplingRect(DrawTarget* aDrawTarget,
+                                         CompositionOp aOp,
+                                         AntialiasMode aAntialiasMode,
                                          const gfxRect& aFillRect,
                                          const gfxRect& aSamplingRect,
                                          ExtendMode aExtendMode,
                                          const Filter& aFilter,
                                          gfxFloat aOpacity)
 {
   if (!mSourceSurface) {
     return true;
@@ -47,64 +49,66 @@ gfxSurfaceDrawable::DrawWithSamplingRect
   samplingRect.RoundOut();
   IntRect intRect(samplingRect.x, samplingRect.y, samplingRect.width, samplingRect.height);
 
   IntSize size = mSourceSurface->GetSize();
   if (!IntRect(0, 0, size.width, size.height).Contains(intRect)) {
     return false;
   }
 
-  DrawInternal(aContext, aFillRect, intRect, ExtendMode::CLAMP, aFilter, aOpacity, gfxMatrix());
+  DrawInternal(aDrawTarget, aOp, aAntialiasMode, aFillRect, intRect,
+               ExtendMode::CLAMP, aFilter, aOpacity, gfxMatrix());
   return true;
 }
 
 bool
 gfxSurfaceDrawable::Draw(gfxContext* aContext,
                          const gfxRect& aFillRect,
                          ExtendMode aExtendMode,
                          const Filter& aFilter,
                          gfxFloat aOpacity,
                          const gfxMatrix& aTransform)
 
 {
   if (!mSourceSurface) {
     return true;
   }
 
-  DrawInternal(aContext, aFillRect, IntRect(), aExtendMode, aFilter, aOpacity, aTransform);
+  DrawInternal(aContext->GetDrawTarget(), aContext->CurrentOp(),
+               aContext->CurrentAntialiasMode(), aFillRect, IntRect(),
+               aExtendMode, aFilter, aOpacity, aTransform);
   return true;
 }
 
 void
-gfxSurfaceDrawable::DrawInternal(gfxContext* aContext,
+gfxSurfaceDrawable::DrawInternal(DrawTarget* aDrawTarget,
+                                 CompositionOp aOp,
+                                 AntialiasMode aAntialiasMode,
                                  const gfxRect& aFillRect,
                                  const IntRect& aSamplingRect,
                                  ExtendMode aExtendMode,
                                  const Filter& aFilter,
                                  gfxFloat aOpacity,
                                  const gfxMatrix& aTransform)
 {
     Matrix patternTransform = ToMatrix(aTransform * mTransform);
     patternTransform.Invert();
 
     SurfacePattern pattern(mSourceSurface, aExtendMode,
                            patternTransform, aFilter, aSamplingRect);
 
     Rect fillRect = ToRect(aFillRect);
-    DrawTarget* dt = aContext->GetDrawTarget();
 
-    if (aContext->CurrentOp() == CompositionOp::OP_SOURCE &&
-        aOpacity == 1.0) {
+    if (aOp == CompositionOp::OP_SOURCE && aOpacity == 1.0) {
         // Emulate cairo operator source which is bound by mask!
-        dt->ClearRect(fillRect);
-        dt->FillRect(fillRect, pattern);
+        aDrawTarget->ClearRect(fillRect);
+        aDrawTarget->FillRect(fillRect, pattern);
     } else {
-        dt->FillRect(fillRect, pattern,
-                     DrawOptions(aOpacity, aContext->CurrentOp(),
-                                 aContext->CurrentAntialiasMode()));
+        aDrawTarget->FillRect(fillRect, pattern,
+                              DrawOptions(aOpacity, aOp, aAntialiasMode));
     }
 }
 
 gfxCallbackDrawable::gfxCallbackDrawable(gfxDrawingCallback* aCallback,
                                          const IntSize aSize)
  : gfxDrawable(aSize)
  , mCallback(aCallback)
 {
--- a/gfx/thebes/gfxDrawable.h
+++ b/gfx/thebes/gfxDrawable.h
@@ -18,16 +18,20 @@ class gfxPattern;
 /**
  * gfxDrawable
  * An Interface representing something that has an intrinsic size and can draw
  * itself repeatedly.
  */
 class gfxDrawable {
     NS_INLINE_DECL_REFCOUNTING(gfxDrawable)
 public:
+    typedef mozilla::gfx::AntialiasMode AntialiasMode;
+    typedef mozilla::gfx::CompositionOp CompositionOp;
+    typedef mozilla::gfx::DrawTarget DrawTarget;
+
     explicit gfxDrawable(const mozilla::gfx::IntSize aSize)
      : mSize(aSize) {}
 
     /**
      * Draw into aContext filling aFillRect, possibly repeating, using aFilter.
      * aTransform is a userspace to "image"space matrix. For example, if Draw
      * draws using a gfxPattern, this is the matrix that should be set on the
      * pattern prior to rendering it.
@@ -35,17 +39,19 @@ public:
      */
     virtual bool Draw(gfxContext* aContext,
                         const gfxRect& aFillRect,
                         mozilla::gfx::ExtendMode aExtendMode,
                         const mozilla::gfx::Filter& aFilter,
                         gfxFloat aOpacity = 1.0,
                         const gfxMatrix& aTransform = gfxMatrix()) = 0;
 
-    virtual bool DrawWithSamplingRect(gfxContext* aContext,
+    virtual bool DrawWithSamplingRect(DrawTarget* aDrawTarget,
+                                      CompositionOp aOp,
+                                      AntialiasMode aAntialiasMode,
                                       const gfxRect& aFillRect,
                                       const gfxRect& aSamplingRect,
                                       mozilla::gfx::ExtendMode aExtendMode,
                                       const mozilla::gfx::Filter& aFilter,
                                       gfxFloat aOpacity = 1.0)
     {
         return false;
     }
@@ -71,25 +77,29 @@ public:
 
     virtual bool Draw(gfxContext* aContext,
                         const gfxRect& aFillRect,
                         mozilla::gfx::ExtendMode aExtendMode,
                         const mozilla::gfx::Filter& aFilter,
                         gfxFloat aOpacity = 1.0,
                         const gfxMatrix& aTransform = gfxMatrix());
 
-    virtual bool DrawWithSamplingRect(gfxContext* aContext,
+    virtual bool DrawWithSamplingRect(DrawTarget* aDrawTarget,
+                                      CompositionOp aOp,
+                                      AntialiasMode aAntialiasMode,
                                       const gfxRect& aFillRect,
                                       const gfxRect& aSamplingRect,
                                       mozilla::gfx::ExtendMode aExtendMode,
                                       const mozilla::gfx::Filter& aFilter,
                                       gfxFloat aOpacity = 1.0);
 
 protected:
-    void DrawInternal(gfxContext* aContext,
+    void DrawInternal(DrawTarget* aDrawTarget,
+                      CompositionOp aOp,
+                      AntialiasMode aAntialiasMode,
                       const gfxRect& aFillRect,
                       const mozilla::gfx::IntRect& aSamplingRect,
                       mozilla::gfx::ExtendMode aExtendMode,
                       const mozilla::gfx::Filter& aFilter,
                       gfxFloat aOpacity,
                       const gfxMatrix& aTransform = gfxMatrix());
 
     RefPtr<mozilla::gfx::SourceSurface> mSourceSurface;
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -741,17 +741,21 @@ gfxUtils::DrawPixelSnapped(gfxContext*  
     // OK now, the hard part left is to account for the subimage sampling
     // restriction. If all the transforms involved are just integer
     // translations, then we assume no resampling will occur so there's
     // nothing to do.
     // XXX if only we had source-clipping in cairo!
 
     if (aContext->CurrentMatrix().HasNonIntegerTranslation()) {
         if ((extendMode != ExtendMode::CLAMP) || !aRegion.RestrictionContains(imageRect)) {
-            if (drawable->DrawWithSamplingRect(aContext, aRegion.Rect(), aRegion.Restriction(),
+            if (drawable->DrawWithSamplingRect(aContext->GetDrawTarget(),
+                                               aContext->CurrentOp(),
+                                               aContext->CurrentAntialiasMode(),
+                                               aRegion.Rect(),
+                                               aRegion.Restriction(),
                                                extendMode, aFilter, aOpacity)) {
               return;
             }
 
 #ifdef MOZ_WIDGET_COCOA
             if (PrescaleAndTileDrawable(aDrawable, aContext, aRegion,
                                         ToRect(imageRect), aFilter,
                                         aFormat, aOpacity)) {
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -21,16 +21,17 @@
 #include "vm/NativeObject-inl.h"
 
 using namespace js;
 using namespace js::unicode;
 
 using mozilla::ArrayLength;
 using mozilla::Maybe;
 
+/* ES6 21.2.5.2.2 steps 19-29. */
 bool
 js::CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs& matches,
                             MutableHandleValue rval)
 {
     MOZ_ASSERT(input);
 
     /*
      * Create the (slow) result array for a match.
@@ -45,70 +46,78 @@ js::CreateRegExpMatchResult(JSContext* c
     /* Get the templateObject that defines the shape and type of the output object */
     JSObject* templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
     if (!templateObject)
         return false;
 
     size_t numPairs = matches.length();
     MOZ_ASSERT(numPairs > 0);
 
+    /* Step 19. */
     RootedArrayObject arr(cx, NewDenseFullyAllocatedArrayWithTemplate(cx, numPairs, templateObject));
     if (!arr)
         return false;
 
-    /* Store a Value for each pair. */
+    /* Steps 27-28
+     * Store a Value for each pair. */
     for (size_t i = 0; i < numPairs; i++) {
         const MatchPair& pair = matches[i];
 
         if (pair.isUndefined()) {
             MOZ_ASSERT(i != 0); /* Since we had a match, first pair must be present. */
             arr->setDenseInitializedLength(i + 1);
             arr->initDenseElement(i, UndefinedValue());
         } else {
             JSLinearString* str = NewDependentString(cx, input, pair.start, pair.length());
             if (!str)
                 return false;
             arr->setDenseInitializedLength(i + 1);
             arr->initDenseElement(i, StringValue(str));
         }
     }
 
-    /* Set the |index| property. (TemplateObject positions it in slot 0) */
+    /* Step 24 (reordered)
+     * Set the |index| property. (TemplateObject positions it in slot 0) */
     arr->setSlot(0, Int32Value(matches[0].start));
 
-    /* Set the |input| property. (TemplateObject positions it in slot 1) */
+    /* Step 25 (reordered)
+     * Set the |input| property. (TemplateObject positions it in slot 1) */
     arr->setSlot(1, StringValue(input));
 
 #ifdef DEBUG
     RootedValue test(cx);
     RootedId id(cx, NameToId(cx->names().index));
     if (!NativeGetProperty(cx, arr, id, &test))
         return false;
     MOZ_ASSERT(test == arr->getSlot(0));
     id = NameToId(cx->names().input);
     if (!NativeGetProperty(cx, arr, id, &test))
         return false;
     MOZ_ASSERT(test == arr->getSlot(1));
 #endif
 
+    /* Step 29. */
     rval.setObject(*arr);
     return true;
 }
 
+/* ES6 21.2.5.2.2 steps 3, 14-17, except 15.a.i-ii, 15.c.i.1-2. */
 static RegExpRunStatus
 ExecuteRegExpImpl(JSContext* cx, RegExpStatics* res, RegExpShared& re, HandleLinearString input,
-                  size_t searchIndex, MatchPairs* matches)
+                  size_t searchIndex, bool sticky, MatchPairs* matches, size_t* endIndex)
 {
-    RegExpRunStatus status = re.execute(cx, input, searchIndex, matches);
+    RegExpRunStatus status = re.execute(cx, input, searchIndex, sticky, matches, endIndex);
+
+    /* Out of spec: Update RegExpStatics. */
     if (status == RegExpRunStatus_Success && res) {
         if (matches) {
             if (!res->updateFromMatchPairs(cx, input, *matches))
                 return RegExpRunStatus_Error;
         } else {
-            res->updateLazily(cx, input, &re, searchIndex);
+            res->updateLazily(cx, input, &re, searchIndex, sticky);
         }
     }
     return status;
 }
 
 /* Legacy ExecuteRegExp behavior is baked into the JSAPI. */
 bool
 js::ExecuteRegExpLegacy(JSContext* cx, RegExpStatics* res, RegExpObject& reobj,
@@ -116,17 +125,18 @@ js::ExecuteRegExpLegacy(JSContext* cx, R
                         MutableHandleValue rval)
 {
     RegExpGuard shared(cx);
     if (!reobj.getShared(cx, &shared))
         return false;
 
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
 
-    RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *shared, input, *lastIndex, &matches);
+    RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *shared, input, *lastIndex, reobj.sticky(),
+                                               &matches, nullptr);
     if (status == RegExpRunStatus_Error)
         return false;
 
     if (status == RegExpRunStatus_Success_NotFound) {
         /* ExecuteRegExp() previously returned an array or null. */
         rval.setNull();
         return true;
     }
@@ -592,18 +602,18 @@ const JSPropertySpec js::regexp_properti
 };
 
 const JSFunctionSpec js::regexp_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_SELF_HOSTED_FN(js_toSource_str, "RegExpToString", 0, 0),
 #endif
     JS_SELF_HOSTED_FN(js_toString_str, "RegExpToString", 0, 0),
     JS_FN("compile",        regexp_compile,     2,0),
-    JS_INLINABLE_FN("exec", regexp_exec,        1,0, RegExpExec),
-    JS_INLINABLE_FN("test", regexp_test,        1,0, RegExpTest),
+    JS_SELF_HOSTED_FN("exec", "RegExp_prototype_Exec", 1,0),
+    JS_SELF_HOSTED_FN("test", "RegExpTest" ,    1,0),
     JS_FS_END
 };
 
 #define STATIC_PAREN_GETTER_CODE(parenNum)                                      \
     if (!res->createParen(cx, parenNum, args.rval()))                           \
         return false;                                                           \
     if (args.rval().isUndefined())                                              \
         args.rval().setString(cx->runtime()->emptyString);                      \
@@ -762,33 +772,16 @@ js::CreateRegExpPrototype(JSContext* cx,
     proto->NativeObject::setPrivate(nullptr);
 
     RootedAtom source(cx, cx->names().empty);
     if (!RegExpObject::initFromAtom(cx, proto, source, RegExpFlag(0)))
         return nullptr;
     return proto;
 }
 
-static bool
-ReportLastIndexNonwritable(JSContext* cx)
-{
-    JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_READ_ONLY, "\"lastIndex\"");
-    return false;
-}
-
-static bool
-SetLastIndex(JSContext* cx, Handle<RegExpObject*> reobj, double lastIndex)
-{
-    if (!reobj->lookup(cx, cx->names().lastIndex)->writable())
-        return ReportLastIndexNonwritable(cx);
-
-    reobj->setLastIndex(lastIndex);
-    return true;
-}
-
 template <typename CharT>
 static bool
 IsTrailSurrogateWithLeadSurrogateImpl(JSContext* cx, HandleLinearString input, size_t index)
 {
     JS::AutoCheckCannotGC nogc;
     MOZ_ASSERT(index > 0 && index < input->length());
     const CharT* inputChars = input->chars<CharT>(nogc);
 
@@ -802,20 +795,21 @@ IsTrailSurrogateWithLeadSurrogate(JSCont
     if (index <= 0 || size_t(index) >= input->length())
         return false;
 
     return input->hasLatin1Chars()
            ? IsTrailSurrogateWithLeadSurrogateImpl<Latin1Char>(cx, input, index)
            : IsTrailSurrogateWithLeadSurrogateImpl<char16_t>(cx, input, index);
 }
 
-/* ES6 final draft 21.2.5.2.2. */
-RegExpRunStatus
-js::ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
-                  MatchPairs* matches, RegExpStaticsUpdate staticsUpdate)
+/* ES6 21.2.5.2.2 steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2. */
+static RegExpRunStatus
+ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
+              int32_t lastIndex, bool sticky,
+              MatchPairs* matches, size_t* endIndex, RegExpStaticsUpdate staticsUpdate)
 {
     /*
      * WARNING: Despite the presence of spec step comment numbers, this
      *          algorithm isn't consistent with any ES6 version, draft or
      *          otherwise.  YOU HAVE BEEN WARNED.
      */
 
     /* Steps 1-2 performed by the caller. */
@@ -833,224 +827,210 @@ js::ExecuteRegExp(JSContext* cx, HandleO
     } else {
         res = nullptr;
     }
 
     RootedLinearString input(cx, string->ensureLinear(cx));
     if (!input)
         return RegExpRunStatus_Error;
 
-    /* Step 3. */
-    size_t length = input->length();
-
-    /* Steps 4-5. */
-    RootedValue lastIndex(cx, reobj->getLastIndex());
-    int searchIndex;
-    if (lastIndex.isInt32()) {
-        /* Aggressively avoid doubles. */
-        searchIndex = lastIndex.toInt32();
-    } else {
-        double d;
-        if (!ToInteger(cx, lastIndex, &d))
-            return RegExpRunStatus_Error;
-
-        /* Inlined steps 6-10, 15.a with doubles to detect failure case. */
-        if (reobj->needUpdateLastIndex() && (d < 0 || d > length)) {
-            /* Steps 15.a.i-ii. */
-            if (!SetLastIndex(cx, reobj, 0))
-                return RegExpRunStatus_Error;
-
-            /* Step 15.a.iii. */
-            return RegExpRunStatus_Success_NotFound;
-        }
+    /* Handled by caller */
+    MOZ_ASSERT(lastIndex >= 0 && size_t(lastIndex) <= input->length());
 
-        searchIndex = int(d);
-    }
-
-    /*
-     * Steps 6-10.
-     *
-     * Also make sure that we have a MatchPairs for regexps which update their
-     * last index, as we won't compute the last index otherwise.
-     */
-    Maybe<ScopedMatchPairs> alternateMatches;
-    if (!reobj->needUpdateLastIndex()) {
-        searchIndex = 0;
-    } else if (!matches) {
-        alternateMatches.emplace(&cx->tempLifoAlloc());
-        matches = &alternateMatches.ref();
-    }
-
-    /* Step 15.a. */
-    if (searchIndex < 0 || size_t(searchIndex) > length) {
-        /* Steps 15.a.i-ii. */
-        if (!SetLastIndex(cx, reobj, 0))
-            return RegExpRunStatus_Error;
-
-        /* Step 15.a.iii. */
-        return RegExpRunStatus_Success_NotFound;
-    }
+    /* Steps 4-10 performed by the caller. */
 
     /* Steps 12-13. */
     if (reobj->unicode()) {
         /*
          * ES6 21.2.2.2 step 2.
          *   Let listIndex be the index into Input of the character that was
          *   obtained from element index of str.
          *
          * In the spec, pattern match is performed with decoded Unicode code
          * points, but our implementation performs it with UTF-16 encoded
-         * string.  In step 2, we should decrement searchIndex (index) if it
+         * string.  In step 2, we should decrement lastIndex (index) if it
          * points the trail surrogate that has corresponding lead surrogate.
          *
          *   var r = /\uD83D\uDC38/ug;
          *   r.lastIndex = 1;
          *   var str = "\uD83D\uDC38";
          *   var result = r.exec(str); // pattern match starts from index 0
          *   print(result.index);      // prints 0
          *
          * Note: this doesn't match the current spec text and result in
          * different values for `result.index` under certain conditions.
          * However, the spec will change to match our implementation's
          * behavior. See https://github.com/tc39/ecma262/issues/128.
          */
-        if (IsTrailSurrogateWithLeadSurrogate(cx, input, searchIndex))
-            searchIndex--;
+        if (IsTrailSurrogateWithLeadSurrogate(cx, input, lastIndex))
+            lastIndex--;
     }
 
-    /* Step 14-29. */
-    RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *re, input, searchIndex, matches);
+    /* Steps 3, 14-17, except 15.a.i-ii, 15.c.i.1-2. */
+    RegExpRunStatus status = ExecuteRegExpImpl(cx, res, *re, input, lastIndex, sticky, matches, endIndex);
     if (status == RegExpRunStatus_Error)
         return RegExpRunStatus_Error;
 
-    if (status == RegExpRunStatus_Success_NotFound) {
-        /* Steps 15.a.i-ii. */
-        if (!SetLastIndex(cx, reobj, 0))
-            return RegExpRunStatus_Error;
-    } else if (reobj->needUpdateLastIndex()) {
-        /* Steps 18.a-b. */
-        MOZ_ASSERT(matches && !matches->empty());
-        if (!SetLastIndex(cx, reobj, (*matches)[0].limit))
-            return RegExpRunStatus_Error;
-    }
+    /* Steps 15.a.i-ii, 18 are done by Self-hosted function. */
 
     return status;
 }
 
-/* ES5 15.10.6.2 (and 15.10.6.3, which calls 15.10.6.2). */
-static RegExpRunStatus
-ExecuteRegExp(JSContext* cx, const CallArgs& args, MatchPairs* matches)
-{
-    /* Step 1 (a) was performed by CallNonGenericMethod. */
-    RootedObject regexp(cx, &args.thisv().toObject());
-
-    /* Step 2. */
-    RootedString string(cx, ToString<CanGC>(cx, args.get(0)));
-    if (!string)
-        return RegExpRunStatus_Error;
-
-    return ExecuteRegExp(cx, regexp, string, matches, UpdateRegExpStatics);
-}
-
-/* ES5 15.10.6.2. */
+/* ES6 21.2.5.2.2 steps 3, 11-29, except 15.a.i-ii, 15.c.i.1-2, 18. */
 static bool
-regexp_exec_impl(JSContext* cx, HandleObject regexp, HandleString string,
-                 RegExpStaticsUpdate staticsUpdate, MutableHandleValue rval)
+RegExpMatcherImpl(JSContext* cx, HandleObject regexp, HandleString string,
+                  int32_t lastIndex, bool sticky,
+                  RegExpStaticsUpdate staticsUpdate, MutableHandleValue rval)
 {
     /* Execute regular expression and gather matches. */
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
 
-    RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, &matches, staticsUpdate);
+    /* Steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2. */
+    RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, lastIndex, sticky,
+                                           &matches, nullptr, staticsUpdate);
     if (status == RegExpRunStatus_Error)
         return false;
 
+    /* Steps 15.a, 15.c. */
     if (status == RegExpRunStatus_Success_NotFound) {
         rval.setNull();
         return true;
     }
 
+    /* Steps 19-29 */
     return CreateRegExpMatchResult(cx, string, matches, rval);
 }
 
-static bool
-regexp_exec_impl(JSContext* cx, const CallArgs& args)
+/* ES6 21.2.5.2.2 steps 3, 11-29, except 15.a.i-ii, 15.c.i.1-2, 18. */
+bool
+js::RegExpMatcher(JSContext* cx, unsigned argc, Value* vp)
 {
-    RootedObject regexp(cx, &args.thisv().toObject());
-    RootedString string(cx, ToString<CanGC>(cx, args.get(0)));
-    if (!string)
+    CallArgs args = CallArgsFromVp(argc, vp);
+    MOZ_ASSERT(args.length() == 4);
+    MOZ_ASSERT(IsRegExpObject(args[0]));
+    MOZ_ASSERT(args[1].isString());
+    MOZ_ASSERT(args[2].isNumber());
+    MOZ_ASSERT(args[3].isBoolean());
+
+    RootedObject regexp(cx, &args[0].toObject());
+    RootedString string(cx, args[1].toString());
+    RootedValue lastIndexVal(cx, args[2]);
+    bool sticky = ToBoolean(args[3]);
+
+    int32_t lastIndex = 0;
+    if (!ToInt32(cx, lastIndexVal, &lastIndex))
         return false;
 
-    return regexp_exec_impl(cx, regexp, string, UpdateRegExpStatics, args.rval());
-}
-
-bool
-js::regexp_exec(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    return CallNonGenericMethod(cx, IsRegExpObject, regexp_exec_impl, args);
+    /* Steps 3, 11-29, except 15.a.i-ii, 15.c.i.1-2, 18. */
+    return RegExpMatcherImpl(cx, regexp, string, lastIndex, sticky,
+                             UpdateRegExpStatics, args.rval());
 }
 
 /* Separate interface for use by IonMonkey. */
 bool
-js::regexp_exec_raw(JSContext* cx, HandleObject regexp, HandleString input,
-                    MatchPairs* maybeMatches, MutableHandleValue output)
+js::RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
+                     int32_t lastIndex, bool sticky,
+                     MatchPairs* maybeMatches, MutableHandleValue output)
 {
+    MOZ_ASSERT(lastIndex <= INT32_MAX);
+
     // The MatchPairs will always be passed in, but RegExp execution was
     // successful only if the pairs have actually been filled in.
     if (maybeMatches && maybeMatches->pairsRaw()[0] >= 0)
         return CreateRegExpMatchResult(cx, input, *maybeMatches, output);
-    return regexp_exec_impl(cx, regexp, input, UpdateRegExpStatics, output);
+    return RegExpMatcherImpl(cx, regexp, input, lastIndex, sticky,
+                             UpdateRegExpStatics, output);
 }
 
 bool
 js::regexp_exec_no_statics(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 2);
     MOZ_ASSERT(IsRegExpObject(args[0]));
     MOZ_ASSERT(args[1].isString());
 
     RootedObject regexp(cx, &args[0].toObject());
     RootedString string(cx, args[1].toString());
 
-    return regexp_exec_impl(cx, regexp, string, DontUpdateRegExpStatics, args.rval());
+    return RegExpMatcherImpl(cx, regexp, string, 0, false,
+                             DontUpdateRegExpStatics, args.rval());
 }
 
-/* ES5 15.10.6.3. */
-static bool
-regexp_test_impl(JSContext* cx, const CallArgs& args)
+/* ES6 21.2.5.2.2 steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2. */
+bool
+js::RegExpTester(JSContext* cx, unsigned argc, Value* vp)
 {
-    RegExpRunStatus status = ExecuteRegExp(cx, args, nullptr);
-    args.rval().setBoolean(status == RegExpRunStatus_Success);
-    return status != RegExpRunStatus_Error;
+    CallArgs args = CallArgsFromVp(argc, vp);
+    MOZ_ASSERT(args.length() == 4);
+    MOZ_ASSERT(IsRegExpObject(args[0]));
+    MOZ_ASSERT(args[1].isString());
+    MOZ_ASSERT(args[2].isNumber());
+    MOZ_ASSERT(args[3].isBoolean());
+
+    RootedObject regexp(cx, &args[0].toObject());
+    RootedString string(cx, args[1].toString());
+    RootedValue lastIndexVal(cx, args[2]);
+    bool sticky = ToBoolean(args[3]);
+
+    int32_t lastIndex = 0;
+    if (!ToInt32(cx, lastIndexVal, &lastIndex))
+        return false;
+
+    /* Steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2. */
+    size_t endIndex = 0;
+    RegExpRunStatus status = ExecuteRegExp(cx, regexp, string,
+                                           lastIndex, sticky,
+                                           nullptr, &endIndex, UpdateRegExpStatics);
+
+    if (status == RegExpRunStatus_Error)
+        return false;
+
+    if (status == RegExpRunStatus_Success) {
+        MOZ_ASSERT(endIndex <= INT32_MAX);
+        args.rval().setInt32(int32_t(endIndex));
+    } else {
+        args.rval().setInt32(-1);
+    }
+    return true;
 }
 
 /* Separate interface for use by IonMonkey. */
 bool
-js::regexp_test_raw(JSContext* cx, HandleObject regexp, HandleString input, bool* result)
+js::RegExpTesterRaw(JSContext* cx, HandleObject regexp, HandleString input,
+                    int32_t lastIndex, bool sticky, int32_t* endIndex)
 {
-    RegExpRunStatus status = ExecuteRegExp(cx, regexp, input, nullptr, UpdateRegExpStatics);
-    *result = (status == RegExpRunStatus_Success);
-    return status != RegExpRunStatus_Error;
-}
+    MOZ_ASSERT(lastIndex <= INT32_MAX);
+
+    size_t endIndexTmp = 0;
+    RegExpRunStatus status = ExecuteRegExp(cx, regexp, input, lastIndex, sticky,
+                                           nullptr, &endIndexTmp, UpdateRegExpStatics);
 
-bool
-js::regexp_test(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    return CallNonGenericMethod(cx, IsRegExpObject, regexp_test_impl, args);
+    if (status == RegExpRunStatus_Success) {
+        MOZ_ASSERT(endIndexTmp <= INT32_MAX);
+        *endIndex = int32_t(endIndexTmp);
+        return true;
+    }
+    if (status == RegExpRunStatus_Success_NotFound) {
+        *endIndex = -1;
+        return true;
+    }
+
+    return false;
 }
 
 bool
 js::regexp_test_no_statics(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 2);
     MOZ_ASSERT(IsRegExpObject(args[0]));
     MOZ_ASSERT(args[1].isString());
 
     RootedObject regexp(cx, &args[0].toObject());
     RootedString string(cx, args[1].toString());
 
-    RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, nullptr, DontUpdateRegExpStatics);
+    size_t ignored = 0;
+    RegExpRunStatus status = ExecuteRegExp(cx, regexp, string, 0, false,
+                                           nullptr, &ignored, DontUpdateRegExpStatics);
     args.rval().setBoolean(status == RegExpRunStatus_Success);
     return status != RegExpRunStatus_Error;
 }
--- a/js/src/builtin/RegExp.h
+++ b/js/src/builtin/RegExp.h
@@ -21,20 +21,16 @@ InitRegExpClass(JSContext* cx, HandleObj
 
 // Whether RegExp statics should be updated with the input and results of a
 // regular expression execution.
 enum RegExpStaticsUpdate { UpdateRegExpStatics, DontUpdateRegExpStatics };
 
 // Whether RegExp statics should be used to create a RegExp instance.
 enum RegExpStaticsUse { UseRegExpStatics, DontUseRegExpStatics };
 
-RegExpRunStatus
-ExecuteRegExp(JSContext* cx, HandleObject regexp, HandleString string,
-              MatchPairs* matches, RegExpStaticsUpdate staticsUpdate);
-
 /*
  * Legacy behavior of ExecuteRegExp(), which is baked into the JSAPI.
  *
  * |res| may be nullptr if the RegExpStatics are not to be updated.
  * |input| may be nullptr if there is no JSString corresponding to
  * |chars| and |length|.
  */
 bool
@@ -43,27 +39,29 @@ ExecuteRegExpLegacy(JSContext* cx, RegEx
                     MutableHandleValue rval);
 
 /* Translation from MatchPairs to a JS array in regexp_exec()'s output format. */
 bool
 CreateRegExpMatchResult(JSContext* cx, HandleString input, const MatchPairs& matches,
                         MutableHandleValue rval);
 
 extern bool
-regexp_exec_raw(JSContext* cx, HandleObject regexp, HandleString input, MatchPairs* maybeMatches,
-                MutableHandleValue output);
+RegExpMatcher(JSContext* cx, unsigned argc, Value* vp);
 
 extern bool
-regexp_exec(JSContext* cx, unsigned argc, Value* vp);
-
-bool
-regexp_test_raw(JSContext* cx, HandleObject regexp, HandleString input, bool* result);
+RegExpMatcherRaw(JSContext* cx, HandleObject regexp, HandleString input,
+                 int32_t lastIndex, bool sticky,
+                 MatchPairs* maybeMatches, MutableHandleValue output);
 
 extern bool
-regexp_test(JSContext* cx, unsigned argc, Value* vp);
+RegExpTester(JSContext* cx, unsigned argc, Value* vp);
+
+extern bool
+RegExpTesterRaw(JSContext* cx, HandleObject regexp, HandleString input,
+                int32_t lastIndex, bool sticky, int32_t* endIndex);
 
 /*
  * The following functions are for use by self-hosted code.
  */
 
 /*
  * Behaves like regexp.exec(string), but doesn't set RegExp statics.
  *
--- a/js/src/builtin/RegExp.js
+++ b/js/src/builtin/RegExp.js
@@ -49,9 +49,126 @@ function RegExpToString()
     var pattern = R.source;
 
     // Steps 5-6.
     var flags = R.flags;
 
     // Step 7.
     return '/' + pattern + '/' + flags;
 }
+
+// ES6 21.2.5.2.
+// NOTE: This is not RegExpExec (21.2.5.2.1).
+function RegExp_prototype_Exec(string) {
+    // Steps 1-3.
+    var R = this;
+    if (!IsObject(R) || !IsRegExpObject(R))
+        return callFunction(CallRegExpMethodIfWrapped, R, string, "RegExp_prototype_Exec");
+
+    // Steps 4-5.
+    var S = ToString(string);
+
+    // Step 6.
+    return RegExpBuiltinExec(R, S, false);
+}
+
+// ES6 21.2.5.2.1.
+function RegExpExec(R, S, forTest) {
+    // Steps 1-2 (skipped).
+
+    // Steps 3-4.
+    var exec = R.exec;
+
+    // Step 5.
+    // If exec is the original RegExp.prototype.exec, use the same, faster,
+    // path as for the case where exec isn't callable.
+    if (exec === RegExp_prototype_Exec || !IsCallable(exec)) {
+        // ES6 21.2.5.2 steps 1-2, 4-5 (skipped) for optimized case.
+
+        // Steps 6-7 or ES6 21.2.5.2 steps 3, 6 for optimized case.
+        return RegExpBuiltinExec(R, S, forTest);
+    }
+
+    // Steps 5.a-b.
+    var result = callContentFunction(exec, R, S);
+
+    // Step 5.c.
+    if (typeof result !== "object")
+        ThrowTypeError(JSMSG_EXEC_NOT_OBJORNULL);
+
+    // Step 5.d.
+    return forTest ? result !== null : result;
+}
+
+// ES6 21.2.5.2.2.
+function RegExpBuiltinExec(R, S, forTest) {
+    // ES6 21.2.5.2.1 step 6.
+    // This check is here for RegExpTest.  RegExp_prototype_Exec does same
+    // thing already.
+    if (!IsRegExpObject(R))
+        return callFunction(CallRegExpMethodIfWrapped, R, R, S, forTest, "RegExpBuiltinExec");
+
+    // Step 1-2 (skipped).
+
+    // Steps 4-5.
+    var lastIndex = ToLength(R.lastIndex);
+
+    // Steps 6-7.
+    var global = !!R.global;
+
+    // Steps 8-9.
+    var sticky = !!R.sticky;
+
+    // Step 10.
+    if (!global && !sticky) {
+        lastIndex = 0;
+    } else {
+        if (lastIndex < 0 || lastIndex > S.length) {
+            // Steps 15.a.i-ii, 15.c.i.1-2.
+            R.lastIndex = 0;
+            return forTest ? false : null;
+        }
+    }
+
+    if (forTest) {
+        // Steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2.
+        var endIndex = RegExpTester(R, S, lastIndex, sticky);
+        if (endIndex == -1) {
+            // Steps 15.a.i-ii, 15.c.i.1-2.
+            R.lastIndex = 0;
+            return false;
+        }
+
+        // Step 18.
+        if (global || sticky)
+            R.lastIndex = endIndex;
+
+        return true;
+    }
+
+    // Steps 3, 11-17, except 15.a.i-ii, 15.c.i.1-2.
+    var result = RegExpMatcher(R, S, lastIndex, sticky);
+    if (result === null) {
+        // Steps 15.a.i-ii, 15.c.i.1-2.
+        R.lastIndex = 0;
+    } else {
+        // Step 18.
+        if (global || sticky)
+            R.lastIndex = result.index + result[0].length;
+    }
+
+    return result;
+}
+
+// ES6 21.2.5.13.
+function RegExpTest(string) {
+    // Steps 1-2.
+    var R = this;
+    if (!IsObject(R))
+        ThrowTypeError(JSMSG_NOT_NONNULL_OBJECT, R === null ? "null" : typeof R);
+
+    // Steps 3-4.
+    var S = ToString(string);
+
+    // Steps 5-6.
+    return RegExpExec(R, S, true);
+}
 _SetCanonicalName(RegExpToString, "toString");
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -4,16 +4,18 @@
  * 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 "builtin/TestingFunctions.h"
 
 #include "mozilla/Move.h"
 #include "mozilla/UniquePtr.h"
 
+#include <cmath>
+
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsfriendapi.h"
 #include "jsgc.h"
 #include "jsobj.h"
 #include "jsprf.h"
 #include "jswrapper.h"
 
@@ -406,20 +408,26 @@ GCParameter(JSContext* cx, unsigned argc
         return false;
     }
 
     if (disableOOMFunctions && (param == JSGC_MAX_BYTES || param == JSGC_MAX_MALLOC_BYTES)) {
         args.rval().setUndefined();
         return true;
     }
 
-    uint32_t value;
-    if (!ToUint32(cx, args[1], &value))
+    double d;
+    if (!ToNumber(cx, args[1], &d))
         return false;
 
+    if (d < 0 || d > UINT32_MAX) {
+        JS_ReportError(cx, "Parameter value out of range");
+        return false;
+    }
+
+    uint32_t value = floor(d);
     if (!info.allowZero && value == 0) {
         JS_ReportError(cx, "the second argument must be convertable to uint32_t "
                            "with non-zero value");
         return false;
     }
 
     if (param == JSGC_MARK_STACK_LIMIT && JS::IsIncrementalGCInProgress(cx->runtime())) {
         JS_ReportError(cx, "attempt to set markStackLimit while a GC is in progress");
--- a/js/src/devtools/automation/cgc-jittest-timeouts.txt
+++ b/js/src/devtools/automation/cgc-jittest-timeouts.txt
@@ -16,10 +16,11 @@ gc/bug-1014972.js
 gc/bug-906236.js
 gc/bug-906241.js
 ion/bug787921.js
 parallel/alloc-many-objs.js
 parallel/alloc-too-many-objs.js
 self-test/assertDeepEq.js
 v8-v5/check-earley-boyer.js
 v8-v5/check-raytrace.js
+v8-v5/check-regexp.js
 v8-v5/check-splay.js
 SIMD/nursery-overflow.js
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1587,17 +1587,17 @@ BytecodeEmitter::tryConvertFreeName(Pars
                 RootedScript moduleScript(cx, ssi.moduleScript());
                 uint32_t slot_;
                 if (lookupAliasedName(moduleScript, name, &slot_, pn)) {
                     slot = Some(slot_);
                     break;
                 }
 
                 // Convert module import accesses to use JSOP_GETIMPORT.
-                RootedModuleEnvironmentObject env(cx, ssi.module().environment());
+                RootedModuleEnvironmentObject env(cx, &ssi.module().initialEnvironment());
                 RootedPropertyName propName(cx, name);
                 MOZ_ASSERT(env);
                 if (env->hasImportBinding(propName)) {
                     if (pn->getOp() == JSOP_GETNAME) {
                         pn->setOp(JSOP_GETIMPORT);
                         return true;
                     }
                     return false;
--- a/js/src/irregexp/NativeRegExpMacroAssembler.cpp
+++ b/js/src/irregexp/NativeRegExpMacroAssembler.cpp
@@ -199,16 +199,20 @@ NativeRegExpMacroAssembler::GenerateCode
 #ifdef DEBUG
         // Bounds check numOutputRegisters.
         Label enoughRegisters;
         masm.branchPtr(Assembler::GreaterThanOrEqual,
                        temp1, ImmWord(num_saved_registers_), &enoughRegisters);
         masm.assumeUnreachable("Not enough output registers for RegExp");
         masm.bind(&enoughRegisters);
 #endif
+    } else {
+        Register endIndexRegister = input_end_pointer;
+        masm.loadPtr(Address(temp0, offsetof(InputOutputData, endIndex)), endIndexRegister);
+        masm.storePtr(endIndexRegister, Address(masm.getStackPointer(), offsetof(FrameData, endIndex)));
     }
 
     // Load string end pointer.
     masm.loadPtr(Address(temp0, offsetof(InputOutputData, inputEnd)), input_end_pointer);
 
     // Load input start pointer, and copy to FrameData.
     masm.loadPtr(Address(temp0, offsetof(InputOutputData, inputStart)), current_position);
     masm.storePtr(current_position, Address(masm.getStackPointer(), offsetof(FrameData, inputStart)));
@@ -349,16 +353,40 @@ NativeRegExpMacroAssembler::GenerateCode
                                    &exit_label_);
 
                 // Advance current position after a zero-length match.
                 masm.addPtr(Imm32(char_size()), current_position);
             }
 
             masm.jump(&load_char_start_regexp);
         } else {
+            if (match_only) {
+                // Store endIndex.
+
+                Register endIndexRegister = temp1;
+                Register inputByteLength = backtrack_stack_pointer;
+
+                masm.loadPtr(Address(masm.getStackPointer(), offsetof(FrameData, endIndex)), endIndexRegister);
+
+                masm.loadPtr(inputOutputAddress, temp0);
+                masm.loadPtr(Address(temp0, offsetof(InputOutputData, inputEnd)), inputByteLength);
+                masm.subPtr(Address(temp0, offsetof(InputOutputData, inputStart)), inputByteLength);
+
+                masm.loadPtr(register_location(1), temp0);
+
+                // Convert to index from start of string, not end.
+                masm.addPtr(inputByteLength, temp0);
+
+                // Convert byte index to character index.
+                if (mode_ == CHAR16)
+                    masm.rshiftPtrArithmetic(Imm32(1), temp0);
+
+                masm.store32(temp0, Address(endIndexRegister, 0));
+            }
+
             masm.movePtr(ImmWord(RegExpRunStatus_Success), temp0);
         }
     }
 
     masm.bind(&exit_label_);
 
     if (global()) {
         // Return the number of successful captures.
--- a/js/src/irregexp/NativeRegExpMacroAssembler.h
+++ b/js/src/irregexp/NativeRegExpMacroAssembler.h
@@ -38,39 +38,42 @@ namespace irregexp {
 
 struct InputOutputData
 {
     const void* inputStart;
     const void* inputEnd;
 
     // Index into inputStart (in chars) at which to begin matching.
     size_t startIndex;
+    size_t* endIndex;
 
     MatchPairs* matches;
 
     // RegExpMacroAssembler::Result for non-global regexps, number of captures
     // for global regexps.
     int32_t result;
 
     template <typename CharT>
     InputOutputData(const CharT* inputStart, const CharT* inputEnd,
-                    size_t startIndex, MatchPairs* matches)
+                    size_t startIndex, MatchPairs* matches, size_t* endIndex)
       : inputStart(inputStart),
         inputEnd(inputEnd),
         startIndex(startIndex),
+        endIndex(endIndex),
         matches(matches),
         result(0)
     {}
 };
 
 struct FrameData
 {
     // Copy of the input/output data's data.
     char16_t* inputStart;
     size_t startIndex;
+    size_t* endIndex;
 
     // Pointer to the character before the input start.
     char16_t* inputStartMinusOne;
 
     // Copy of the input MatchPairs registers, may be modified by JIT code.
     int32_t* outputRegisters;
     int32_t numOutputRegisters;
 
--- a/js/src/irregexp/RegExpEngine.cpp
+++ b/js/src/irregexp/RegExpEngine.cpp
@@ -1881,39 +1881,39 @@ irregexp::CompilePattern(JSContext* cx, 
     }
 
     return compiler.Assemble(cx, assembler, node, data->capture_count);
 }
 
 template <typename CharT>
 RegExpRunStatus
 irregexp::ExecuteCode(JSContext* cx, jit::JitCode* codeBlock, const CharT* chars, size_t start,
-                      size_t length, MatchPairs* matches)
+                      size_t length, MatchPairs* matches, size_t* endIndex)
 {
     typedef void (*RegExpCodeSignature)(InputOutputData*);
 
-    InputOutputData data(chars, chars + length, start, matches);
+    InputOutputData data(chars, chars + length, start, matches, endIndex);
 
     RegExpCodeSignature function = reinterpret_cast<RegExpCodeSignature>(codeBlock->raw());
 
     {
         JS::AutoSuppressGCAnalysis nogc;
         CALL_GENERATED_1(function, &data);
     }
 
     return (RegExpRunStatus) data.result;
 }
 
 template RegExpRunStatus
 irregexp::ExecuteCode(JSContext* cx, jit::JitCode* codeBlock, const Latin1Char* chars, size_t start,
-                      size_t length, MatchPairs* matches);
+                      size_t length, MatchPairs* matches, size_t* endIndex);
 
 template RegExpRunStatus
 irregexp::ExecuteCode(JSContext* cx, jit::JitCode* codeBlock, const char16_t* chars, size_t start,
-                      size_t length, MatchPairs* matches);
+                      size_t length, MatchPairs* matches, size_t* endIndex);
 
 // -------------------------------------------------------------------
 // Tree to graph conversion
 
 RegExpNode*
 RegExpAtom::ToNode(RegExpCompiler* compiler, RegExpNode* on_success)
 {
     TextElementVector* elms =
--- a/js/src/irregexp/RegExpEngine.h
+++ b/js/src/irregexp/RegExpEngine.h
@@ -91,22 +91,22 @@ CompilePattern(JSContext* cx, RegExpShar
                bool is_ascii, bool match_only, bool force_bytecode, bool sticky,
                bool unicode);
 
 // Note: this may return RegExpRunStatus_Error if an interrupt was requested
 // while the code was executing.
 template <typename CharT>
 RegExpRunStatus
 ExecuteCode(JSContext* cx, jit::JitCode* codeBlock, const CharT* chars, size_t start,
-            size_t length, MatchPairs* matches);
+            size_t length, MatchPairs* matches, size_t* endIndex);
 
 template <typename CharT>
 RegExpRunStatus
 InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* chars, size_t start,
-              size_t length, MatchPairs* matches);
+              size_t length, MatchPairs* matches, size_t* endIndex);
 
 #define FOR_EACH_NODE_TYPE(VISIT)                                    \
   VISIT(End)                                                         \
   VISIT(Action)                                                      \
   VISIT(Choice)                                                      \
   VISIT(BackReference)                                               \
   VISIT(Assertion)                                                   \
   VISIT(Text)
--- a/js/src/irregexp/RegExpInterpreter.cpp
+++ b/js/src/irregexp/RegExpInterpreter.cpp
@@ -112,17 +112,17 @@ Load16Aligned(const uint8_t* pc)
     return *reinterpret_cast<const uint16_t*>(pc);
 }
 
 #define BYTECODE(name)  case BC_##name:
 
 template <typename CharT>
 RegExpRunStatus
 irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const CharT* chars, size_t current,
-                        size_t length, MatchPairs* matches)
+                        size_t length, MatchPairs* matches, size_t* endIndex)
 {
     const uint8_t* pc = byteCode;
 
     uint32_t current_char = current ? chars[current - 1] : '\n';
 
     RegExpStackCursor stack(cx);
 
     if (!stack.init())
@@ -194,16 +194,18 @@ irregexp::InterpretCode(JSContext* cx, c
             registers[insn >> BYTECODE_SHIFT] = stack.pop();
             pc += BC_POP_REGISTER_LENGTH;
             break;
           BYTECODE(FAIL)
             return RegExpRunStatus_Success_NotFound;
           BYTECODE(SUCCEED)
             if (matches)
                 memcpy(matches->pairsRaw(), registers.begin(), matches->length() * 2 * sizeof(int32_t));
+            else if (endIndex)
+                *endIndex = registers[1];
             return RegExpRunStatus_Success;
           BYTECODE(ADVANCE_CP)
             current += insn >> BYTECODE_SHIFT;
             pc += BC_ADVANCE_CP_LENGTH;
             break;
           BYTECODE(GOTO)
             pc = byteCode + Load32Aligned(pc + 4);
             break;
@@ -487,13 +489,13 @@ irregexp::InterpretCode(JSContext* cx, c
           default:
             MOZ_CRASH("Bad bytecode");
         }
     }
 }
 
 template RegExpRunStatus
 irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const Latin1Char* chars, size_t current,
-                        size_t length, MatchPairs* matches);
+                        size_t length, MatchPairs* matches, size_t* endIndex);
 
 template RegExpRunStatus
 irregexp::InterpretCode(JSContext* cx, const uint8_t* byteCode, const char16_t* chars, size_t current,
-                        size_t length, MatchPairs* matches);
+                        size_t length, MatchPairs* matches, size_t* endIndex);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/regexpLastIndexReset.js
@@ -0,0 +1,26 @@
+// Bug 1207922 - lastIndex should be reset to 0 when match fails.
+
+var pattern = /abc/;
+var string = 'aaaaaaaa';
+
+function test() {
+  pattern.lastIndex = 3;
+  var result = pattern.exec(string);
+  assertEq(result, null);
+  assertEq(pattern.lastIndex, 0);
+}
+
+for (let i = 0; i < 10; i++) {
+  test();
+}
+
+function test2() {
+  pattern.lastIndex = 3;
+  var result = pattern.test(string);
+  assertEq(result, false);
+  assertEq(pattern.lastIndex, 0);
+}
+
+for (let i = 0; i < 10; i++) {
+  test2();
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1237153.js
@@ -0,0 +1,2 @@
+// |jit-test| error: Error
+gcparam("sliceTimeBudget", -1);
--- a/js/src/jit-test/tests/gc/oomInRegExp.js
+++ b/js/src/jit-test/tests/gc/oomInRegExp.js
@@ -1,5 +1,6 @@
 if (!('oomTest' in this))
     quit();
 
 oomTest(() => assertEq("foobar\xff5baz\u1200".search(/bar\u0178\d/i), 3));
 oomTest(() => assertEq((/(?!(?!(?!6)[\Wc]))/i).test(), false));
+oomTest(() => assertEq((/bar\u0178\d/i).exec("foobar\xff5baz\u1200") != null, true));
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/modules/bug-1233179.js
@@ -0,0 +1,6 @@
+let c = parseModule(`
+  function a(x) { return x; }
+  function b(x) { return i<40; }
+  function d(x) { return x + 3; }
+`);
+getLcovInfo();
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -32,16 +32,17 @@
 #include "jit/Lowering.h"
 #include "jit/MIRGenerator.h"
 #include "jit/MoveEmitter.h"
 #include "jit/RangeAnalysis.h"
 #include "jit/SharedICHelpers.h"
 #include "vm/MatchPairs.h"
 #include "vm/RegExpStatics.h"
 #include "vm/TraceLogging.h"
+#include "vm/Unicode.h"
 
 #include "jsboolinlines.h"
 
 #include "jit/MacroAssembler-inl.h"
 #include "jit/shared/CodeGenerator-shared-inl.h"
 #include "jit/shared/Lowering-shared-inl.h"
 #include "vm/Interpreter-inl.h"
 
@@ -1029,32 +1030,35 @@ RegExpPairCountAddress(MacroAssembler& m
 }
 
 // Prepare an InputOutputData and optional MatchPairs which space has been
 // allocated for on the stack, and try to execute a RegExp on a string input.
 // If the RegExp was successfully executed and matched the input, fallthrough,
 // otherwise jump to notFound or failure.
 static bool
 PrepareAndExecuteRegExp(JSContext* cx, MacroAssembler& masm, Register regexp, Register input,
+                        Register lastIndex, Register sticky,
                         Register temp1, Register temp2, Register temp3,
                         size_t inputOutputDataStartOffset,
                         RegExpShared::CompilationMode mode,
                         Label* notFound, Label* failure)
 {
     size_t matchPairsStartOffset = inputOutputDataStartOffset + sizeof(irregexp::InputOutputData);
     size_t pairsVectorStartOffset = RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
 
     Address inputStartAddress(masm.getStackPointer(),
         inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, inputStart));
     Address inputEndAddress(masm.getStackPointer(),
         inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, inputEnd));
     Address matchesPointerAddress(masm.getStackPointer(),
         inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, matches));
     Address startIndexAddress(masm.getStackPointer(),
         inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, startIndex));
+    Address endIndexAddress(masm.getStackPointer(),
+        inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, endIndex));
     Address matchResultAddress(masm.getStackPointer(),
         inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, result));
 
     Address pairCountAddress = RegExpPairCountAddress(masm, inputOutputDataStartOffset);
     Address pairsPointerAddress(masm.getStackPointer(),
         matchPairsStartOffset + MatchPairs::offsetOfPairs());
 
     Address pairsVectorAddress(masm.getStackPointer(), pairsVectorStartOffset);
@@ -1079,19 +1083,55 @@ PrepareAndExecuteRegExp(JSContext* cx, M
 
     // Check for a linear input string.
     masm.branchIfRope(input, failure);
 
     // Get the RegExpShared for the RegExp.
     masm.loadPtr(Address(regexp, NativeObject::getFixedSlotOffset(RegExpObject::PRIVATE_SLOT)), temp1);
     masm.branchPtr(Assembler::Equal, temp1, ImmWord(0), failure);
 
-    // Don't handle RegExps which read and write to lastIndex.
-    masm.branchTest32(Assembler::NonZero, Address(temp1, RegExpShared::offsetOfFlags()),
-                      Imm32(StickyFlag | GlobalFlag), failure);
+    // ES6 21.2.2.2 step 2.
+    // See RegExp.cpp ExecuteRegExp for more detail.
+    {
+        Label done;
+
+        masm.branchTest32(Assembler::Zero, Address(temp1, RegExpShared::offsetOfFlags()),
+                          Imm32(UnicodeFlag), &done);
+
+        // If input is latin1, there should not be surrogate pair.
+        masm.branchLatin1String(input, &done);
+
+        // Check if |lastIndex > 0 && lastIndex < input->length()|.
+        // lastIndex should already have no sign here.
+        masm.branchTest32(Assembler::Zero, lastIndex, lastIndex, &done);
+        masm.loadStringLength(input, temp2);
+        masm.branch32(Assembler::AboveOrEqual, lastIndex, temp2, &done);
+
+        // Check if input[lastIndex] is trail surrogate.
+        masm.loadStringChars(input, temp2);
+        masm.computeEffectiveAddress(BaseIndex(temp2, lastIndex, TimesTwo), temp3);
+        masm.load16ZeroExtend(Address(temp3, 0), temp3);
+
+        masm.branch32(Assembler::Below, temp3, Imm32(unicode::TrailSurrogateMin), &done);
+        masm.branch32(Assembler::Above, temp3, Imm32(unicode::TrailSurrogateMax), &done);
+
+        // Check if input[lastIndex-1] is lead surrogate.
+        masm.move32(lastIndex, temp3);
+        masm.sub32(Imm32(1), temp3);
+        masm.computeEffectiveAddress(BaseIndex(temp2, temp3, TimesTwo), temp3);
+        masm.load16ZeroExtend(Address(temp3, 0), temp3);
+
+        masm.branch32(Assembler::Below, temp3, Imm32(unicode::LeadSurrogateMin), &done);
+        masm.branch32(Assembler::Above, temp3, Imm32(unicode::LeadSurrogateMax), &done);
+
+        // Move lastIndex to lead surrogate.
+        masm.subPtr(Imm32(2), lastIndex);
+
+        masm.bind(&done);
+    }
 
     if (mode == RegExpShared::Normal) {
         // Don't handle RegExps with excessive parens.
         masm.load32(Address(temp1, RegExpShared::offsetOfParenCount()), temp2);
         masm.branch32(Assembler::AboveOrEqual, temp2, Imm32(RegExpObject::MaxPairCount), failure);
 
         // Fill in the paren count in the MatchPairs on the stack.
         masm.add32(Imm32(1), temp2);
@@ -1100,47 +1140,79 @@ PrepareAndExecuteRegExp(JSContext* cx, M
 
     // Load the code pointer for the type of input string we have, and compute
     // the input start/end pointers in the InputOutputData.
     Register codePointer = temp1;
     {
         masm.loadStringChars(input, temp2);
         masm.storePtr(temp2, inputStartAddress);
         masm.loadStringLength(input, temp3);
-        Label isLatin1, done;
-        masm.branchTest32(Assembler::NonZero, Address(input, JSString::offsetOfFlags()),
-                          Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
+
+        Label stickyCode, done;
+        masm.branchTest32(Assembler::NonZero, sticky, sticky, &stickyCode);
         {
-            masm.lshiftPtr(Imm32(1), temp3);
-            masm.loadPtr(Address(temp1, RegExpShared::offsetOfJitCode(mode, false)), codePointer);
+            Label isLatin1;
+            masm.branchLatin1String(input, &isLatin1);
+            {
+                masm.lshiftPtr(Imm32(1), temp3);
+                masm.loadPtr(Address(temp1, RegExpShared::offsetOfNotStickyTwoByteJitCode(mode)),
+                             codePointer);
+            }
+            masm.jump(&done);
+            {
+                masm.bind(&isLatin1);
+                masm.loadPtr(Address(temp1, RegExpShared::offsetOfNotStickyLatin1JitCode(mode)),
+                             codePointer);
+            }
         }
         masm.jump(&done);
         {
-            masm.bind(&isLatin1);
-            masm.loadPtr(Address(temp1, RegExpShared::offsetOfJitCode(mode, true)), codePointer);
+            masm.bind(&stickyCode);
+            Label isLatin1;
+            masm.branchLatin1String(input, &isLatin1);
+            {
+                masm.lshiftPtr(Imm32(1), temp3);
+                masm.loadPtr(Address(temp1, RegExpShared::offsetOfStickyTwoByteJitCode(mode)),
+                             codePointer);
+            }
+            masm.jump(&done);
+            {
+                masm.bind(&isLatin1);
+                masm.loadPtr(Address(temp1, RegExpShared::offsetOfStickyLatin1JitCode(mode)),
+                             codePointer);
+            }
         }
         masm.bind(&done);
+
         masm.addPtr(temp3, temp2);
         masm.storePtr(temp2, inputEndAddress);
     }
 
     // Check the RegExpShared has been compiled for this type of input.
     masm.branchPtr(Assembler::Equal, codePointer, ImmWord(0), failure);
     masm.loadPtr(Address(codePointer, JitCode::offsetOfCode()), codePointer);
 
     // Finish filling in the InputOutputData instance on the stack.
     if (mode == RegExpShared::Normal) {
         masm.computeEffectiveAddress(Address(masm.getStackPointer(), matchPairsStartOffset), temp2);
         masm.storePtr(temp2, matchesPointerAddress);
-    }
-    masm.storePtr(ImmWord(0), startIndexAddress);
+    } else {
+        // Use InputOutputData.endIndex itself for output.
+        masm.computeEffectiveAddress(endIndexAddress, temp2);
+        masm.storePtr(temp2, endIndexAddress);
+    }
+    masm.storePtr(lastIndex, startIndexAddress);
     masm.store32(Imm32(0), matchResultAddress);
 
     // Save any volatile inputs.
     LiveGeneralRegisterSet volatileRegs;
+    if (sticky.volatile_())
+        volatileRegs.add(sticky);
+    if (lastIndex.volatile_())
+        volatileRegs.add(lastIndex);
     if (input.volatile_())
         volatileRegs.add(input);
     if (regexp.volatile_())
         volatileRegs.add(regexp);
 
     // Execute the RegExp.
     masm.computeEffectiveAddress(Address(masm.getStackPointer(), inputOutputDataStartOffset), temp2);
     masm.PushRegsInMask(volatileRegs);
@@ -1156,32 +1228,40 @@ PrepareAndExecuteRegExp(JSContext* cx, M
                   Imm32(RegExpRunStatus_Error), failure);
 
     // Lazily update the RegExpStatics.
     masm.movePtr(ImmPtr(res), temp1);
 
     Address pendingInputAddress(temp1, RegExpStatics::offsetOfPendingInput());
     Address matchesInputAddress(temp1, RegExpStatics::offsetOfMatchesInput());
     Address lazySourceAddress(temp1, RegExpStatics::offsetOfLazySource());
+    Address lazyIndexAddress(temp1, RegExpStatics::offsetOfLazyIndex());
+    Address lazyStickyAddress(temp1, RegExpStatics::offsetOfLazySticky());
 
     masm.patchableCallPreBarrier(pendingInputAddress, MIRType_String);
     masm.patchableCallPreBarrier(matchesInputAddress, MIRType_String);
     masm.patchableCallPreBarrier(lazySourceAddress, MIRType_String);
 
     masm.storePtr(input, pendingInputAddress);
     masm.storePtr(input, matchesInputAddress);
-    masm.storePtr(ImmWord(0), Address(temp1, RegExpStatics::offsetOfLazyIndex()));
+    masm.storePtr(sticky, lazyStickyAddress);
+    masm.storePtr(lastIndex, Address(temp1, RegExpStatics::offsetOfLazyIndex()));
     masm.store32(Imm32(1), Address(temp1, RegExpStatics::offsetOfPendingLazyEvaluation()));
 
     masm.loadPtr(Address(regexp, NativeObject::getFixedSlotOffset(RegExpObject::PRIVATE_SLOT)), temp2);
     masm.loadPtr(Address(temp2, RegExpShared::offsetOfSource()), temp3);
     masm.storePtr(temp3, lazySourceAddress);
     masm.load32(Address(temp2, RegExpShared::offsetOfFlags()), temp3);
     masm.store32(temp3, Address(temp1, RegExpStatics::offsetOfLazyFlags()));
 
+    if (mode == RegExpShared::MatchOnly) {
+        // endIndex is passed via temp3.
+        masm.load32(endIndexAddress, temp3);
+    }
+
     return true;
 }
 
 static void
 CopyStringChars(MacroAssembler& masm, Register to, Register from, Register len,
                 Register byteOpScratch, size_t fromWidth, size_t toWidth);
 
 static void
@@ -1298,348 +1378,454 @@ CreateDependentString(MacroAssembler& ma
         masm.storePtr(temp1, Address(string, JSDependentString::offsetOfBase()));
         masm.bind(&noBase);
     }
 
     masm.bind(&done);
 }
 
 JitCode*
-JitCompartment::generateRegExpExecStub(JSContext* cx)
-{
-    Register regexp = CallTempReg0;
-    Register input = CallTempReg1;
+JitCompartment::generateRegExpMatcherStub(JSContext* cx)
+{
+    Register regexp = RegExpMatcherRegExpReg;
+    Register input = RegExpMatcherStringReg;
+    Register lastIndex = RegExpMatcherLastIndexReg;
+    Register sticky = RegExpMatcherStickyReg;
     ValueOperand result = JSReturnOperand;
 
-    // We are free to clobber all registers, as LRegExpExec is a call instruction.
+    // We are free to clobber all registers, as LRegExpMatcher is a call instruction.
     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
     regs.take(input);
     regs.take(regexp);
+    regs.take(lastIndex);
+    regs.take(sticky);
 
     // temp5 is used in single byte instructions when creating dependent
     // strings, and has restrictions on which register it can be on some
     // platforms.
     Register temp5;
     {
         AllocatableGeneralRegisterSet oregs = regs;
         do {
             temp5 = oregs.takeAny();
         } while (!MacroAssembler::canUseInSingleByteInstruction(temp5));
         regs.take(temp5);
     }
 
     Register temp1 = regs.takeAny();
     Register temp2 = regs.takeAny();
-    Register temp3 = regs.takeAny();
-    Register temp4 = regs.takeAny();
+
+    Register maybeTemp3 = InvalidReg;
+    Register maybeTemp4 = InvalidReg;
+    if (!regs.empty()) {
+        // There are not enough registers on x86.
+        maybeTemp3 = regs.takeAny();
+        maybeTemp4 = regs.takeAny();
+    }
 
     ArrayObject* templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
     if (!templateObject)
         return nullptr;
 
     // The template object should have enough space for the maximum number of
     // pairs this stub can handle.
     MOZ_ASSERT(ObjectElements::VALUES_PER_HEADER + RegExpObject::MaxPairCount ==
                gc::GetGCKindSlots(templateObject->asTenured().getAllocKind()));
 
     MacroAssembler masm(cx);
 
     // The InputOutputData is placed above the return address on the stack.
     size_t inputOutputDataStartOffset = sizeof(void*);
 
     Label notFound, oolEntry;
-    if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, temp1, temp2, temp3,
-                                 inputOutputDataStartOffset, RegExpShared::Normal,
-                                 &notFound, &oolEntry))
+    if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, lastIndex, sticky,
+                                 temp1, temp2, temp5, inputOutputDataStartOffset,
+                                 RegExpShared::Normal, &notFound, &oolEntry))
     {
         return nullptr;
     }
 
     // Construct the result.
     Register object = temp1;
     masm.createGCObject(object, temp2, templateObject, gc::DefaultHeap, &oolEntry);
 
+    size_t elementsOffset = NativeObject::offsetOfFixedElements();
+
+#ifdef DEBUG
+    // Assert the initial value of initializedLength and length to make sure
+    // restoration on failure case works.
+    {
+        Label initLengthOK, lengthOK;
+        masm.branch32(Assembler::Equal,
+                      Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()),
+                      Imm32(templateObject->getDenseInitializedLength()),
+                      &initLengthOK);
+        masm.assumeUnreachable("Initial value of the match object's initializedLength does not match to restoration.");
+        masm.bind(&initLengthOK);
+
+        masm.branch32(Assembler::Equal,
+                      Address(object, elementsOffset + ObjectElements::offsetOfLength()),
+                      Imm32(templateObject->length()),
+                      &lengthOK);
+        masm.assumeUnreachable("Initial value of The match object's length does not match to restoration.");
+        masm.bind(&lengthOK);
+    }
+#endif
+
     Register matchIndex = temp2;
     masm.move32(Imm32(0), matchIndex);
 
     size_t pairsVectorStartOffset = RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
     Address pairsVectorAddress(masm.getStackPointer(), pairsVectorStartOffset);
     Address pairCountAddress = RegExpPairCountAddress(masm, inputOutputDataStartOffset);
 
-    size_t elementsOffset = NativeObject::offsetOfFixedElements();
     BaseIndex stringAddress(object, matchIndex, TimesEight, elementsOffset);
 
     JS_STATIC_ASSERT(sizeof(MatchPair) == 8);
     BaseIndex stringIndexAddress(masm.getStackPointer(), matchIndex, TimesEight,
                                  pairsVectorStartOffset + offsetof(MatchPair, start));
     BaseIndex stringLimitAddress(masm.getStackPointer(), matchIndex, TimesEight,
                                  pairsVectorStartOffset + offsetof(MatchPair, limit));
 
     // Loop to construct the match strings. There are two different loops,
     // depending on whether the input is latin1.
     {
         Label isLatin1, done;
-        masm.branchTest32(Assembler::NonZero, Address(input, JSString::offsetOfFlags()),
-                          Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
+        masm.branchLatin1String(input, &isLatin1);
+
+        Label* failure = &oolEntry;
+        Register temp3 = (maybeTemp3 == InvalidReg) ? sticky : maybeTemp3;
+        Register temp4 = (maybeTemp3 == InvalidReg) ? lastIndex : maybeTemp4;
+
+        Label failureRestore;
+        if (maybeTemp3 == InvalidReg) {
+            failure = &failureRestore;
+
+            // Save sticky and lastIndex values to temporary space.
+            masm.store32(sticky, Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()));
+            masm.store32(lastIndex, Address(object, elementsOffset + ObjectElements::offsetOfLength()));
+        }
 
         for (int isLatin = 0; isLatin <= 1; isLatin++) {
             if (isLatin)
                 masm.bind(&isLatin1);
 
             Label matchLoop;
             masm.bind(&matchLoop);
 
             Label isUndefined, storeDone;
             masm.branch32(Assembler::LessThan, stringIndexAddress, Imm32(0), &isUndefined);
 
             CreateDependentString(masm, cx->names(), isLatin, temp3, input, temp4, temp5,
-                                  stringIndexAddress, stringLimitAddress, &oolEntry);
+                                  stringIndexAddress, stringLimitAddress, failure);
             masm.storeValue(JSVAL_TYPE_STRING, temp3, stringAddress);
 
             masm.jump(&storeDone);
             masm.bind(&isUndefined);
 
             masm.storeValue(UndefinedValue(), stringAddress);
             masm.bind(&storeDone);
 
             masm.add32(Imm32(1), matchIndex);
             masm.branch32(Assembler::LessThanOrEqual, pairCountAddress, matchIndex, &done);
             masm.jump(&matchLoop);
         }
 
+        if (maybeTemp3 == InvalidReg) {
+            // Restore sticky and lastIndex values from temporary space, both
+            // for success and failure cases.
+
+            masm.load32(Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()), sticky);
+            masm.load32(Address(object, elementsOffset + ObjectElements::offsetOfLength()), lastIndex);
+            masm.jump(&done);
+
+            masm.bind(&failureRestore);
+            masm.load32(Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()), sticky);
+            masm.load32(Address(object, elementsOffset + ObjectElements::offsetOfLength()), lastIndex);
+
+            // Restore the match object for failure case.
+            masm.store32(Imm32(templateObject->getDenseInitializedLength()),
+                         Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()));
+            masm.store32(Imm32(templateObject->length()),
+                         Address(object, elementsOffset + ObjectElements::offsetOfLength()));
+            masm.jump(&oolEntry);
+        }
+
         masm.bind(&done);
     }
 
     // Fill in the rest of the output object.
     masm.store32(matchIndex, Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()));
     masm.store32(matchIndex, Address(object, elementsOffset + ObjectElements::offsetOfLength()));
 
     masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2);
 
     MOZ_ASSERT(templateObject->numFixedSlots() == 0);
     MOZ_ASSERT(templateObject->lookupPure(cx->names().index)->slot() == 0);
     MOZ_ASSERT(templateObject->lookupPure(cx->names().input)->slot() == 1);
 
-    masm.load32(pairsVectorAddress, temp3);
-    masm.storeValue(JSVAL_TYPE_INT32, temp3, Address(temp2, 0));
+    // sticky is now free, because no more ool entry happens.
+    masm.load32(pairsVectorAddress, sticky);
+    masm.storeValue(JSVAL_TYPE_INT32, sticky, Address(temp2, 0));
     masm.storeValue(JSVAL_TYPE_STRING, input, Address(temp2, sizeof(Value)));
 
     // All done!
     masm.tagValue(JSVAL_TYPE_OBJECT, object, result);
     masm.ret();
 
     masm.bind(&notFound);
     masm.moveValue(NullValue(), result);
     masm.ret();
 
     // Use an undefined value to signal to the caller that the OOL stub needs to be called.
     masm.bind(&oolEntry);
     masm.moveValue(UndefinedValue(), result);
     masm.ret();
 
     Linker linker(masm);
-    AutoFlushICache afc("RegExpExecStub");
+    AutoFlushICache afc("RegExpMatcherStub");
     JitCode* code = linker.newCode<CanGC>(cx, OTHER_CODE);
     if (!code)
         return nullptr;
 
 #ifdef JS_ION_PERF
-    writePerfSpewerJitCodeProfile(code, "RegExpExecStub");
+    writePerfSpewerJitCodeProfile(code, "RegExpMatcherStub");
 #endif
 
     if (cx->zone()->needsIncrementalBarrier())
         code->togglePreBarriers(true, DontReprotect);
 
     return code;
 }
 
-class OutOfLineRegExpExec : public OutOfLineCodeBase<CodeGenerator>
-{
-    LRegExpExec* lir_;
+class OutOfLineRegExpMatcher : public OutOfLineCodeBase<CodeGenerator>
+{
+    LRegExpMatcher* lir_;
 
   public:
-    explicit OutOfLineRegExpExec(LRegExpExec* lir)
+    explicit OutOfLineRegExpMatcher(LRegExpMatcher* lir)
       : lir_(lir)
     { }
 
     void accept(CodeGenerator* codegen) {
-        codegen->visitOutOfLineRegExpExec(this);
-    }
-
-    LRegExpExec* lir() const {
+        codegen->visitOutOfLineRegExpMatcher(this);
+    }
+
+    LRegExpMatcher* lir() const {
         return lir_;
     }
 };
 
-typedef bool (*RegExpExecRawFn)(JSContext* cx, HandleObject regexp,
-                                HandleString input, MatchPairs* pairs, MutableHandleValue output);
-static const VMFunction RegExpExecRawInfo = FunctionInfo<RegExpExecRawFn>(regexp_exec_raw);
-
-void
-CodeGenerator::visitOutOfLineRegExpExec(OutOfLineRegExpExec* ool)
-{
-    LRegExpExec* lir = ool->lir();
+typedef bool (*RegExpMatcherRawFn)(JSContext* cx, HandleObject regexp, HandleString input,
+                                   int32_t lastIndex, bool sticky,
+                                   MatchPairs* pairs, MutableHandleValue output);
+static const VMFunction RegExpMatcherRawInfo = FunctionInfo<RegExpMatcherRawFn>(RegExpMatcherRaw);
+
+void
+CodeGenerator::visitOutOfLineRegExpMatcher(OutOfLineRegExpMatcher* ool)
+{
+    LRegExpMatcher* lir = ool->lir();
+    Register sticky = ToRegister(lir->sticky());
+    Register lastIndex = ToRegister(lir->lastIndex());
     Register input = ToRegister(lir->string());
     Register regexp = ToRegister(lir->regexp());
 
     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
+    regs.take(sticky);
+    regs.take(lastIndex);
     regs.take(input);
     regs.take(regexp);
     Register temp = regs.takeAny();
 
     masm.computeEffectiveAddress(Address(masm.getStackPointer(),
         sizeof(irregexp::InputOutputData)), temp);
 
     pushArg(temp);
+    pushArg(sticky);
+    pushArg(lastIndex);
     pushArg(input);
     pushArg(regexp);
 
-    callVM(RegExpExecRawInfo, lir);
+    callVM(RegExpMatcherRawInfo, lir);
 
     masm.jump(ool->rejoin());
 }
 
 void
-CodeGenerator::visitRegExpExec(LRegExpExec* lir)
-{
-    MOZ_ASSERT(ToRegister(lir->regexp()) == CallTempReg0);
-    MOZ_ASSERT(ToRegister(lir->string()) == CallTempReg1);
+CodeGenerator::visitRegExpMatcher(LRegExpMatcher* lir)
+{
+    MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpMatcherRegExpReg);
+    MOZ_ASSERT(ToRegister(lir->string()) == RegExpMatcherStringReg);
+    MOZ_ASSERT(ToRegister(lir->lastIndex()) == RegExpMatcherLastIndexReg);
+    MOZ_ASSERT(ToRegister(lir->sticky()) == RegExpMatcherStickyReg);
     MOZ_ASSERT(GetValueOutput(lir) == JSReturnOperand);
 
+#if defined(JS_NUNBOX32)
+    MOZ_ASSERT(RegExpMatcherRegExpReg != JSReturnReg_Type);
+    MOZ_ASSERT(RegExpMatcherRegExpReg != JSReturnReg_Data);
+    MOZ_ASSERT(RegExpMatcherStringReg != JSReturnReg_Type);
+    MOZ_ASSERT(RegExpMatcherStringReg != JSReturnReg_Data);
+    MOZ_ASSERT(RegExpMatcherLastIndexReg != JSReturnReg_Type);
+    MOZ_ASSERT(RegExpMatcherLastIndexReg != JSReturnReg_Data);
+    MOZ_ASSERT(RegExpMatcherStickyReg != JSReturnReg_Type);
+    MOZ_ASSERT(RegExpMatcherStickyReg != JSReturnReg_Data);
+#elif defined(JS_PUNBOX64)
+    MOZ_ASSERT(RegExpMatcherRegExpReg != JSReturnReg);
+    MOZ_ASSERT(RegExpMatcherStringReg != JSReturnReg);
+    MOZ_ASSERT(RegExpMatcherLastIndexReg != JSReturnReg);
+    MOZ_ASSERT(RegExpMatcherStickyReg != JSReturnReg);
+#endif
+
     masm.reserveStack(RegExpReservedStack);
 
-    OutOfLineRegExpExec* ool = new(alloc()) OutOfLineRegExpExec(lir);
+    OutOfLineRegExpMatcher* ool = new(alloc()) OutOfLineRegExpMatcher(lir);
     addOutOfLineCode(ool, lir->mir());
 
-    JitCode* regExpExecStub = gen->compartment->jitCompartment()->regExpExecStubNoBarrier();
-    masm.call(regExpExecStub);
+    JitCode* regExpMatcherStub = gen->compartment->jitCompartment()->regExpMatcherStubNoBarrier();
+    masm.call(regExpMatcherStub);
     masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry());
     masm.bind(ool->rejoin());
 
     masm.freeStack(RegExpReservedStack);
 }
 
-// The value returned by the RegExp test stub if inline execution failed.
-static const int32_t RegExpTestFailedValue = 2;
+static const int32_t RegExpTesterResultNotFound = -1;
+static const int32_t RegExpTesterResultFailed = -2;
 
 JitCode*
-JitCompartment::generateRegExpTestStub(JSContext* cx)
-{
-    Register regexp = CallTempReg2;
-    Register input = CallTempReg3;
+JitCompartment::generateRegExpTesterStub(JSContext* cx)
+{
+    Register regexp = RegExpTesterRegExpReg;
+    Register input = RegExpTesterStringReg;
+    Register lastIndex = RegExpTesterLastIndexReg;
+    Register sticky = RegExpTesterStickyReg;
     Register result = ReturnReg;
 
-    MOZ_ASSERT(regexp != result && input != result);
-
-    // We are free to clobber all registers, as LRegExpTest is a call instruction.
-    AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
-    regs.take(input);
-    regs.take(regexp);
-    Register temp1 = regs.takeAny();
-    Register temp2 = regs.takeAny();
-    Register temp3 = regs.takeAny();
-
     MacroAssembler masm(cx);
 
 #ifdef JS_USE_LINK_REGISTER
     masm.pushReturnAddress();
 #endif
 
+    // We are free to clobber all registers, as LRegExpTester is a call instruction.
+    AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
+    regs.take(input);
+    regs.take(regexp);
+    regs.take(lastIndex);
+    regs.take(sticky);
+
+    Register temp1 = regs.takeAny();
+    Register temp2 = regs.takeAny();
+    Register temp3 = regs.takeAny();
+
     masm.reserveStack(sizeof(irregexp::InputOutputData));
 
     Label notFound, oolEntry;
-    if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, temp1, temp2, temp3, 0,
+    if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, lastIndex, sticky,
+                                 temp1, temp2, temp3, 0,
                                  RegExpShared::MatchOnly, &notFound, &oolEntry))
     {
         return nullptr;
     }
 
     Label done;
 
-    masm.move32(Imm32(1), result);
+    // temp3 contains endIndex.
+    masm.move32(temp3, result);
     masm.jump(&done);
 
     masm.bind(&notFound);
-    masm.move32(Imm32(0), result);
+    masm.move32(Imm32(RegExpTesterResultNotFound), result);
     masm.jump(&done);
 
     masm.bind(&oolEntry);
-    masm.move32(Imm32(RegExpTestFailedValue), result);
+    masm.move32(Imm32(RegExpTesterResultFailed), result);
 
     masm.bind(&done);
     masm.freeStack(sizeof(irregexp::InputOutputData));
     masm.ret();
 
     Linker linker(masm);
-    AutoFlushICache afc("RegExpTestStub");
+    AutoFlushICache afc("RegExpTesterStub");
     JitCode* code = linker.newCode<CanGC>(cx, OTHER_CODE);
     if (!code)
         return nullptr;
 
 #ifdef JS_ION_PERF
-    writePerfSpewerJitCodeProfile(code, "RegExpTestStub");
+    writePerfSpewerJitCodeProfile(code, "RegExpTesterStub");
 #endif
 
     if (cx->zone()->needsIncrementalBarrier())
         code->togglePreBarriers(true, DontReprotect);
 
     return code;
 }
 
-class OutOfLineRegExpTest : public OutOfLineCodeBase<CodeGenerator>
-{
-    LRegExpTest* lir_;
+class OutOfLineRegExpTester : public OutOfLineCodeBase<CodeGenerator>
+{
+    LRegExpTester* lir_;
 
   public:
-    explicit OutOfLineRegExpTest(LRegExpTest* lir)
+    explicit OutOfLineRegExpTester(LRegExpTester* lir)
       : lir_(lir)
     { }
 
     void accept(CodeGenerator* codegen) {
-        codegen->visitOutOfLineRegExpTest(this);
-    }
-
-    LRegExpTest* lir() const {
+        codegen->visitOutOfLineRegExpTester(this);
+    }
+
+    LRegExpTester* lir() const {
         return lir_;
     }
 };
 
-typedef bool (*RegExpTestRawFn)(JSContext* cx, HandleObject regexp,
-                                HandleString input, bool* result);
-static const VMFunction RegExpTestRawInfo = FunctionInfo<RegExpTestRawFn>(regexp_test_raw);
-
-void
-CodeGenerator::visitOutOfLineRegExpTest(OutOfLineRegExpTest* ool)
-{
-    LRegExpTest* lir = ool->lir();
+typedef bool (*RegExpTesterRawFn)(JSContext* cx, HandleObject regexp, HandleString input,
+                                  int32_t lastIndex, bool sticky, int32_t* result);
+static const VMFunction RegExpTesterRawInfo = FunctionInfo<RegExpTesterRawFn>(RegExpTesterRaw);
+
+void
+CodeGenerator::visitOutOfLineRegExpTester(OutOfLineRegExpTester* ool)
+{
+    LRegExpTester* lir = ool->lir();
+    Register sticky = ToRegister(lir->sticky());
+    Register lastIndex = ToRegister(lir->lastIndex());
     Register input = ToRegister(lir->string());
     Register regexp = ToRegister(lir->regexp());
 
+    pushArg(sticky);
+    pushArg(lastIndex);
     pushArg(input);
     pushArg(regexp);
 
-    callVM(RegExpTestRawInfo, lir);
+    callVM(RegExpTesterRawInfo, lir);
 
     masm.jump(ool->rejoin());
 }
 
 void
-CodeGenerator::visitRegExpTest(LRegExpTest* lir)
-{
-    MOZ_ASSERT(ToRegister(lir->regexp()) == CallTempReg2);
-    MOZ_ASSERT(ToRegister(lir->string()) == CallTempReg3);
+CodeGenerator::visitRegExpTester(LRegExpTester* lir)
+{
+    MOZ_ASSERT(ToRegister(lir->regexp()) == RegExpTesterRegExpReg);
+    MOZ_ASSERT(ToRegister(lir->string()) == RegExpTesterStringReg);
+    MOZ_ASSERT(ToRegister(lir->lastIndex()) == RegExpTesterLastIndexReg);
+    MOZ_ASSERT(ToRegister(lir->sticky()) == RegExpTesterStickyReg);
     MOZ_ASSERT(ToRegister(lir->output()) == ReturnReg);
 
-    OutOfLineRegExpTest* ool = new(alloc()) OutOfLineRegExpTest(lir);
+    MOZ_ASSERT(RegExpTesterRegExpReg != ReturnReg);
+    MOZ_ASSERT(RegExpTesterStringReg != ReturnReg);
+    MOZ_ASSERT(RegExpTesterLastIndexReg != ReturnReg);
+    MOZ_ASSERT(RegExpTesterStickyReg != ReturnReg);
+
+    OutOfLineRegExpTester* ool = new(alloc()) OutOfLineRegExpTester(lir);
     addOutOfLineCode(ool, lir->mir());
 
-    JitCode* regExpTestStub = gen->compartment->jitCompartment()->regExpTestStubNoBarrier();
-    masm.call(regExpTestStub);
-
-    masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpTestFailedValue), ool->entry());
+    JitCode* regExpTesterStub = gen->compartment->jitCompartment()->regExpTesterStubNoBarrier();
+    masm.call(regExpTesterStub);
+
+    masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpTesterResultFailed), ool->entry());
     masm.bind(ool->rejoin());
 }
 
 typedef JSString* (*RegExpReplaceFn)(JSContext*, HandleString, HandleObject, HandleString);
 static const VMFunction RegExpReplaceInfo = FunctionInfo<RegExpReplaceFn>(RegExpReplace);
 
 void
 CodeGenerator::visitRegExpReplace(LRegExpReplace* lir)
@@ -6039,18 +6225,17 @@ static void
 CopyStringCharsMaybeInflate(MacroAssembler& masm, Register input, Register destChars,
                             Register temp1, Register temp2)
 {
     // destChars is TwoByte and input is a Latin1 or TwoByte string, so we may
     // have to inflate.
 
     Label isLatin1, done;
     masm.loadStringLength(input, temp1);
-    masm.branchTest32(Assembler::NonZero, Address(input, JSString::offsetOfFlags()),
-                      Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
+    masm.branchLatin1String(input, &isLatin1);
     {
         masm.loadStringChars(input, input);
         CopyStringChars(masm, destChars, input, temp1, temp2, sizeof(char16_t), sizeof(char16_t));
         masm.jump(&done);
     }
     masm.bind(&isLatin1);
     {
         masm.loadStringChars(input, input);
@@ -6181,18 +6366,17 @@ CodeGenerator::visitSubstr(LSubstr* lir)
 
     // Handle inlined strings by creating a FatInlineString.
     masm.branchTest32(Assembler::Zero, stringFlags, Imm32(JSString::INLINE_CHARS_BIT), &notInline);
     masm.newGCFatInlineString(output, temp, slowPath);
     masm.store32(length, Address(output, JSString::offsetOfLength()));
     Address stringStorage(string, JSInlineString::offsetOfInlineStorage());
     Address outputStorage(output, JSInlineString::offsetOfInlineStorage());
 
-    masm.branchTest32(Assembler::NonZero, stringFlags, Imm32(JSString::LATIN1_CHARS_BIT),
-                      &isInlinedLatin1);
+    masm.branchLatin1String(string, &isInlinedLatin1);
     {
         masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS),
                      Address(output, JSString::offsetOfFlags()));
         masm.computeEffectiveAddress(stringStorage, temp);
         if (temp2 == string)
             masm.push(string);
         BaseIndex chars(temp, begin, ScaleFromElemWidth(sizeof(char16_t)));
         masm.computeEffectiveAddress(chars, temp2);
@@ -6223,17 +6407,17 @@ CodeGenerator::visitSubstr(LSubstr* lir)
     }
 
     // Handle other cases with a DependentString.
     masm.bind(&notInline);
     masm.newGCString(output, temp, slowPath);
     masm.store32(length, Address(output, JSString::offsetOfLength()));
     masm.storePtr(string, Address(output, JSDependentString::offsetOfBase()));
 
-    masm.branchTest32(Assembler::NonZero, stringFlags, Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
+    masm.branchLatin1String(string, &isLatin1);
     {
         masm.store32(Imm32(JSString::DEPENDENT_FLAGS), Address(output, JSString::offsetOfFlags()));
         masm.loadPtr(Address(string, JSString::offsetOfNonInlineChars()), temp);
         BaseIndex chars(temp, begin, ScaleFromElemWidth(sizeof(char16_t)));
         masm.computeEffectiveAddress(chars, temp);
         masm.storePtr(temp, Address(output, JSString::offsetOfNonInlineChars()));
         masm.jump(done);
     }
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -39,18 +39,18 @@ class OutOfLineNewObject;
 class CheckOverRecursedFailure;
 class OutOfLineInterruptCheckImplicit;
 class OutOfLineUnboxFloatingPoint;
 class OutOfLineStoreElementHole;
 class OutOfLineTypeOfV;
 class OutOfLineUpdateCache;
 class OutOfLineCallPostWriteBarrier;
 class OutOfLineIsCallable;
-class OutOfLineRegExpExec;
-class OutOfLineRegExpTest;
+class OutOfLineRegExpMatcher;
+class OutOfLineRegExpTester;
 class OutOfLineLambdaArrow;
 
 class CodeGenerator : public CodeGeneratorSpecific
 {
     void generateArgumentsChecks(bool bailout = true);
     bool generateBody();
 
     ConstantOrRegister toConstantOrRegister(LInstruction* lir, size_t n, MIRType type);
@@ -101,20 +101,20 @@ class CodeGenerator : public CodeGenerat
     void visitBooleanToString(LBooleanToString* lir);
     void emitIntToString(Register input, Register output, Label* ool);
     void visitIntToString(LIntToString* lir);
     void visitDoubleToString(LDoubleToString* lir);
     void visitValueToString(LValueToString* lir);
     void visitValueToObjectOrNull(LValueToObjectOrNull* lir);
     void visitInteger(LInteger* lir);
     void visitRegExp(LRegExp* lir);
-    void visitRegExpExec(LRegExpExec* lir);
-    void visitOutOfLineRegExpExec(OutOfLineRegExpExec* ool);
-    void visitRegExpTest(LRegExpTest* lir);
-    void visitOutOfLineRegExpTest(OutOfLineRegExpTest* ool);
+    void visitRegExpMatcher(LRegExpMatcher* lir);
+    void visitOutOfLineRegExpMatcher(OutOfLineRegExpMatcher* ool);
+    void visitRegExpTester(LRegExpTester* lir);
+    void visitOutOfLineRegExpTester(OutOfLineRegExpTester* ool);
     void visitRegExpReplace(LRegExpReplace* lir);
     void visitStringReplace(LStringReplace* lir);
     void emitSharedStub(ICStub::Kind kind, LInstruction* lir);
     void visitBinarySharedStub(LBinarySharedStub* lir);
     void visitUnarySharedStub(LUnarySharedStub* lir);
     void visitLambda(LLambda* lir);
     void visitOutOfLineLambdaArrow(OutOfLineLambdaArrow* ool);
     void visitLambdaArrow(LLambdaArrow* lir);
--- a/js/src/jit/InlinableNatives.h
+++ b/js/src/jit/InlinableNatives.h
@@ -61,18 +61,19 @@
     _(MathCosH)                     \
     _(MathASinH)                    \
     _(MathATanH)                    \
     _(MathACosH)                    \
     _(MathSign)                     \
     _(MathTrunc)                    \
     _(MathCbrt)                     \
                                     \
-    _(RegExpExec)                   \
-    _(RegExpTest)                   \
+    _(RegExpMatcher)                \
+    _(RegExpTester)                 \
+    _(IsRegExpObject)               \
                                     \
     _(String)                       \
     _(StringSplit)                  \
     _(StringCharCodeAt)             \
     _(StringFromCharCode)           \
     _(StringCharAt)                 \
     _(StringReplace)                \
                                     \
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -396,18 +396,18 @@ JitRuntime::patchIonBackedges(JSRuntime*
     backedgeExecAlloc_.makeAllExecutable();
 }
 
 JitCompartment::JitCompartment()
   : stubCodes_(nullptr),
     baselineGetPropReturnAddr_(nullptr),
     baselineSetPropReturnAddr_(nullptr),
     stringConcatStub_(nullptr),
-    regExpExecStub_(nullptr),
-    regExpTestStub_(nullptr)
+    regExpMatcherStub_(nullptr),
+    regExpTesterStub_(nullptr)
 {
     baselineCallReturnAddrs_[0] = baselineCallReturnAddrs_[1] = nullptr;
 }
 
 JitCompartment::~JitCompartment()
 {
     js_delete(stubCodes_);
 }
@@ -742,37 +742,37 @@ JitCompartment::sweep(FreeOp* fop, JSCom
     if (!stubCodes_->lookup(ICGetProp_Fallback::Compiler::BASELINE_KEY))
         baselineGetPropReturnAddr_ = nullptr;
     if (!stubCodes_->lookup(ICSetProp_Fallback::Compiler::BASELINE_KEY))
         baselineSetPropReturnAddr_ = nullptr;
 
     if (stringConcatStub_ && !IsMarkedUnbarriered(&stringConcatStub_))
         stringConcatStub_ = nullptr;
 
-    if (regExpExecStub_ && !IsMarkedUnbarriered(&regExpExecStub_))
-        regExpExecStub_ = nullptr;
-
-    if (regExpTestStub_ && !IsMarkedUnbarriered(&regExpTestStub_))
-        regExpTestStub_ = nullptr;
+    if (regExpMatcherStub_ && !IsMarkedUnbarriered(&regExpMatcherStub_))
+        regExpMatcherStub_ = nullptr;
+
+    if (regExpTesterStub_ && !IsMarkedUnbarriered(&regExpTesterStub_))
+        regExpTesterStub_ = nullptr;
 
     for (size_t i = 0; i <= SimdTypeDescr::LAST_TYPE; i++) {
         ReadBarrieredObject& obj = simdTemplateObjects_[i];
         if (obj && IsAboutToBeFinalized(&obj))
             obj.set(nullptr);
     }
 }
 
 void
 JitCompartment::toggleBarriers(bool enabled)
 {
     // Toggle barriers in compartment wide stubs that have patchable pre barriers.
-    if (regExpExecStub_)
-        regExpExecStub_->togglePreBarriers(enabled, Reprotect);
-    if (regExpTestStub_)
-        regExpTestStub_->togglePreBarriers(enabled, Reprotect);
+    if (regExpMatcherStub_)
+        regExpMatcherStub_->togglePreBarriers(enabled, Reprotect);
+    if (regExpTesterStub_)
+        regExpTesterStub_->togglePreBarriers(enabled, Reprotect);
 
     // Toggle barriers in baseline IC stubs.
     for (ICStubCodeMap::Enum e(*stubCodes_); !e.empty(); e.popFront()) {
         JitCode* code = *e.front().value().unsafeGet();
         code->togglePreBarriers(enabled, Reprotect);
     }
 }
 
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -1840,20 +1840,16 @@ jit::MakeMRegExpHoistable(MIRGraph& grap
                     continue;
 
                 MOZ_ASSERT(i->consumer()->isDefinition());
 
                 // All MRegExp* MIR's don't adjust the regexp.
                 MDefinition* use = i->consumer()->toDefinition();
                 if (use->isRegExpReplace())
                     continue;
-                if (use->isRegExpExec())
-                    continue;
-                if (use->isRegExpTest())
-                    continue;
 
                 hoistable = false;
                 break;
             }
 
             if (!hoistable)
                 continue;
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -5076,16 +5076,18 @@ IonBuilder::inlineScriptedCall(CallInfo&
 
         // Inlining the callee failed. Mark the callee as uninlineable only if
         // the inlining was aborted for a non-exception reason.
         if (inlineBuilder.abortReason_ == AbortReason_Disable) {
             calleeScript->setUninlineable();
             abortReason_ = AbortReason_Inlining;
         } else if (inlineBuilder.abortReason_ == AbortReason_Inlining) {
             abortReason_ = AbortReason_Inlining;
+        } else if (inlineBuilder.abortReason_ == AbortReason_Alloc) {
+            abortReason_ = AbortReason_Alloc;
         } else if (inlineBuilder.abortReason_ == AbortReason_PreliminaryObjects) {
             const ObjectGroupVector& groups = inlineBuilder.abortedPreliminaryGroups();
             MOZ_ASSERT(!groups.empty());
             for (size_t i = 0; i < groups.length(); i++)
                 addAbortedPreliminaryGroup(groups[i]);
             abortReason_ = AbortReason_PreliminaryObjects;
         }
 
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -809,19 +809,20 @@ class IonBuilder
     InliningStatus inlineConstantStringSplit(CallInfo& callInfo);
     InliningStatus inlineStringSplit(CallInfo& callInfo);
     InliningStatus inlineStrCharCodeAt(CallInfo& callInfo);
     InliningStatus inlineConstantCharCodeAt(CallInfo& callInfo);
     InliningStatus inlineStrFromCharCode(CallInfo& callInfo);
     InliningStatus inlineStrCharAt(CallInfo& callInfo);
     InliningStatus inlineStrReplace(CallInfo& callInfo);
 
-    // RegExp natives.
-    InliningStatus inlineRegExpExec(CallInfo& callInfo);
-    InliningStatus inlineRegExpTest(CallInfo& callInfo);
+    // RegExp intrinsics.
+    InliningStatus inlineRegExpMatcher(CallInfo& callInfo);
+    InliningStatus inlineRegExpTester(CallInfo& callInfo);
+    InliningStatus inlineIsRegExpObject(CallInfo& callInfo);
 
     // Object natives and intrinsics.
     InliningStatus inlineObjectCreate(CallInfo& callInfo);
     InliningStatus inlineDefineDataProperty(CallInfo& callInfo);
 
     // Atomics natives.
     InliningStatus inlineAtomicsCompareExchange(CallInfo& callInfo);
     InliningStatus inlineAtomicsExchange(CallInfo& callInfo);
--- a/js/src/jit/JitCompartment.h
+++ b/js/src/jit/JitCompartment.h
@@ -397,24 +397,24 @@ class JitCompartment
 
     // Stubs to concatenate two strings inline, or perform RegExp calls inline.
     // These bake in zone and compartment specific pointers and can't be stored
     // in JitRuntime. These are weak pointers, but are not declared as
     // ReadBarriered since they are only read from during Ion compilation,
     // which may occur off thread and whose barriers are captured during
     // CodeGenerator::link.
     JitCode* stringConcatStub_;
-    JitCode* regExpExecStub_;
-    JitCode* regExpTestStub_;
+    JitCode* regExpMatcherStub_;
+    JitCode* regExpTesterStub_;
 
     mozilla::Array<ReadBarrieredObject, SimdTypeDescr::LAST_TYPE + 1> simdTemplateObjects_;
 
     JitCode* generateStringConcatStub(JSContext* cx);
-    JitCode* generateRegExpExecStub(JSContext* cx);
-    JitCode* generateRegExpTestStub(JSContext* cx);
+    JitCode* generateRegExpMatcherStub(JSContext* cx);
+    JitCode* generateRegExpTesterStub(JSContext* cx);
 
   public:
     JSObject* getSimdTemplateObjectFor(JSContext* cx, Handle<SimdTypeDescr*> descr) {
         ReadBarrieredObject& tpl = simdTemplateObjects_[descr->type()];
         if (!tpl)
             tpl.set(TypedObject::createZeroed(cx, descr, 0, gc::TenuredHeap));
         return tpl.get();
     }
@@ -488,36 +488,36 @@ class JitCompartment
 
     void mark(JSTracer* trc, JSCompartment* compartment);
     void sweep(FreeOp* fop, JSCompartment* compartment);
 
     JitCode* stringConcatStubNoBarrier() const {
         return stringConcatStub_;
     }
 
-    JitCode* regExpExecStubNoBarrier() const {
-        return regExpExecStub_;
+    JitCode* regExpMatcherStubNoBarrier() const {
+        return regExpMatcherStub_;
     }
 
-    bool ensureRegExpExecStubExists(JSContext* cx) {
-        if (regExpExecStub_)
+    bool ensureRegExpMatcherStubExists(JSContext* cx) {
+        if (regExpMatcherStub_)
             return true;
-        regExpExecStub_ = generateRegExpExecStub(cx);
-        return regExpExecStub_ != nullptr;
+        regExpMatcherStub_ = generateRegExpMatcherStub(cx);
+        return regExpMatcherStub_ != nullptr;
     }
 
-    JitCode* regExpTestStubNoBarrier() const {
-        return regExpTestStub_;
+    JitCode* regExpTesterStubNoBarrier() const {
+        return regExpTesterStub_;
     }
 
-    bool ensureRegExpTestStubExists(JSContext* cx) {
-        if (regExpTestStub_)
+    bool ensureRegExpTesterStubExists(JSContext* cx) {
+        if (regExpTesterStub_)
             return true;
-        regExpTestStub_ = generateRegExpTestStub(cx);
-        return regExpTestStub_ != nullptr;
+        regExpTesterStub_ = generateRegExpTesterStub(cx);
+        return regExpTesterStub_ != nullptr;
     }
 };
 
 // Called from JSCompartment::discardJitCode().
 void InvalidateAll(FreeOp* fop, JS::Zone* zone);
 void FinishInvalidation(FreeOp* fop, JSScript* script);
 
 // On windows systems, really large frames need to be incrementally touched.
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2078,22 +2078,16 @@ MustCloneRegExpForCall(MCall* call, uint
 {
     // We have a regex literal flowing into a call. Return |false| iff
     // this is a native call that does not let the regex escape.
 
     JSFunction* target = call->getSingleTarget();
     if (!target || !target->isNative())
         return true;
 
-    if (useIndex == MCall::IndexOfThis() &&
-        (target->native() == regexp_exec || target->native() == regexp_test))
-    {
-        return false;
-    }
-
     if (useIndex == MCall::IndexOfArgument(0) &&
         (target->native() == str_split ||
          target->native() == str_replace ||
          target->native() == str_match ||
          target->native() == str_search))
     {
         return false;
     }
@@ -2112,18 +2106,18 @@ MustCloneRegExp(MRegExp* regexp)
     // it escape, we don't have to clone it.
 
     for (MUseIterator iter(regexp->usesBegin()); iter != regexp->usesEnd(); iter++) {
         MNode* node = iter->consumer();
         if (!node->isDefinition())
             return true;
 
         MDefinition* def = node->toDefinition();
-        if (def->isRegExpTest()) {
-            MRegExpTest* test = def->toRegExpTest();
+        if (def->isRegExpTester()) {
+            MRegExpTester* test = def->toRegExpTester();
             if (test->indexOf(*iter) == 1) {
                 // Optimized RegExp.prototype.test.
                 MOZ_ASSERT(test->regexp() == regexp);
                 continue;
             }
         } else if (def->isCall()) {
             MCall* call = def->toCall();
             if (!MustCloneRegExpForCall(call, call->indexOf(*iter)))
@@ -2144,35 +2138,43 @@ LIRGenerator::visitRegExp(MRegExp* ins)
     } else {
         LRegExp* lir = new(alloc()) LRegExp();
         defineReturn(lir, ins);
         assignSafepoint(lir, ins);
     }
 }
 
 void
-LIRGenerator::visitRegExpExec(MRegExpExec* ins)
+LIRGenerator::visitRegExpMatcher(MRegExpMatcher* ins)
 {
     MOZ_ASSERT(ins->regexp()->type() == MIRType_Object);
     MOZ_ASSERT(ins->string()->type() == MIRType_String);
-
-    LRegExpExec* lir = new(alloc()) LRegExpExec(useFixedAtStart(ins->regexp(), CallTempReg0),
-                                                useFixedAtStart(ins->string(), CallTempReg1));
+    MOZ_ASSERT(ins->lastIndex()->type() == MIRType_Int32);
+    MOZ_ASSERT(ins->sticky()->type() == MIRType_Boolean);
+
+    LRegExpMatcher* lir = new(alloc()) LRegExpMatcher(useFixedAtStart(ins->regexp(), RegExpMatcherRegExpReg),
+                                                      useFixedAtStart(ins->string(), RegExpMatcherStringReg),
+                                                      useFixedAtStart(ins->lastIndex(), RegExpMatcherLastIndexReg),
+                                                      useFixedAtStart(ins->sticky(), RegExpMatcherStickyReg));
     defineReturn(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
-LIRGenerator::visitRegExpTest(MRegExpTest* ins)
+LIRGenerator::visitRegExpTester(MRegExpTester* ins)
 {
     MOZ_ASSERT(ins->regexp()->type() == MIRType_Object);
     MOZ_ASSERT(ins->string()->type() == MIRType_String);
-
-    LRegExpTest* lir = new(alloc()) LRegExpTest(useFixedAtStart(ins->regexp(), CallTempReg2),
-                                                useFixedAtStart(ins->string(), CallTempReg3));
+    MOZ_ASSERT(ins->lastIndex()->type() == MIRType_Int32);
+    MOZ_ASSERT(ins->sticky()->type() == MIRType_Boolean);
+
+    LRegExpTester* lir = new(alloc()) LRegExpTester(useFixedAtStart(ins->regexp(), RegExpTesterRegExpReg),
+                                                    useFixedAtStart(ins->string(), RegExpTesterStringReg),
+                                                    useFixedAtStart(ins->lastIndex(), RegExpTesterLastIndexReg),
+                                                    useFixedAtStart(ins->sticky(), RegExpTesterStickyReg));
     defineReturn(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitRegExpReplace(MRegExpReplace* ins)
 {
     MOZ_ASSERT(ins->pattern()->type() == MIRType_Object);
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -156,18 +156,18 @@ class LIRGenerator : public LIRGenerator
     void visitOsrArgumentsObject(MOsrArgumentsObject* object);
     void visitToDouble(MToDouble* convert);
     void visitToFloat32(MToFloat32* convert);
     void visitToInt32(MToInt32* convert);
     void visitTruncateToInt32(MTruncateToInt32* truncate);
     void visitToString(MToString* convert);
     void visitToObjectOrNull(MToObjectOrNull* convert);
     void visitRegExp(MRegExp* ins);
-    void visitRegExpExec(MRegExpExec* ins);
-    void visitRegExpTest(MRegExpTest* ins);
+    void visitRegExpMatcher(MRegExpMatcher* ins);
+    void visitRegExpTester(MRegExpTester* ins);
     void visitRegExpReplace(MRegExpReplace* ins);
     void visitStringReplace(MStringReplace* ins);
     void visitBinarySharedStub(MBinarySharedStub* ins);
     void visitUnarySharedStub(MUnarySharedStub* ins);
     void visitLambda(MLambda* ins);
     void visitLambdaArrow(MLambdaArrow* ins);
     void visitKeepAliveObject(MKeepAliveObject* ins);
     void visitSlots(MSlots* ins);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -171,20 +171,22 @@ IonBuilder::inlineNativeCall(CallInfo& c
       case InlinableNative::MathSign:
         return inlineMathFunction(callInfo, MMathFunction::Sign);
       case InlinableNative::MathTrunc:
         return inlineMathFunction(callInfo, MMathFunction::Trunc);
       case InlinableNative::MathCbrt:
         return inlineMathFunction(callInfo, MMathFunction::Cbrt);
 
       // RegExp natives.
-      case InlinableNative::RegExpExec:
-        return CallResultEscapes(pc) ? inlineRegExpExec(callInfo) : inlineRegExpTest(callInfo);
-      case InlinableNative::RegExpTest:
-        return inlineRegExpTest(callInfo);
+      case InlinableNative::RegExpMatcher:
+        return inlineRegExpMatcher(callInfo);
+      case InlinableNative::RegExpTester:
+        return inlineRegExpTester(callInfo);
+      case InlinableNative::IsRegExpObject:
+        return inlineIsRegExpObject(callInfo);
 
       // String natives.
       case InlinableNative::String:
         return inlineStringObject(callInfo);
       case InlinableNative::StringSplit:
         return inlineStringSplit(callInfo);
       case InlinableNative::StringCharCodeAt:
         return inlineStrCharCodeAt(callInfo);
@@ -1826,90 +1828,155 @@ IonBuilder::inlineStrCharAt(CallInfo& ca
 
     MFromCharCode* string = MFromCharCode::New(alloc(), charCode);
     current->add(string);
     current->push(string);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
-IonBuilder::inlineRegExpExec(CallInfo& callInfo)
+IonBuilder::inlineRegExpMatcher(CallInfo& callInfo)
 {
-    if (callInfo.argc() != 1 || callInfo.constructing()) {
+    // This is called from Self-hosted JS, after testing each argument,
+    // most of following tests should be passed.
+
+    if (callInfo.argc() != 4 || callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
-    if (callInfo.thisArg()->type() != MIRType_Object)
+    // regexp
+    if (callInfo.getArg(0)->type() != MIRType_Object)
         return InliningStatus_NotInlined;
 
-    TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet();
-    const Class* clasp = thisTypes ? thisTypes->getKnownClass(constraints()) : nullptr;
+    TemporaryTypeSet* arg0Types = callInfo.getArg(0)->resultTypeSet();
+    const Class* clasp = arg0Types ? arg0Types->getKnownClass(constraints()) : nullptr;
     if (clasp != &RegExpObject::class_)
         return InliningStatus_NotInlined;
 
-    if (callInfo.getArg(0)->mightBeType(MIRType_Object))
+    // string
+    if (callInfo.getArg(1)->mightBeType(MIRType_Object))
+        return InliningStatus_NotInlined;
+
+    // lastIndex: Only inline if it's Int32.
+    if (callInfo.getArg(2)->type() != MIRType_Int32)
+        return InliningStatus_NotInlined;
+
+    // sticky
+    if (callInfo.getArg(3)->type() != MIRType_Boolean)
         return InliningStatus_NotInlined;
 
     JSContext* cx = GetJitContext()->cx;
-    if (!cx->compartment()->jitCompartment()->ensureRegExpExecStubExists(cx))
+    if (!cx->compartment()->jitCompartment()->ensureRegExpMatcherStubExists(cx)) {
+        oom();
         return InliningStatus_Error;
+    }
 
     callInfo.setImplicitlyUsedUnchecked();
 
-    MInstruction* exec = MRegExpExec::New(alloc(), callInfo.thisArg(), callInfo.getArg(0));
-    current->add(exec);
-    current->push(exec);
-
-    if (!resumeAfter(exec))
+    MInstruction* matcher = MRegExpMatcher::New(alloc(), callInfo.getArg(0), callInfo.getArg(1),
+                                                callInfo.getArg(2), callInfo.getArg(3));
+    current->add(matcher);
+    current->push(matcher);
+
+    if (!resumeAfter(matcher))
         return InliningStatus_Error;
 
-    if (!pushTypeBarrier(exec, getInlineReturnTypeSet(), BarrierKind::TypeSet))
+    if (!pushTypeBarrier(matcher, getInlineReturnTypeSet(), BarrierKind::TypeSet))
         return InliningStatus_Error;
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
-IonBuilder::inlineRegExpTest(CallInfo& callInfo)
+IonBuilder::inlineRegExpTester(CallInfo& callInfo)
 {
-    if (callInfo.argc() != 1 || callInfo.constructing()) {
+    // This is called from Self-hosted JS, after testing each argument,
+    // most of following tests should be passed.
+
+    if (callInfo.argc() != 4 || callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
-    // TI can infer a nullptr return type of regexp_test with eager compilation.
-    if (CallResultEscapes(pc) && getInlineReturnType() != MIRType_Boolean)
+    // regexp
+    if (callInfo.getArg(0)->type() != MIRType_Object)
         return InliningStatus_NotInlined;
 
-    if (callInfo.thisArg()->type() != MIRType_Object)
-        return InliningStatus_NotInlined;
-    TemporaryTypeSet* thisTypes = callInfo.thisArg()->resultTypeSet();
-    const Class* clasp = thisTypes ? thisTypes->getKnownClass(constraints()) : nullptr;
+    TemporaryTypeSet* arg0Types = callInfo.getArg(0)->resultTypeSet();
+    const Class* clasp = arg0Types ? arg0Types->getKnownClass(constraints()) : nullptr;
     if (clasp != &RegExpObject::class_)
         return InliningStatus_NotInlined;
-    if (callInfo.getArg(0)->mightBeType(MIRType_Object))
+
+    // string
+    if (callInfo.getArg(1)->mightBeType(MIRType_Object))
+        return InliningStatus_NotInlined;
+
+    // lastIndex: Only inline if it's Int32.
+    if (callInfo.getArg(2)->type() != MIRType_Int32)
+        return InliningStatus_NotInlined;
+
+    // sticky
+    if (callInfo.getArg(3)->type() != MIRType_Boolean)
         return InliningStatus_NotInlined;
 
     JSContext* cx = GetJitContext()->cx;
-    if (!cx->compartment()->jitCompartment()->ensureRegExpTestStubExists(cx))
+    if (!cx->compartment()->jitCompartment()->ensureRegExpTesterStubExists(cx)) {
+        oom();
         return InliningStatus_Error;
+    }
 
     callInfo.setImplicitlyUsedUnchecked();
 
-    MInstruction* match = MRegExpTest::New(alloc(), callInfo.thisArg(), callInfo.getArg(0));
-    current->add(match);
-    current->push(match);
-    if (!resumeAfter(match))
+    MInstruction* tester = MRegExpTester::New(alloc(), callInfo.getArg(0), callInfo.getArg(1),
+                                              callInfo.getArg(2), callInfo.getArg(3));
+    current->add(tester);
+    current->push(tester);
+
+    if (!resumeAfter(tester))
         return InliningStatus_Error;
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
+IonBuilder::inlineIsRegExpObject(CallInfo& callInfo)
+{
+    if (callInfo.constructing() || callInfo.argc() != 1) {
+        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
+        return InliningStatus_NotInlined;
+    }
+
+    if (getInlineReturnType() != MIRType_Boolean)
+        return InliningStatus_NotInlined;
+
+    MDefinition* arg = callInfo.getArg(0);
+
+    bool isRegExpObject;
+    if (!arg->mightBeType(MIRType_Object)) {
+        isRegExpObject = false;
+    } else {
+        if (arg->type() != MIRType_Object)
+            return InliningStatus_NotInlined;
+
+        TemporaryTypeSet* types = arg->resultTypeSet();
+        const Class* clasp = types ? types->getKnownClass(constraints()) : nullptr;
+        if (!clasp || clasp->isProxy())
+            return InliningStatus_NotInlined;
+
+        isRegExpObject = (clasp == &RegExpObject::class_);
+    }
+
+    pushConstant(BooleanValue(isRegExpObject));
+
+    callInfo.setImplicitlyUsedUnchecked();
+    return InliningStatus_Inlined;
+}
+
+IonBuilder::InliningStatus
 IonBuilder::inlineStrReplace(CallInfo& callInfo)
 {
     if (callInfo.argc() != 2 || callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
     // Return: String.
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -7509,91 +7509,118 @@ class MRegExp : public MNullaryInstructi
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
     bool possiblyCalls() const override {
         return true;
     }
 };
 
-class MRegExpExec
-  : public MBinaryInstruction,
-    public MixPolicy<ConvertToStringPolicy<0>, ObjectPolicy<1> >::Data
+class MRegExpMatcher
+  : public MAryInstruction<4>,
+    public NoTypePolicy::Data
 {
   private:
 
-    MRegExpExec(MDefinition* regexp, MDefinition* string)
-      : MBinaryInstruction(string, regexp)
-    {
+    MRegExpMatcher(MDefinition* regexp, MDefinition* string, MDefinition* lastIndex,
+                   MDefinition* sticky)
+      : MAryInstruction<4>()
+    {
+        initOperand(0, regexp);
+        initOperand(1, string);
+        initOperand(2, lastIndex);
+        initOperand(3, sticky);
+
         // May be object or null.
         setResultType(MIRType_Value);
     }
 
   public:
-    INSTRUCTION_HEADER(RegExpExec)
-
-    static MRegExpExec* New(TempAllocator& alloc, MDefinition* regexp, MDefinition* string) {
-        return new(alloc) MRegExpExec(regexp, string);
-    }
-
-    MDefinition* string() const {
-        return getOperand(0);
+    INSTRUCTION_HEADER(RegExpMatcher)
+
+    static MRegExpMatcher* New(TempAllocator& alloc, MDefinition* regexp, MDefinition* string,
+                               MDefinition* lastIndex, MDefinition* sticky)
+    {
+        return new(alloc) MRegExpMatcher(regexp, string, lastIndex, sticky);
     }
 
     MDefinition* regexp() const {
+        return getOperand(0);
+    }
+    MDefinition* string() const {
         return getOperand(1);
     }
+    MDefinition* lastIndex() const {
+        return getOperand(2);
+    }
+    MDefinition* sticky() const {
+        return getOperand(3);
+    }
 
     bool writeRecoverData(CompactBufferWriter& writer) const override;
 
     bool canRecoverOnBailout() const override {
         // XXX: always return false for now, to work around bug 1132128.
         if (false && regexp()->isRegExp())
             return !regexp()->toRegExp()->source()->needUpdateLastIndex();
         return false;
     }
 
     bool possiblyCalls() const override {
         return true;
     }
 };
 
-class MRegExpTest
-  : public MBinaryInstruction,
-    public MixPolicy<ObjectPolicy<1>, ConvertToStringPolicy<0> >::Data
+class MRegExpTester
+  : public MAryInstruction<4>,
+    public NoTypePolicy::Data
 {
   private:
 
-    MRegExpTest(MDefinition* regexp, MDefinition* string)
-      : MBinaryInstruction(string, regexp)
-    {
-        setResultType(MIRType_Boolean);
-    }
-
-  public:
-    INSTRUCTION_HEADER(RegExpTest)
-
-    static MRegExpTest* New(TempAllocator& alloc, MDefinition* regexp, MDefinition* string) {
-        return new(alloc) MRegExpTest(regexp, string);
-    }
-
+    MRegExpTester(MDefinition* regexp, MDefinition* string, MDefinition* lastIndex,
+                  MDefinition* sticky)
+      : MAryInstruction<4>()
+    {
+        initOperand(0, regexp);
+        initOperand(1, string);
+        initOperand(2, lastIndex);
+        initOperand(3, sticky);
+
+        setResultType(MIRType_Int32);
+    }
+
+  public:
+    INSTRUCTION_HEADER(RegExpTester)
+
+    static MRegExpTester* New(TempAllocator& alloc, MDefinition* regexp, MDefinition* string,
+                              MDefinition* lastIndex, MDefinition* sticky)
+    {
+        return new(alloc) MRegExpTester(regexp, string, lastIndex, sticky);
+    }
+
+    MDefinition* regexp() const {
+        return getOperand(0);
+    }
     MDefinition* string() const {
-        return getOperand(0);
-    }
-    MDefinition* regexp() const {
         return getOperand(1);
     }
+    MDefinition* lastIndex() const {
+        return getOperand(2);
+    }
+    MDefinition* sticky() const {
+        return getOperand(3);
+    }
 
     bool possiblyCalls() const override {
         return true;
     }
 
     bool writeRecoverData(CompactBufferWriter& writer) const override;
     bool canRecoverOnBailout() const override {
-        // RegExpTest has a side-effect on the regexp object's lastIndex
+        // RegExpTester has a side-effect on the regexp object's lastIndex
         // when sticky or global flags are set.
         // Return false unless we are sure it's not the case.
         // XXX: always return false for now, to work around bug 1132128.
         if (false && regexp()->isRegExp())
             return !regexp()->toRegExp()->source()->needUpdateLastIndex();
         return false;
     }
 };
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -134,18 +134,18 @@ namespace jit {
     _(MutateProto)                                                          \
     _(InitProp)                                                             \
     _(InitPropGetterSetter)                                                 \
     _(Start)                                                                \
     _(OsrEntry)                                                             \
     _(Nop)                                                                  \
     _(LimitedTruncate)                                                      \
     _(RegExp)                                                               \
-    _(RegExpExec)                                                           \
-    _(RegExpTest)                                                           \
+    _(RegExpMatcher)                                                        \
+    _(RegExpTester)                                                         \
     _(RegExpReplace)                                                        \
     _(StringReplace)                                                        \
     _(Lambda)                                                               \
     _(LambdaArrow)                                                          \
     _(KeepAliveObject)                                                      \
     _(Slots)                                                                \
     _(Elements)                                                             \
     _(ConstantElements)                                                     \
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1237,18 +1237,17 @@ void
 MacroAssembler::loadStringChar(Register str, Register index, Register output)
 {
     MOZ_ASSERT(str != output);
     MOZ_ASSERT(index != output);
 
     loadStringChars(str, output);
 
     Label isLatin1, done;
-    branchTest32(Assembler::NonZero, Address(str, JSString::offsetOfFlags()),
-                 Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
+    branchLatin1String(str, &isLatin1);
     load16ZeroExtend(BaseIndex(output, index, TimesTwo), output);
     jump(&done);
 
     bind(&isLatin1);
     load8ZeroExtend(BaseIndex(output, index, TimesOne), output);
 
     bind(&done);
 }
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -905,16 +905,25 @@ class MacroAssembler : public MacroAssem
     void loadStringChar(Register str, Register index, Register output);
 
     void branchIfRope(Register str, Label* label) {
         Address flags(str, JSString::offsetOfFlags());
         static_assert(JSString::ROPE_FLAGS == 0, "Rope type flags must be 0");
         branchTest32(Assembler::Zero, flags, Imm32(JSString::TYPE_FLAGS_MASK), label);
     }
 
+    void branchLatin1String(Register string, Label* label) {
+        branchTest32(Assembler::NonZero, Address(string, JSString::offsetOfFlags()),
+                     Imm32(JSString::LATIN1_CHARS_BIT), label);
+    }
+    void branchTwoByteString(Register string, Label* label) {
+        branchTest32(Assembler::Zero, Address(string, JSString::offsetOfFlags()),
+                     Imm32(JSString::LATIN1_CHARS_BIT), label);
+    }
+
     void loadJSContext(Register dest) {
         loadPtr(AbsoluteAddress(GetJitContext()->runtime->addressOfJSContext()), dest);
     }
     void loadJitActivation(Register dest) {
         loadPtr(AbsoluteAddress(GetJitContext()->runtime->addressOfActivation()), dest);
     }
 
     template<typename T>
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -991,62 +991,67 @@ RStringSplit::recover(JSContext* cx, Sna
     if (!res)
         return false;
 
     result.setObject(*res);
     iter.storeInstructionResult(result);
     return true;
 }
 
-bool MRegExpExec::writeRecoverData(CompactBufferWriter& writer) const
+bool MRegExpMatcher::writeRecoverData(CompactBufferWriter& writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
-    writer.writeUnsigned(uint32_t(RInstruction::Recover_RegExpExec));
+    writer.writeUnsigned(uint32_t(RInstruction::Recover_RegExpMatcher));
     return true;
 }
 
-RRegExpExec::RRegExpExec(CompactBufferReader& reader)
+RRegExpMatcher::RRegExpMatcher(CompactBufferReader& reader)
 {}
 
-bool RRegExpExec::recover(JSContext* cx, SnapshotIterator& iter) const{
+bool RRegExpMatcher::recover(JSContext* cx, SnapshotIterator& iter) const{
     RootedObject regexp(cx, &iter.read().toObject());
     RootedString input(cx, iter.read().toString());
+    int32_t lastIndex = iter.read().toInt32();
+    bool sticky = iter.read().toBoolean();
 
     RootedValue result(cx);
 
-    if (!regexp_exec_raw(cx, regexp, input, nullptr, &result))
+    fprintf(stderr, "Recover\n");
+    if (!RegExpMatcherRaw(cx, regexp, input, lastIndex, sticky, nullptr, &result))
         return false;
 
     iter.storeInstructionResult(result);
     return true;
 }
 
 bool
-MRegExpTest::writeRecoverData(CompactBufferWriter& writer) const
+MRegExpTester::writeRecoverData(CompactBufferWriter& writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
-    writer.writeUnsigned(uint32_t(RInstruction::Recover_RegExpTest));
+    writer.writeUnsigned(uint32_t(RInstruction::Recover_RegExpTester));
     return true;
 }
 
-RRegExpTest::RRegExpTest(CompactBufferReader& reader)
+RRegExpTester::RRegExpTester(CompactBufferReader& reader)
 { }
 
 bool
-RRegExpTest::recover(JSContext* cx, SnapshotIterator& iter) const
+RRegExpTester::recover(JSContext* cx, SnapshotIterator& iter) const
 {
     RootedString string(cx, iter.read().toString());
     RootedObject regexp(cx, &iter.read().toObject());
-    bool resultBool;
+    int32_t lastIndex = iter.read().toInt32();
+    bool sticky = iter.read().toBoolean();
+    int32_t endIndex;
 
-    if (!js::regexp_test_raw(cx, regexp, string, &resultBool))
+    if (!js::RegExpTesterRaw(cx, regexp, string, lastIndex, sticky, &endIndex))
         return false;
 
     RootedValue result(cx);
-    result.setBoolean(resultBool);
+    result.setInt32(endIndex);
     iter.storeInstructionResult(result);
     return true;
 }
 
 bool
 MRegExpReplace::writeRecoverData(CompactBufferWriter& writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
--- a/js/src/jit/Recover.h
+++ b/js/src/jit/Recover.h
@@ -83,18 +83,18 @@ namespace jit {
     _(PowHalf)                                  \
     _(MinMax)                                   \
     _(Abs)                                      \
     _(Sqrt)                                     \
     _(Atan2)                                    \
     _(Hypot)                                    \
     _(MathFunction)                             \
     _(StringSplit)                              \
-    _(RegExpExec)                               \
-    _(RegExpTest)                               \
+    _(RegExpMatcher)                            \
+    _(RegExpTester)                             \
     _(RegExpReplace)                            \
     _(StringReplace)                            \
     _(TypeOf)                                   \
     _(ToDouble)                                 \
     _(ToFloat32)                                \
     _(TruncateToInt32)                          \
     _(NewObject)                                \
     _(NewArray)                                 \
@@ -554,35 +554,35 @@ class RStringSplit final : public RInstr
 
     virtual uint32_t numOperands() const {
         return 3;
     }
 
     bool recover(JSContext* cx, SnapshotIterator& iter) const;
 };
 
-class RRegExpExec final : public RInstruction
+class RRegExpMatcher final : public RInstruction
 {
   public:
-    RINSTRUCTION_HEADER_(RegExpExec)
+    RINSTRUCTION_HEADER_(RegExpMatcher)
 
     virtual uint32_t numOperands() const {
-        return 2;
+        return 5;
     }
 
     bool recover(JSContext* cx, SnapshotIterator& iter) const;
 };
 
-class RRegExpTest final : public RInstruction
+class RRegExpTester final : public RInstruction
 {
   public:
-    RINSTRUCTION_HEADER_(RegExpTest)
+    RINSTRUCTION_HEADER_(RegExpTester)
 
     virtual uint32_t numOperands() const {
-        return 2;
+        return 5;
     }
 
     bool recover(JSContext* cx, SnapshotIterator& iter) const;
 };
 
 class RRegExpReplace final : public RInstruction
 {
   public:
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -155,16 +155,27 @@ static MOZ_CONSTEXPR_VAR Register AsmJSI
 // Registers used in the GenerateFFIIonExit Disable Activation block.
 // None of these may be the second scratch register (lr).
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnData = r2;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnType = r3;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = r0;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = r1;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = r4;
 
+// Registerd used in RegExpMatcher instruction (do not use JSReturnOperand).
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherRegExpReg = CallTempReg0;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStringReg = CallTempReg1;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStickyReg = CallTempReg3;
+
+// Registerd used in RegExpTester instruction (do not use ReturnReg).
+static MOZ_CONSTEXPR_VAR Register RegExpTesterRegExpReg = CallTempReg0;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStringReg = CallTempReg1;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStickyReg = CallTempReg3;
 
 static MOZ_CONSTEXPR_VAR FloatRegister d0  = {FloatRegisters::d0, VFPRegister::Double};
 static MOZ_CONSTEXPR_VAR FloatRegister d1  = {FloatRegisters::d1, VFPRegister::Double};
 static MOZ_CONSTEXPR_VAR FloatRegister d2  = {FloatRegisters::d2, VFPRegister::Double};
 static MOZ_CONSTEXPR_VAR FloatRegister d3  = {FloatRegisters::d3, VFPRegister::Double};
 static MOZ_CONSTEXPR_VAR FloatRegister d4  = {FloatRegisters::d4, VFPRegister::Double};
 static MOZ_CONSTEXPR_VAR FloatRegister d5  = {FloatRegisters::d5, VFPRegister::Double};
 static MOZ_CONSTEXPR_VAR FloatRegister d6  = {FloatRegisters::d6, VFPRegister::Double};
--- a/js/src/jit/arm64/Assembler-arm64.h
+++ b/js/src/jit/arm64/Assembler-arm64.h
@@ -128,16 +128,28 @@ static constexpr Register AsmJSIonExitRe
 // Registers used in the GenerateFFIIonExit Disable Activation block.
 // None of these may be the second scratch register.
 static constexpr Register AsmJSIonExitRegReturnData = r2;
 static constexpr Register AsmJSIonExitRegReturnType = r3;
 static constexpr Register AsmJSIonExitRegD0 = r0;
 static constexpr Register AsmJSIonExitRegD1 = r1;
 static constexpr Register AsmJSIonExitRegD2 = r4;
 
+// Registerd used in RegExpMatcher instruction (do not use JSReturnOperand).
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherRegExpReg = CallTempReg0;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStringReg = CallTempReg1;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStickyReg = CallTempReg3;
+
+// Registerd used in RegExpTester instruction (do not use ReturnReg).
+static MOZ_CONSTEXPR_VAR Register RegExpTesterRegExpReg = CallTempReg0;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStringReg = CallTempReg1;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStickyReg = CallTempReg3;
+
 static constexpr Register JSReturnReg_Type = r3;
 static constexpr Register JSReturnReg_Data = r2;
 
 static constexpr FloatRegister NANReg = { FloatRegisters::d14, FloatRegisters::Single };
 // N.B. r8 isn't listed as an aapcs temp register, but we can use it as such because we never
 // use return-structs.
 static constexpr Register CallTempNonArgRegs[] = { r8, r9, r10, r11, r12, r13, r14, r15 };
 static const uint32_t NumCallTempNonArgRegs =
--- a/js/src/jit/mips-shared/Assembler-mips-shared.h
+++ b/js/src/jit/mips-shared/Assembler-mips-shared.h
@@ -120,16 +120,28 @@ static MOZ_CONSTEXPR_VAR Register AsmJSI
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE3 = a3;
 
 // Registers used in the GenerateFFIIonExit Disable Activation block.
 // None of these may be the second scratch register (t8).
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = a0;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = a1;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = t0;
 
+// Registerd used in RegExpMatcher instruction (do not use JSReturnOperand).
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherRegExpReg = CallTempReg0;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStringReg = CallTempReg1;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStickyReg = CallTempReg3;
+
+// Registerd used in RegExpTester instruction (do not use ReturnReg).
+static MOZ_CONSTEXPR_VAR Register RegExpTesterRegExpReg = CallTempReg0;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStringReg = CallTempReg1;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStickyReg = CallTempReg3;
+
 static MOZ_CONSTEXPR_VAR uint32_t CodeAlignment = 4;
 
 // This boolean indicates whether we support SIMD instructions flavoured for
 // this architecture or not. Rather than a method in the LIRGenerator, it is
 // here such that it is accessible from the entire codebase. Once full support
 // for SIMD is reached on all tier-1 platforms, this constant can be deleted.
 static MOZ_CONSTEXPR_VAR bool SupportsSimd = false;
 
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -3935,59 +3935,77 @@ class LRegExp : public LCallInstructionH
   public:
     LIR_HEADER(RegExp)
 
     const MRegExp* mir() const {
         return mir_->toRegExp();
     }
 };
 
-class LRegExpExec : public LCallInstructionHelper<BOX_PIECES, 2, 0>
-{
-  public:
-    LIR_HEADER(RegExpExec)
-
-    LRegExpExec(const LAllocation& regexp, const LAllocation& string)
+class LRegExpMatcher : public LCallInstructionHelper<BOX_PIECES, 4, 0>
+{
+  public:
+    LIR_HEADER(RegExpMatcher)
+
+    LRegExpMatcher(const LAllocation& regexp, const LAllocation& string,
+                   const LAllocation& lastIndex, const LAllocation& sticky)
     {
         setOperand(0, regexp);
         setOperand(1, string);
+        setOperand(2, lastIndex);
+        setOperand(3, sticky);
     }
 
     const LAllocation* regexp() {
         return getOperand(0);
     }
     const LAllocation* string() {
         return getOperand(1);
     }
-
-    const MRegExpExec* mir() const {
-        return mir_->toRegExpExec();
-    }
-};
-
-class LRegExpTest : public LCallInstructionHelper<1, 2, 0>
-{
-  public:
-    LIR_HEADER(RegExpTest)
-
-    LRegExpTest(const LAllocation& regexp, const LAllocation& string)
+    const LAllocation* lastIndex() {
+        return getOperand(2);
+    }
+    const LAllocation* sticky() {
+        return getOperand(3);
+    }
+
+    const MRegExpMatcher* mir() const {
+        return mir_->toRegExpMatcher();
+    }
+};
+
+class LRegExpTester : public LCallInstructionHelper<1, 4, 0>
+{
+  public:
+    LIR_HEADER(RegExpTester)
+
+    LRegExpTester(const LAllocation& regexp, const LAllocation& string,
+                  const LAllocation& lastIndex, const LAllocation& sticky)
     {
         setOperand(0, regexp);
         setOperand(1, string);
+        setOperand(2, lastIndex);
+        setOperand(3, sticky);
     }
 
     const LAllocation* regexp() {
         return getOperand(0);
     }
     const LAllocation* string() {
         return getOperand(1);
     }
-
-    const MRegExpTest* mir() const {
-        return mir_->toRegExpTest();
+    const LAllocation* lastIndex() {
+        return getOperand(2);
+    }
+    const LAllocation* sticky() {
+        return getOperand(3);
+    }
+
+    const MRegExpTester* mir() const {
+        return mir_->toRegExpTester();
     }
 };
 
 
 class LStrReplace : public LCallInstructionHelper<1, 3, 0>
 {
   public:
     LStrReplace(const LAllocation& string, const LAllocation& pattern,
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -187,18 +187,18 @@
     _(Float32x4ToInt32x4)           \
     _(Start)                        \
     _(OsrEntry)                     \
     _(OsrValue)                     \
     _(OsrScopeChain)                \
     _(OsrReturnValue)               \
     _(OsrArgumentsObject)           \
     _(RegExp)                       \
-    _(RegExpExec)                   \
-    _(RegExpTest)                   \
+    _(RegExpMatcher)                \
+    _(RegExpTester)                 \
     _(RegExpReplace)                \
     _(StringReplace)                \
     _(Substr)                       \
     _(BinarySharedStub)             \
     _(UnarySharedStub)              \
     _(Lambda)                       \
     _(LambdaArrow)                  \
     _(LambdaForSingleton)           \
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -157,16 +157,28 @@ static MOZ_CONSTEXPR_VAR Register AsmJSI
 
 // Registers used in the GenerateFFIIonExit Disable Activation block.
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnData = ecx;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnType = ecx;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = rax;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = rdi;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = rbx;
 
+// Registerd used in RegExpMatcher instruction (do not use JSReturnOperand).
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherRegExpReg = CallTempReg0;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStringReg = CallTempReg1;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStickyReg = CallTempReg4;
+
+// Registerd used in RegExpTester instruction (do not use ReturnReg).
+static MOZ_CONSTEXPR_VAR Register RegExpTesterRegExpReg = CallTempReg1;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStringReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterLastIndexReg = CallTempReg3;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStickyReg = CallTempReg4;
+
 class ABIArgGenerator
 {
 #if defined(XP_WIN)
     unsigned regIndex_;
 #else
     unsigned intRegIndex_;
     unsigned floatRegIndex_;
 #endif
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -98,16 +98,28 @@ static MOZ_CONSTEXPR_VAR Register AsmJSI
 
 // Registers used in the GenerateFFIIonExit Disable Activation block.
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnData = edx;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnType = ecx;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = edi;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = eax;
 static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = esi;
 
+// Registerd used in RegExpMatcher instruction (do not use JSReturnOperand).
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherRegExpReg = CallTempReg0;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStringReg = CallTempReg1;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherLastIndexReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpMatcherStickyReg = CallTempReg4;
+
+// Registerd used in RegExpTester instruction (do not use ReturnReg).
+static MOZ_CONSTEXPR_VAR Register RegExpTesterRegExpReg = CallTempReg0;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStringReg = CallTempReg2;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterLastIndexReg = CallTempReg3;
+static MOZ_CONSTEXPR_VAR Register RegExpTesterStickyReg = CallTempReg4;
+
 // GCC stack is aligned on 16 bytes. Ion does not maintain this for internal
 // calls. asm.js code does.
 #if defined(__GNUC__)
 static MOZ_CONSTEXPR_VAR uint32_t ABIStackAlignment = 16;
 #else
 static MOZ_CONSTEXPR_VAR uint32_t ABIStackAlignment = 4;
 #endif
 static MOZ_CONSTEXPR_VAR uint32_t CodeAlignment = 16;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1516,17 +1516,17 @@ GCRuntime::getParameter(JSGCParamKey key
         return uint32_t(fullChunks(lock).count() +
                         availableChunks(lock).count() +
                         emptyChunks(lock).count());
       case JSGC_SLICE_TIME_BUDGET:
         if (defaultTimeBudget_ == SliceBudget::UnlimitedTimeBudget) {
             return 0;
         } else {
             MOZ_RELEASE_ASSERT(defaultTimeBudget_ >= 0);
-            MOZ_RELEASE_ASSERT(defaultTimeBudget_ < UINT32_MAX);
+            MOZ_RELEASE_ASSERT(defaultTimeBudget_ <= UINT32_MAX);
             return uint32_t(defaultTimeBudget_);
         }
       case JSGC_MARK_STACK_LIMIT:
         return marker.maxCapacity();
       case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
         return tunables.highFrequencyThresholdUsec() / PRMJ_USEC_PER_MSEC;
       case JSGC_HIGH_FREQUENCY_LOW_LIMIT:
         return tunables.highFrequencyLowLimitBytes() / 1024 / 1024;
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -2251,17 +2251,18 @@ class MOZ_STACK_CLASS StringRegExpGuard
 
 } /* anonymous namespace */
 
 static bool
 DoMatchLocal(JSContext* cx, const CallArgs& args, RegExpStatics* res, HandleLinearString input,
              RegExpShared& re)
 {
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
-    RegExpRunStatus status = re.execute(cx, input, 0, &matches);
+    bool sticky = re.sticky();
+    RegExpRunStatus status = re.execute(cx, input, 0, sticky, &matches, nullptr);
     if (status == RegExpRunStatus_Error)
         return false;
 
     if (status == RegExpRunStatus_Success_NotFound) {
         args.rval().setNull();
         return true;
     }
 
@@ -2362,22 +2363,23 @@ DoMatchGlobal(JSContext* cx, const CallA
     // The loop variables from steps 8c-e aren't needed, as we use different
     // techniques from the spec to implement step 8f's loop.
 
     // Step 8f.
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
     size_t charsLen = input->length();
     RegExpShared& re = g.regExp();
     bool unicode = re.unicode();
+    bool sticky = re.sticky();
     for (size_t searchIndex = 0; searchIndex <= charsLen; ) {
         if (!CheckForInterrupt(cx))
             return false;
 
         // Steps 8f(i-ii), minus "lastIndex" updates (see above).
-        RegExpRunStatus status = re.execute(cx, input, searchIndex, &matches);
+        RegExpRunStatus status = re.execute(cx, input, searchIndex, sticky, &matches, nullptr);
         if (status == RegExpRunStatus_Error)
             return false;
 
         // Step 8f(ii).
         if (status == RegExpRunStatus_Success_NotFound)
             break;
 
         lastSuccessfulStart = searchIndex;
@@ -2400,17 +2402,17 @@ DoMatchGlobal(JSContext* cx, const CallA
     if (elements.empty()) {
         args.rval().setNull();
         return true;
     }
 
     // The last *successful* match updates the RegExpStatics. (Interestingly,
     // this implies that String.prototype.match's semantics aren't those
     // implied by the RegExp.prototype.exec calls in the ES5 algorithm.)
-    res->updateLazily(cx, input, &re, lastSuccessfulStart);
+    res->updateLazily(cx, input, &re, lastSuccessfulStart, sticky);
 
     // Steps 8b, 8f(iii)(5-6), 8h.
     JSObject* array = NewDenseCopiedArray(cx, elements.length(), elements.begin());
     if (!array)
         return false;
 
     args.rval().setObject(*array);
     return true;
@@ -2530,22 +2532,24 @@ js::str_search(JSContext* cx, unsigned a
         return false;
 
     RegExpStatics* res = cx->global()->getRegExpStatics(cx);
     if (!res)
         return false;
 
     /* Per ECMAv5 15.5.4.12 (5) The last index property is ignored and left unchanged. */
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
-    RegExpRunStatus status = g.regExp().execute(cx, linearStr, 0, &matches);
+    RegExpShared& re = g.regExp();
+    bool sticky = re.sticky();
+    RegExpRunStatus status = re.execute(cx, linearStr, 0, sticky, &matches, nullptr);
     if (status == RegExpRunStatus_Error)
         return false;
 
     if (status == RegExpRunStatus_Success)
-        res->updateLazily(cx, linearStr, &g.regExp(), 0);
+        res->updateLazily(cx, linearStr, &re, 0, sticky);
 
     args.rval().setInt32(status == RegExpRunStatus_Success_NotFound ? -1 : matches[0].start);
     return true;
 }
 
 // Utility for building a rope (lazy concatenation) of strings.
 class RopeBuilder {
     JSContext* cx;
@@ -2627,17 +2631,18 @@ struct ReplaceData
 static bool
 ReplaceRegExp(JSContext* cx, RegExpStatics* res, ReplaceData& rdata);
 
 static bool
 DoMatchForReplaceLocal(JSContext* cx, RegExpStatics* res, HandleLinearString linearStr,
                        RegExpShared& re, ReplaceData& rdata, size_t* rightContextOffset)
 {
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
-    RegExpRunStatus status = re.execute(cx, linearStr, 0, &matches);
+    bool sticky = re.sticky();
+    RegExpRunStatus status = re.execute(cx, linearStr, 0, sticky, &matches, nullptr);
     if (status == RegExpRunStatus_Error)
         return false;
 
     if (status == RegExpRunStatus_Success_NotFound)
         return true;
 
     MatchPair& match = matches[0];
     *rightContextOffset = match.limit;
@@ -2648,23 +2653,24 @@ DoMatchForReplaceLocal(JSContext* cx, Re
     return ReplaceRegExp(cx, res, rdata);
 }
 
 static bool
 DoMatchForReplaceGlobal(JSContext* cx, RegExpStatics* res, HandleLinearString linearStr,
                         RegExpShared& re, ReplaceData& rdata, size_t* rightContextOffset)
 {
     bool unicode = re.unicode();
+    bool sticky = re.sticky();
     size_t charsLen = linearStr->length();
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
     for (size_t count = 0, searchIndex = 0; searchIndex <= charsLen; ++count) {
         if (!CheckForInterrupt(cx))
             return false;
 
-        RegExpRunStatus status = re.execute(cx, linearStr, searchIndex, &matches);
+        RegExpRunStatus status = re.execute(cx, linearStr, searchIndex, sticky, &matches, nullptr);
         if (status == RegExpRunStatus_Error)
             return false;
 
         if (status == RegExpRunStatus_Success_NotFound)
             break;
 
         MatchPair& match = matches[0];
         searchIndex = match.isEmpty()
@@ -3265,21 +3271,22 @@ StrReplaceRegexpRemove(JSContext* cx, Ha
 
     ScopedMatchPairs matches(&cx->tempLifoAlloc());
     size_t startIndex = 0; /* Index used for iterating through the string. */
     size_t lastIndex = 0;  /* Index after last successful match. */
     size_t lazyIndex = 0;  /* Index before last successful match. */
 
     /* Accumulate StringRanges for unmatched substrings. */
     bool unicode = re.unicode();
+    bool sticky = re.sticky();
     while (startIndex <= charsLen) {
         if (!CheckForInterrupt(cx))
             return nullptr;
 
-        RegExpRunStatus status = re.execute(cx, linearStr, startIndex, &matches);
+        RegExpRunStatus status = re.execute(cx, linearStr, startIndex, sticky, &matches, nullptr);
         if (status == RegExpRunStatus_Error)
             return nullptr;
         if (status == RegExpRunStatus_Success_NotFound)
             break;
         MatchPair& match = matches[0];
 
         /* Include the latest unmatched substring. */
         if (size_t(match.start) > lastIndex) {
@@ -3302,28 +3309,28 @@ StrReplaceRegexpRemove(JSContext* cx, Ha
     RegExpStatics* res;
 
     /* If unmatched, return the input string. */
     if (!lastIndex) {
         if (startIndex > 0) {
             res = cx->global()->getRegExpStatics(cx);
             if (!res)
                 return nullptr;
-            res->updateLazily(cx, linearStr, &re, lazyIndex);
+            res->updateLazily(cx, linearStr, &re, lazyIndex, sticky);
         }
 
         return str;
     }
 
     /* The last successful match updates the RegExpStatics. */
     res = cx->global()->getRegExpStatics(cx);
     if (!res)
         return nullptr;
 
-    res->updateLazily(cx, linearStr, &re, lazyIndex);
+    res->updateLazily(cx, linearStr, &re, lazyIndex, sticky);
 
     /* Include any remaining part of the string. */
     if (lastIndex < charsLen) {
         if (!ranges.append(StringRange(lastIndex, charsLen - lastIndex)))
             return nullptr;
     }
 
     /* Handle the empty string before calling .begin(). */
@@ -3947,27 +3954,30 @@ namespace {
  *
  * The algorithm differs from the spec in that the we return the next index at
  * which a match happens.
  */
 class SplitRegExpMatcher
 {
     RegExpShared& re;
     RegExpStatics* res;
+    bool sticky;
 
   public:
-    SplitRegExpMatcher(RegExpShared& re, RegExpStatics* res) : re(re), res(res) {}
+    SplitRegExpMatcher(RegExpShared& re, RegExpStatics* res) : re(re), res(res) {
+        sticky = re.sticky();
+    }
 
     static const bool returnsCaptures = true;
 
     bool operator()(JSContext* cx, HandleLinearString str, size_t index,
                     SplitMatchResult* result) const
     {
         ScopedMatchPairs matches(&cx->tempLifoAlloc());
-        RegExpRunStatus status = re.execute(cx, str, index, &matches);
+        RegExpRunStatus status = re.execute(cx, str, index, sticky, &matches, nullptr);
         if (status == RegExpRunStatus_Error)
             return false;
 
         if (status == RegExpRunStatus_Success_NotFound) {
             result->setFailure();
             return true;
         }
 
--- a/js/src/tests/ecma_3/RegExp/15.10.6.2-2.js
+++ b/js/src/tests/ecma_3/RegExp/15.10.6.2-2.js
@@ -105,93 +105,54 @@ addThis();
  * At this point |lastIndex| is > string.length, so the match should be null -
  */
 status = inSection(4);
 actualmatch = pattern.exec(string);
 expectedmatch = null;
 addThis();
 
 /*
- * Now let's set |lastIndex| to -1, so the match should again be null -
- */
-status = inSection(5);
-pattern.lastIndex = -1;
-actualmatch = pattern.exec(string);
-expectedmatch = null;
-addThis();
-
-/*
  * Now try some edge-case values. Thanks to the work done in
  * http://bugzilla.mozilla.org/show_bug.cgi?id=124339, |lastIndex|
  * is now stored as a double instead of a uint32_t (unsigned integer).
  *
  * Note 2^32 -1 is the upper bound for uint32's, but doubles can go
  * all the way up to Number.MAX_VALUE. So that's why we need cases
  * between those two numbers.
  */
 status = inSection(6);
 pattern.lastIndex = Math.pow(2,32);
 actualmatch = pattern.exec(string);
 expectedmatch = null;
 addThis();
  
-status = inSection(7);
-pattern.lastIndex = -Math.pow(2,32);
-actualmatch = pattern.exec(string);
-expectedmatch = null;
-addThis();
-
 status = inSection(8);
 pattern.lastIndex = Math.pow(2,32) + 1;
 actualmatch = pattern.exec(string);
 expectedmatch = null;
 addThis();
 
-status = inSection(9);
-pattern.lastIndex = -(Math.pow(2,32) + 1);
-actualmatch = pattern.exec(string);
-expectedmatch = null;
-addThis();
-
 status = inSection(10);
 pattern.lastIndex = Math.pow(2,32) * 2;
 actualmatch = pattern.exec(string);
 expectedmatch = null;
 addThis();
 
-status = inSection(11);
-pattern.lastIndex = -Math.pow(2,32) * 2;
-actualmatch = pattern.exec(string);
-expectedmatch = null;
-addThis();
-
 status = inSection(12);
 pattern.lastIndex = Math.pow(2,40);
 actualmatch = pattern.exec(string);
 expectedmatch = null;
 addThis();
 
-status = inSection(13);
-pattern.lastIndex = -Math.pow(2,40);
-actualmatch = pattern.exec(string);
-expectedmatch = null;
-addThis();
-
 status = inSection(14);
 pattern.lastIndex = Number.MAX_VALUE;
 actualmatch = pattern.exec(string);
 expectedmatch = null;
 addThis();
 
-status = inSection(15);
-pattern.lastIndex = -Number.MAX_VALUE;
-actualmatch = pattern.exec(string);
-expectedmatch = null;
-addThis();
- 
 
 
 /******************************************************************************
  *
  * Case 2: repeat all the above cases WITHOUT the global flag set.
  * According to EMCA. |lastIndex| should get set to 0 before the match.
  *
  * Therefore re.exec(str) should be unaffected; thus our expected values
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/RegExp/exec-lastIndex-negative.js
@@ -0,0 +1,27 @@
+var BUGNUMBER = 1207922;
+var summary = "negative lastIndex should be treated as 0.";
+
+print(BUGNUMBER + ": " + summary);
+
+var pattern = /abc/gi;
+var string = 'AbcaBcabC';
+
+var indices = [
+    -1,
+    -Math.pow(2,32),
+    -(Math.pow(2,32) + 1),
+    -Math.pow(2,32) * 2,
+    -Math.pow(2,40),
+    -Number.MAX_VALUE,
+];
+for (var index of indices) {
+  pattern.lastIndex = index;
+  var result = pattern.exec(string);
+  assertEq(result.index, 0);
+  assertEq(result.length, 1);
+  assertEq(result[0], "Abc");
+  assertEq(pattern.lastIndex, 3);
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/vm/NativeObject.cpp
+++ b/js/src/vm/NativeObject.cpp
@@ -129,17 +129,17 @@ js::NativeObject::checkShapeConsistency(
              fslot = getSlot(fslot).toPrivateUint32())
         {
             MOZ_ASSERT(fslot < slotSpan());
         }
 
         for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) {
             MOZ_ASSERT_IF(lastProperty() != shape, !shape->hasTable());
 
-            ShapeTable::Entry& entry = table.search(shape->propid(), false);
+            ShapeTable::Entry& entry = table.search<MaybeAdding::NotAdding>(shape->propid());
             MOZ_ASSERT(entry.shape() == shape);
         }
 
         shape = lastProperty();
         for (int n = throttle; --n >= 0 && shape; shape = shape->parent) {
             MOZ_ASSERT_IF(shape->slot() != SHAPE_INVALID_SLOT, shape->slot() < slotSpan());
             if (!prev) {
                 MOZ_ASSERT(lastProperty() == shape);
@@ -150,17 +150,17 @@ js::NativeObject::checkShapeConsistency(
             prev = shape;
         }
     } else {
         for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) {
             if (shape->hasTable()) {
                 ShapeTable& table = shape->table();
                 MOZ_ASSERT(shape->parent);
                 for (Shape::Range<NoGC> r(shape); !r.empty(); r.popFront()) {
-                    ShapeTable::Entry& entry = table.search(r.front().propid(), false);
+                    ShapeTable::Entry& entry = table.search<MaybeAdding::NotAdding>(r.front().propid());
                     MOZ_ASSERT(entry.shape() == &r.front());
                 }
             }
             if (prev) {
                 MOZ_ASSERT(prev->maybeSlot() >= shape->maybeSlot());
                 shape->kids.checkConsistency(prev);
             }
             prev = shape;
@@ -351,17 +351,17 @@ NativeObject::setLastPropertyMakeNonNati
 
     shape_ = shape;
 }
 
 void
 NativeObject::setLastPropertyMakeNative(ExclusiveContext* cx, Shape* shape)
 {
     MOZ_ASSERT(getClass()->isNative());
-    MOZ_ASSERT(shape->isNative());
+    MOZ_ASSERT(shape->getObjectClass()->isNative());
     MOZ_ASSERT(!shape->inDictionary());
 
     // This method is used to convert unboxed objects into native objects. In
     // this case, the shape_ field was previously used to store other data and
     // this should be treated as an initialization.
     shape_.init(shape);
 
     slots_ = nullptr;
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -533,17 +533,17 @@ ObjectGroup::defaultNewGroup(ExclusiveCo
         MOZ_ASSERT_IF(clasp, group->clasp() == clasp);
         MOZ_ASSERT_IF(!clasp, group->clasp() == &PlainObject::class_ ||
                               group->clasp() == &UnboxedPlainObject::class_);
         MOZ_ASSERT(group->proto() == proto);
         return group;
     }
 
     ObjectGroupFlags initialFlags = 0;
-    if (!proto.isObject() || proto.toObject()->isNewGroupUnknown())
+    if (proto.isLazy() || (proto.isObject() && proto.toObject()->isNewGroupUnknown()))
         initialFlags = OBJECT_FLAG_DYNAMIC_MASK;
 
     Rooted<TaggedProto> protoRoot(cx, proto);
     ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, clasp ? clasp : &PlainObject::class_,
                                                            protoRoot, initialFlags);
     if (!group)
         return nullptr;
 
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -493,28 +493,28 @@ RegExpShared::trace(JSTracer* trc)
         RegExpCompilation& compilation = compilationArray[i];
         if (compilation.jitCode)
             TraceEdge(trc, &compilation.jitCode, "RegExpShared code");
     }
 }
 
 bool
 RegExpShared::compile(JSContext* cx, HandleLinearString input,
-                      CompilationMode mode, ForceByteCodeEnum force)
+                      CompilationMode mode, bool sticky, ForceByteCodeEnum force)
 {
     TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
     AutoTraceLog logCompile(logger, TraceLogger_IrregexpCompile);
 
     RootedAtom pattern(cx, source);
-    return compile(cx, pattern, input, mode, force);
+    return compile(cx, pattern, input, mode, sticky, force);
 }
 
 bool
 RegExpShared::compile(JSContext* cx, HandleAtom pattern, HandleLinearString input,
-                      CompilationMode mode, ForceByteCodeEnum force)
+                      CompilationMode mode, bool sticky, ForceByteCodeEnum force)
 {
     if (!ignoreCase() && !StringHasRegExpMetaChars(pattern))
         canStringMatch = true;
 
     CompileOptions options(cx);
     TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr);
 
     LifoAllocScope scope(&cx->tempLifoAlloc());
@@ -530,51 +530,53 @@ RegExpShared::compile(JSContext* cx, Han
     this->parenCount = data.capture_count;
 
     irregexp::RegExpCode code = irregexp::CompilePattern(cx, this, &data, input,
                                                          false /* global() */,
                                                          ignoreCase(),
                                                          input->hasLatin1Chars(),
                                                          mode == MatchOnly,
                                                          force == ForceByteCode,
-                                                         sticky(), unicode());
+                                                         sticky, unicode());
     if (code.empty())
         return false;
 
     MOZ_ASSERT(!code.jitCode || !code.byteCode);
     MOZ_ASSERT_IF(force == ForceByteCode, code.byteCode);
 
-    RegExpCompilation& compilation = this->compilation(mode, input->hasLatin1Chars());
+    RegExpCompilation& compilation = this->compilation(mode, sticky, input->hasLatin1Chars());
     if (code.jitCode)
         compilation.jitCode = code.jitCode;
     else if (code.byteCode)
         compilation.byteCode = code.byteCode;
 
     return true;
 }
 
 bool
 RegExpShared::compileIfNecessary(JSContext* cx, HandleLinearString input,
-                                 CompilationMode mode, ForceByteCodeEnum force)
+                                 CompilationMode mode, bool sticky, ForceByteCodeEnum force)
 {
-    if (isCompiled(mode, input->hasLatin1Chars(), force))
+    if (isCompiled(mode, sticky, input->hasLatin1Chars(), force))
         return true;
-    return compile(cx, input, mode, force);
+    return compile(cx, input, mode, sticky, force);
 }
 
 RegExpRunStatus
-RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start,
-                      MatchPairs* matches)
+RegExpShared::execute(JSContext* cx, HandleLinearString input, size_t start, bool sticky,
+                      MatchPairs* matches, size_t* endIndex)
 {
+    MOZ_ASSERT_IF(matches, !endIndex);
+    MOZ_ASSERT_IF(!matches, endIndex);
     TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
 
     CompilationMode mode = matches ? Normal : MatchOnly;
 
     /* Compile the code at point-of-use. */
-    if (!compileIfNecessary(cx, input, mode, DontForceByteCode))
+    if (!compileIfNecessary(cx, input, mode, sticky, DontForceByteCode))
         return RegExpRunStatus_Error;
 
     /*
      * Ensure sufficient memory for output vector.
      * No need to initialize it. The RegExp engine fills them in on a match.
      */
     if (matches && !matches->allocOrExpandArray(pairCount())) {
         ReportOutOfMemory(cx);
@@ -584,60 +586,64 @@ RegExpShared::execute(JSContext* cx, Han
     size_t length = input->length();
 
     // Reset the Irregexp backtrack stack if it grows during execution.
     irregexp::RegExpStackScope stackScope(cx->runtime());
 
     if (canStringMatch) {
         MOZ_ASSERT(pairCount() == 1);
         size_t sourceLength = source->length();
-        if (sticky()) {
+        if (sticky) {
             // First part checks size_t overflow.
             if (sourceLength + start < sourceLength || sourceLength + start > length)
                 return RegExpRunStatus_Success_NotFound;
             if (!HasSubstringAt(input, source, start))
                 return RegExpRunStatus_Success_NotFound;
 
             if (matches) {
                 (*matches)[0].start = start;
                 (*matches)[0].limit = start + sourceLength;
 
                 matches->checkAgainst(length);
+            } else if (endIndex) {
+                *endIndex = start + sourceLength;
             }
             return RegExpRunStatus_Success;
         }
 
         int res = StringFindPattern(input, source, start);
         if (res == -1)
             return RegExpRunStatus_Success_NotFound;
 
         if (matches) {
             (*matches)[0].start = res;
             (*matches)[0].limit = res + sourceLength;
 
             matches->checkAgainst(length);
+        } else if (endIndex) {
+            *endIndex = res + sourceLength;
         }
         return RegExpRunStatus_Success;
     }
 
     do {
-        jit::JitCode* code = compilation(mode, input->hasLatin1Chars()).jitCode;
+        jit::JitCode* code = compilation(mode, sticky, input->hasLatin1Chars()).jitCode;
         if (!code)
             break;
 
         RegExpRunStatus result;
         {
             AutoTraceLog logJIT(logger, TraceLogger_IrregexpExecute);
             AutoCheckCannotGC nogc;
             if (input->hasLatin1Chars()) {
                 const Latin1Char* chars = input->latin1Chars(nogc);
-                result = irregexp::ExecuteCode(cx, code, chars, start, length, matches);
+                result = irregexp::ExecuteCode(cx, code, chars, start, length, matches, endIndex);
             } else {
                 const char16_t* chars = input->twoByteChars(nogc);
-                result = irregexp::ExecuteCode(cx, code, chars, start, length, matches);
+                result = irregexp::ExecuteCode(cx, code, chars, start, length, matches, endIndex);
             }
         }
 
         if (result == RegExpRunStatus_Error) {
             // An 'Error' result is returned if a stack overflow guard or
             // interrupt guard failed. If CheckOverRecursed doesn't throw, break
             // out and retry the regexp in the bytecode interpreter, which can
             // execute while tolerating future interrupts. Otherwise, if we keep
@@ -653,33 +659,33 @@ RegExpShared::execute(JSContext* cx, Han
         MOZ_ASSERT(result == RegExpRunStatus_Success);
 
         if (matches)
             matches->checkAgainst(length);
         return RegExpRunStatus_Success;
     } while (false);
 
     // Compile bytecode for the RegExp if necessary.
-    if (!compileIfNecessary(cx, input, mode, ForceByteCode))
+    if (!compileIfNecessary(cx, input, mode, sticky, ForceByteCode))
         return RegExpRunStatus_Error;
 
-    uint8_t* byteCode = compilation(mode, input->hasLatin1Chars()).byteCode;
+    uint8_t* byteCode = compilation(mode, sticky, input->hasLatin1Chars()).byteCode;
     AutoTraceLog logInterpreter(logger, TraceLogger_IrregexpExecute);
 
     AutoStableStringChars inputChars(cx);
     if (!inputChars.init(cx, input))
         return RegExpRunStatus_Error;
 
     RegExpRunStatus result;
     if (inputChars.isLatin1()) {
         const Latin1Char* chars = inputChars.latin1Range().start().get();
-        result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches);
+        result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches, endIndex);
     } else {
         const char16_t* chars = inputChars.twoByteRange().start().get();
-        result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches);
+        result = irregexp::InterpretCode(cx, byteCode, chars, start, length, matches, endIndex);
     }
 
     if (result == RegExpRunStatus_Success && matches)
         matches->checkAgainst(length);
     return result;
 }
 
 size_t
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -122,54 +122,54 @@ class RegExpShared
     /* Source to the RegExp, for lazy compilation. */
     RelocatablePtrAtom source;
 
     RegExpFlag         flags;
     size_t             parenCount;
     bool               canStringMatch;
     bool               marked_;
 
-    RegExpCompilation  compilationArray[4];
+    RegExpCompilation  compilationArray[8];
 
-    static int CompilationIndex(CompilationMode mode, bool latin1) {
+    static int CompilationIndex(CompilationMode mode, bool sticky, bool latin1) {
         switch (mode) {
-          case Normal:    return latin1 ? 0 : 1;
-          case MatchOnly: return latin1 ? 2 : 3;
+          case Normal:    return sticky ? (latin1 ? 0 : 1) : (latin1 ? 2 : 3);
+          case MatchOnly: return sticky ? (latin1 ? 4 : 5) : (latin1 ? 6 : 7);
         }
         MOZ_CRASH();
     }
 
     // Tables referenced by JIT code.
     Vector<uint8_t*, 0, SystemAllocPolicy> tables;
 
     /* Internal functions. */
     bool compile(JSContext* cx, HandleLinearString input,
-                 CompilationMode mode, ForceByteCodeEnum force);
+                 CompilationMode mode, bool sticky, ForceByteCodeEnum force);
     bool compile(JSContext* cx, HandleAtom pattern, HandleLinearString input,
-                 CompilationMode mode, ForceByteCodeEnum force);
+                 CompilationMode mode, bool sticky, ForceByteCodeEnum force);
 
     bool compileIfNecessary(JSContext* cx, HandleLinearString input,
-                            CompilationMode mode, ForceByteCodeEnum force);
+                            CompilationMode mode, bool sticky, ForceByteCodeEnum force);
 
-    const RegExpCompilation& compilation(CompilationMode mode, bool latin1) const {
-        return compilationArray[CompilationIndex(mode, latin1)];
+    const RegExpCompilation& compilation(CompilationMode mode, bool sticky, bool latin1) const {
+        return compilationArray[CompilationIndex(mode, sticky, latin1)];
     }
 
-    RegExpCompilation& compilation(CompilationMode mode, bool latin1) {
-        return compilationArray[CompilationIndex(mode, latin1)];
+    RegExpCompilation& compilation(CompilationMode mode, bool sticky, bool latin1) {
+        return compilationArray[CompilationIndex(mode, sticky, latin1)];
     }
 
   public:
     RegExpShared(JSAtom* source, RegExpFlag flags);
     ~RegExpShared();
 
     // Execute this RegExp on input starting from searchIndex, filling in
     // matches if specified and otherwise only determining if there is a match.
     RegExpRunStatus execute(JSContext* cx, HandleLinearString input, size_t searchIndex,
-                            MatchPairs* matches);
+                            bool sticky, MatchPairs* matches, size_t* endIndex);
 
     // Register a table with this RegExpShared, and take ownership.
     bool addTable(uint8_t* table) {
         return tables.append(table);
     }
 
     /* Accessors */
 
@@ -184,23 +184,25 @@ class RegExpShared
     JSAtom* getSource() const           { return source; }
     RegExpFlag getFlags() const         { return flags; }
     bool ignoreCase() const             { return flags & IgnoreCaseFlag; }
     bool global() const                 { return flags & GlobalFlag; }
     bool multiline() const              { return flags & MultilineFlag; }
     bool sticky() const                 { return flags & StickyFlag; }
     bool unicode() const                { return flags & UnicodeFlag; }
 
-    bool isCompiled(CompilationMode mode, bool latin1,
+    bool isCompiled(CompilationMode mode, bool sticky, bool latin1,
                     ForceByteCodeEnum force = DontForceByteCode) const {
-        return compilation(mode, latin1).compiled(force);
+        return compilation(mode, sticky, latin1).compiled(force);
     }
     bool isCompiled() const {
-        return isCompiled(Normal, true) || isCompiled(Normal, false)
-            || isCompiled(MatchOnly, true) || isCompiled(MatchOnly, false);
+        return isCompiled(Normal, true, true) || isCompiled(Normal, true, false)
+            || isCompiled(Normal, false, true) || isCompiled(Normal, false, false)
+            || isCompiled(MatchOnly, true, true) || isCompiled(MatchOnly, true, false)
+            || isCompiled(MatchOnly, false, true) || isCompiled(MatchOnly, false, false);
     }
 
     void trace(JSTracer* trc);
 
     bool marked() const { return marked_; }
     void clearMarked() { marked_ = false; }
 
     static size_t offsetOfSource() {
@@ -210,19 +212,34 @@ class RegExpShared
     static size_t offsetOfFlags() {
         return offsetof(RegExpShared, flags);
     }
 
     static size_t offsetOfParenCount() {
         return offsetof(RegExpShared, parenCount);
     }
 
-    static size_t offsetOfJitCode(CompilationMode mode, bool latin1) {
+    static size_t offsetOfStickyLatin1JitCode(CompilationMode mode) {
+        return offsetof(RegExpShared, compilationArray)
+             + (CompilationIndex(mode, true, true) * sizeof(RegExpCompilation))
+             + offsetof(RegExpCompilation, jitCode);
+    }
+    static size_t offsetOfNotStickyLatin1JitCode(CompilationMode mode) {
         return offsetof(RegExpShared, compilationArray)
-             + (CompilationIndex(mode, latin1) * sizeof(RegExpCompilation))
+             + (CompilationIndex(mode, false, true) * sizeof(RegExpCompilation))
+             + offsetof(RegExpCompilation, jitCode);
+    }
+    static size_t offsetOfStickyTwoByteJitCode(CompilationMode mode) {
+        return offsetof(RegExpShared, compilationArray)
+             + (CompilationIndex(mode, true, false) * sizeof(RegExpCompilation))
+             + offsetof(RegExpCompilation, jitCode);
+    }
+    static size_t offsetOfNotStickyTwoByteJitCode(CompilationMode mode) {
+        return offsetof(RegExpShared, compilationArray)
+             + (CompilationIndex(mode, false, false) * sizeof(RegExpCompilation))
              + offsetof(RegExpCompilation, jitCode);
     }
 
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
 };
 
 /*
  * Extend the lifetime of a given RegExpShared to at least the lifetime of
--- a/js/src/vm/RegExpStatics.cpp
+++ b/js/src/vm/RegExpStatics.cpp
@@ -96,17 +96,17 @@ RegExpStatics::executeLazy(JSContext* cx
 
     /*
      * It is not necessary to call aboutToWrite(): evaluation of
      * implicit copies is safe.
      */
 
     /* Execute the full regular expression. */
     RootedLinearString input(cx, matchesInput);
-    RegExpRunStatus status = g->execute(cx, input, lazyIndex, &this->matches);
+    RegExpRunStatus status = g->execute(cx, input, lazyIndex, lazySticky, &this->matches, nullptr);
     if (status == RegExpRunStatus_Error)
         return false;
 
     /*
      * RegExpStatics are only updated on successful (matching) execution.
      * Re-running the same expression must therefore produce a matching result.
      */
     MOZ_ASSERT(status == RegExpRunStatus_Success);
--- a/js/src/vm/RegExpStatics.h
+++ b/js/src/vm/RegExpStatics.h
@@ -26,16 +26,17 @@ class RegExpStatics
     /*
      * The previous RegExp input, used to resolve lazy state.
      * A raw RegExpShared cannot be stored because it may be in
      * a different compartment via evalcx().
      */
     RelocatablePtrAtom      lazySource;
     RegExpFlag              lazyFlags;
     size_t                  lazyIndex;
+    bool                    lazySticky;
 
     /* The latest RegExp input, set before execution. */
     RelocatablePtrString    pendingInput;
     RegExpFlag              flags;
 
     /*
      * If non-zero, |matchesInput| and the |lazy*| fields may be used
      * to replay the last executed RegExp, and |matches| is invalid.
@@ -61,17 +62,17 @@ class RegExpStatics
     void markFlagsSet(JSContext* cx);
 
     struct InitBuffer {};
     explicit RegExpStatics(InitBuffer) {}
 
   public:
     /* Mutators. */
     inline void updateLazily(JSContext* cx, JSLinearString* input,
-                             RegExpShared* shared, size_t lastIndex);
+                             RegExpShared* shared, size_t lastIndex, bool sticky);
     inline bool updateFromMatchPairs(JSContext* cx, JSLinearString* input, MatchPairs& newPairs);
 
     void setMultiline(JSContext* cx, bool enabled) {
         if (enabled) {
             flags = RegExpFlag(flags | MultilineFlag);
             markFlagsSet(cx);
         } else {
             flags = RegExpFlag(flags & ~MultilineFlag);
@@ -148,16 +149,20 @@ class RegExpStatics
     static size_t offsetOfLazyFlags() {
         return offsetof(RegExpStatics, lazyFlags);
     }
 
     static size_t offsetOfLazyIndex() {
         return offsetof(RegExpStatics, lazyIndex);
     }
 
+    static size_t offsetOfLazySticky() {
+        return offsetof(RegExpStatics, lazySticky);
+    }
+
     static size_t offsetOfPendingLazyEvaluation() {
         return offsetof(RegExpStatics, pendingLazyEvaluation);
     }
 };
 
 inline bool
 RegExpStatics::createDependent(JSContext* cx, size_t start, size_t end, MutableHandleValue out)
 {
@@ -336,27 +341,28 @@ RegExpStatics::getRightContext(JSSubStri
     }
     MOZ_ASSERT(matches[0].limit <= int(matchesInput->length()));
     size_t length = matchesInput->length() - matches[0].limit;
     out->init(matchesInput, matches[0].limit, length);
 }
 
 inline void
 RegExpStatics::updateLazily(JSContext* cx, JSLinearString* input,
-                            RegExpShared* shared, size_t lastIndex)
+                            RegExpShared* shared, size_t lastIndex, bool sticky)
 {
     MOZ_ASSERT(input && shared);
 
     BarrieredSetPair<JSString, JSLinearString>(cx->zone(),
                                                pendingInput, input,
                                                matchesInput, input);
 
     lazySource = shared->source;
     lazyFlags = shared->flags;
     lazyIndex = lastIndex;
+    lazySticky = sticky;
     pendingLazyEvaluation = 1;
 }
 
 inline bool
 RegExpStatics::updateFromMatchPairs(JSContext* cx, JSLinearString* input, MatchPairs& newPairs)
 {
     MOZ_ASSERT(input);
 
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -1002,17 +1002,17 @@ StaticBlockObject::addVar(ExclusiveConte
 {
     MOZ_ASSERT(JSID_IS_ATOM(id));
     MOZ_ASSERT(index < LOCAL_INDEX_LIMIT);
 
     *redeclared = false;
 
     /* Inline NativeObject::addProperty in order to trap the redefinition case. */
     ShapeTable::Entry* entry;
-    if (Shape::search(cx, block->lastProperty(), id, &entry, true)) {
+    if (Shape::search<MaybeAdding::Adding>(cx, block->lastProperty(), id, &entry)) {
         *redeclared = true;
         return nullptr;
     }
 
     /*
      * Don't convert this object to dictionary mode so that we can clone the
      * block's shape later.
      */
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -1691,16 +1691,26 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("intl_DateTimeFormat_availableLocales", intl_DateTimeFormat_availableLocales, 0,0),
     JS_FN("intl_FormatDateTime", intl_FormatDateTime, 2,0),
     JS_FN("intl_FormatNumber", intl_FormatNumber, 2,0),
     JS_FN("intl_NumberFormat", intl_NumberFormat, 2,0),
     JS_FN("intl_NumberFormat_availableLocales", intl_NumberFormat_availableLocales, 0,0),
     JS_FN("intl_numberingSystem", intl_numberingSystem, 1,0),
     JS_FN("intl_patternForSkeleton", intl_patternForSkeleton, 2,0),
 
+    JS_INLINABLE_FN("IsRegExpObject",
+                    intrinsic_IsInstanceOfBuiltin<RegExpObject>, 1,0,
+                    IsRegExpObject),
+    JS_FN("CallRegExpMethodIfWrapped",
+          CallNonGenericSelfhostedMethod<Is<RegExpObject>>, 2,0),
+    JS_INLINABLE_FN("RegExpMatcher", RegExpMatcher, 5,0,
+                    RegExpMatcher),
+    JS_INLINABLE_FN("RegExpTester", RegExpTester, 5,0,
+                    RegExpTester),
+
     // See builtin/RegExp.h for descriptions of the regexp_* functions.
     JS_FN("regexp_exec_no_statics", regexp_exec_no_statics, 2,0),
     JS_FN("regexp_test_no_statics", regexp_test_no_statics, 2,0),
     JS_FN("regexp_construct_no_statics", regexp_construct_no_statics, 2,0),
 
     JS_FN("IsModule", intrinsic_IsInstanceOfBuiltin<ModuleObject>, 1, 0),
     JS_FN("CallModuleMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<ModuleObject>>, 2, 0),
--- a/js/src/vm/Shape-inl.h
+++ b/js/src/vm/Shape-inl.h
@@ -32,35 +32,36 @@ StackBaseShape::StackBaseShape(Exclusive
 
 inline Shape*
 Shape::search(ExclusiveContext* cx, jsid id)
 {
     ShapeTable::Entry* _;
     return search(cx, this, id, &_);
 }
 
+template<MaybeAdding Adding>
 /* static */ inline Shape*
-Shape::search(ExclusiveContext* cx, Shape* start, jsid id, ShapeTable::Entry** pentry, bool adding)
+Shape::search(ExclusiveContext* cx, Shape* start, jsid id, ShapeTable::Entry** pentry)
 {
     if (start->inDictionary()) {
-        *pentry = &start->table().search(id, adding);
+        *pentry = &start->table().search<Adding>(id);
         return (*pentry)->shape();
     }
 
     *pentry = nullptr;
 
     if (start->hasTable()) {
-        ShapeTable::Entry& entry = start->table().search(id, adding);
+        ShapeTable::Entry& entry = start->table().search<Adding>(id);
         return entry.shape();
     }
 
     if (start->numLinearSearches() == LINEAR_SEARCHES_MAX) {
         if (start->isBigEnoughForAShapeTable()) {
             if (Shape::hashify(cx, start)) {
-                ShapeTable::Entry& entry = start->table().search(id, adding);
+                ShapeTable::Entry& entry = start->table().search<Adding>(id);
                 return entry.shape();
             } else {
                 cx->recoverFromOutOfMemory();
             }
         }
         /*
          * No table built -- there weren't enough entries, or OOM occurred.
          * Don't increment numLinearSearches, to keep hasTable() false.
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -55,17 +55,17 @@ ShapeTable::init(ExclusiveContext* cx, S
     if (!entries_)
         return false;
 
     MOZ_ASSERT(sizeLog2 <= HASH_BITS);
     hashShift_ = HASH_BITS - sizeLog2;
 
     for (Shape::Range<NoGC> r(lastProp); !r.empty(); r.popFront()) {
         Shape& shape = r.front();
-        Entry& entry = search(shape.propid(), true);
+        Entry& entry = search<MaybeAdding::Adding>(shape.propid());
 
         /*
          * Beware duplicate args and arg vs. var conflicts: the youngest shape
          * (nearest to lastProp) must win. See bug 600067.
          */
         if (!entry.shape())
             entry.setPreservingCollision(&shape);
     }
@@ -181,18 +181,19 @@ Hash1(HashNumber hash0, uint32_t shift)
 }
 
 static HashNumber
 Hash2(HashNumber hash0, uint32_t log2, uint32_t shift)
 {
     return ((hash0 << log2) >> shift) | 1;
 }
 
+template<MaybeAdding Adding>
 ShapeTable::Entry&
-ShapeTable::search(jsid id, bool adding)
+ShapeTable::search(jsid id)
 {
     MOZ_ASSERT(entries_);
     MOZ_ASSERT(!JSID_IS_EMPTY(id));
 
     /* Compute the primary hash address. */
     HashNumber hash0 = HashId(id);
     HashNumber hash1 = Hash1(hash0, hashShift_);
     Entry* entry = &getEntry(hash1);
@@ -206,62 +207,70 @@ ShapeTable::search(jsid id, bool adding)
     if (shape && shape->propidRaw() == id)
         return *entry;
 
     /* Collision: double hash. */
     uint32_t sizeLog2 = HASH_BITS - hashShift_;
     HashNumber hash2 = Hash2(hash0, sizeLog2, hashShift_);
     uint32_t sizeMask = JS_BITMASK(sizeLog2);
 
-#ifdef DEBUG
-    bool collisionFlag = true;
-#endif
-
     /* Save the first removed entry pointer so we can recycle it if adding. */
     Entry* firstRemoved;
-    if (entry->isRemoved()) {
-        firstRemoved = entry;
-    } else {
-        firstRemoved = nullptr;
-        if (adding && !entry->hadCollision())
-            entry->flagCollision();
+    if (Adding == MaybeAdding::Adding) {
+        if (entry->isRemoved()) {
+            firstRemoved = entry;
+        } else {
+            firstRemoved = nullptr;
+            if (!entry->hadCollision())
+                entry->flagCollision();
+        }
+    }
+
 #ifdef DEBUG
-        collisionFlag &= entry->hadCollision();
+    bool collisionFlag = true;
+    if (!entry->isRemoved())
+        collisionFlag = entry->hadCollision();
 #endif
-    }
 
     while (true) {
         hash1 -= hash2;
         hash1 &= sizeMask;
         entry = &getEntry(hash1);
 
         if (entry->isFree())
-            return (adding && firstRemoved) ? *firstRemoved : *entry;
+            return (Adding == MaybeAdding::Adding && firstRemoved) ? *firstRemoved : *entry;
 
         shape = entry->shape();
         if (shape && shape->propidRaw() == id) {
             MOZ_ASSERT(collisionFlag);
             return *entry;
         }
 
-        if (entry->isRemoved()) {
-            if (!firstRemoved)
-                firstRemoved = entry;
-        } else {
-            if (adding && !entry->hadCollision())
-                entry->flagCollision();
+        if (Adding == MaybeAdding::Adding) {
+            if (entry->isRemoved()) {
+                if (!firstRemoved)
+                    firstRemoved = entry;
+            } else {
+                if (!entry->hadCollision())
+                    entry->flagCollision();
+            }
+        }
+
 #ifdef DEBUG
+        if (!entry->isRemoved())
             collisionFlag &= entry->hadCollision();
 #endif
-        }
     }
 
     MOZ_CRASH("Shape::search failed to find an expected entry.");
 }
 
+template ShapeTable::Entry& ShapeTable::search<MaybeAdding::Adding>(jsid id);
+template ShapeTable::Entry& ShapeTable::search<MaybeAdding::NotAdding>(jsid id);
+
 bool
 ShapeTable::change(int log2Delta, ExclusiveContext* cx)
 {
     MOZ_ASSERT(entries_);
     MOZ_ASSERT(-1 <= log2Delta && log2Delta <= 1);
 
     /*
      * Grow, shrink, or compress by changing this->entries_.
@@ -279,17 +288,17 @@ ShapeTable::change(int log2Delta, Exclus
     hashShift_ = HASH_BITS - newLog2;
     removedCount_ = 0;
     Entry* oldTable = entries_;
     entries_ = newTable;
 
     /* Copy only live entries, leaving removed and free ones behind. */
     for (Entry* oldEntry = oldTable; oldSize != 0; oldEntry++) {
         if (Shape* shape = oldEntry->shape()) {
-            Entry& entry = search(shape->propid(), true);
+            Entry& entry = search<MaybeAdding::Adding>(shape->propid());
             MOZ_ASSERT(entry.isFree());
             entry.setShape(shape);
         }
         oldSize--;
     }
 
     MOZ_ASSERT(capacity() == newSize);
 
@@ -488,17 +497,17 @@ NativeObject::addProperty(ExclusiveConte
     if (!extensible) {
         if (cx->isJSContext())
             obj->reportNotExtensible(cx->asJSContext());
         return nullptr;
     }
 
     ShapeTable::Entry* entry = nullptr;
     if (obj->inDictionaryMode())
-        entry = &obj->lastProperty()->table().search(id, true);
+        entry = &obj->lastProperty()->table().search<MaybeAdding::Adding>(id);
 
     return addPropertyInternal(cx, obj, id, getter, setter, slot, attrs, flags, entry,
                                allowDictionary);
 }
 
 static bool
 ShouldConvertToDictionary(NativeObject* obj)
 {
@@ -538,24 +547,24 @@ NativeObject::addPropertyInternal(Exclus
             (slot == obj->lastProperty()->maybeSlot() + 1);
         MOZ_ASSERT_IF(!allowDictionary, stableSlot);
         if (allowDictionary &&
             (!stableSlot || ShouldConvertToDictionary(obj)))
         {
             if (!obj->toDictionaryMode(cx))
                 return nullptr;
             table = &obj->lastProperty()->table();
-            entry = &table->search(id, true);
+            entry = &table->search<MaybeAdding::Adding>(id);
         }
     } else {
         table = &obj->lastProperty()->table();
         if (table->needsToGrow()) {
             if (!table->grow(cx))
                 return nullptr;
-            entry = &table->search(id, true);
+            entry = &table->search<MaybeAdding::Adding>(id);
             MOZ_ASSERT(!entry->shape());
         }
     }
 
     MOZ_ASSERT(!!table == !!entry);
 
     /* Find or create a property tree node labeled by our arguments. */
     RootedShape shape(cx);
@@ -706,17 +715,17 @@ NativeObject::putProperty(ExclusiveConte
      * Note that we can only try to claim an entry in a table that is thread
      * local. An object may be thread local *without* its shape being thread
      * local. The only thread local objects that *also* have thread local
      * shapes are dictionaries that were allocated/converted thread
      * locally. Only for those objects we can try to claim an entry in its
      * shape table.
      */
     ShapeTable::Entry* entry;
-    RootedShape shape(cx, Shape::search(cx, obj->lastProperty(), id, &entry, true));
+    RootedShape shape(cx, Shape::search<MaybeAdding::Adding>(cx, obj->lastProperty(), id, &entry));
     if (!shape) {
         /*
          * You can't add properties to a non-extensible object, but you can change
          * attributes of properties in such objects.
          */
         bool extensible;
 
         if (!IsExtensible(cx, obj, &extensible))
@@ -770,17 +779,17 @@ NativeObject::putProperty(ExclusiveConte
     /*
      * Overwriting a non-last property requires switching to dictionary mode.
      * The shape tree is shared immutable, and we can't removeProperty and then
      * addPropertyInternal because a failure under add would lose data.
      */
     if (shape != obj->lastProperty() && !obj->inDictionaryMode()) {
         if (!obj->toDictionaryMode(cx))
             return nullptr;
-        entry = &obj->lastProperty()->table().search(shape->propid(), false);
+        entry = &obj->lastProperty()->table().search<MaybeAdding::NotAdding>(shape->propid());
         shape = entry->shape();
     }
 
     MOZ_ASSERT_IF(shape->hasSlot() && !(attrs & JSPROP_SHARED), shape->slot() == slot);
 
     if (obj->inDictionaryMode()) {
         /*
          * Updating some property in a dictionary-mode object. Create a new
@@ -918,17 +927,17 @@ NativeObject::removeProperty(ExclusiveCo
 
     /*
      * If shape is not the last property added, or the last property cannot
      * be removed, switch to dictionary mode.
      */
     if (!self->inDictionaryMode() && (shape != self->lastProperty() || !self->canRemoveLastProperty())) {
         if (!self->toDictionaryMode(cx))
             return false;
-        entry = &self->lastProperty()->table().search(shape->propid(), false);
+        entry = &self->lastProperty()->table().search<MaybeAdding::NotAdding>(shape->propid());
         shape = entry->shape();
     }
 
     /*
      * If in dictionary mode, get a new shape for the last property after the
      * removal. We need a fresh shape for all dictionary deletions, even of
      * the last property. Otherwise, a shape could replay and caches might
      * return deleted DictionaryShapes! See bug 595365. Do this before changing
@@ -1101,17 +1110,17 @@ NativeObject::replaceWithNewEquivalentSh
         new (newShape) Shape(oldRoot->base()->unowned(), 0);
         self = selfRoot;
         oldShape = oldRoot;
     }
 
     ShapeTable& table = self->lastProperty()->table();
     ShapeTable::Entry* entry = oldShape->isEmptyShape()
                                ? nullptr
-                               : &table.search(oldShape->propidRef(), false);
+                               : &table.search<MaybeAdding::NotAdding>(oldShape->propidRef());
 
     /*
      * Splice the new shape into the same position as the old shape, preserving
      * enumeration order (see bug 601399).
      */
     StackShape nshape(oldShape);
     newShape->initDictionaryShape(nshape, self->numFixedSlots(), oldShape->listp);
 
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -116,16 +116,18 @@ class TenuringTracer;
 typedef JSGetterOp GetterOp;
 typedef JSSetterOp SetterOp;
 typedef JSPropertyDescriptor PropertyDescriptor;
 
 /* Limit on the number of slotful properties in an object. */
 static const uint32_t SHAPE_INVALID_SLOT = JS_BIT(24) - 1;
 static const uint32_t SHAPE_MAXIMUM_SLOT = JS_BIT(24) - 2;
 
+enum class MaybeAdding { Adding = true, NotAdding = false };
+
 /*
  * Shapes use multiplicative hashing, but specialized to
  * minimize footprint.
  */
 class ShapeTable {
   public:
     friend class NativeObject;
     static const uint32_t MIN_ENTRIES   = 11;
@@ -218,17 +220,19 @@ class ShapeTable {
 
     /*
      * NB: init and change are fallible but do not report OOM, so callers can
      * cope or ignore. They do however use the context's calloc method in
      * order to update the malloc counter on success.
      */
     bool init(ExclusiveContext* cx, Shape* lastProp);
     bool change(int log2Delta, ExclusiveContext* cx);
-    Entry& search(jsid id, bool adding);
+
+    template<MaybeAdding Adding>
+    Entry& search(jsid id);
 
   private:
     Entry& getEntry(uint32_t i) const {
         MOZ_ASSERT(i < capacity());
         return entries_[i];
     }
     void decEntryCount() {
         MOZ_ASSERT(entryCount_ > 0);
@@ -575,18 +579,19 @@ class Shape : public gc::TenuredCell
         KidsPointer kids;       /* null, single child, or a tagged ptr
                                    to many-kids data structure */
         HeapPtrShape* listp;    /* dictionary list starting at shape_
                                    has a double-indirect back pointer,
                                    either to the next shape's parent if not
                                    last, else to obj->shape_ */
     };
 
+    template<MaybeAdding Adding = MaybeAdding::NotAdding>
     static inline Shape* search(ExclusiveContext* cx, Shape* start, jsid id,
-                                ShapeTable::Entry** pentry, bool adding = false);
+                                ShapeTable::Entry** pentry);
     static inline Shape* searchNoHashify(Shape* start, jsid id);
 
     void removeFromDictionary(NativeObject* obj);
     void insertIntoDictionary(HeapPtrShape* dictp);
 
     inline void initDictionaryShape(const StackShape& child, uint32_t nfixed, HeapPtrShape* dictp);
 
     /* Replace the base shape of the last shape in a non-dictionary lineage with base. */
@@ -630,21 +635,16 @@ class Shape : public gc::TenuredCell
             else
                 info->shapesMallocHeapTreeTables += table().sizeOfIncludingThis(mallocSizeOf);
         }
 
         if (!inDictionary() && kids.isHash())
             info->shapesMallocHeapTreeKids += kids.toHash()->sizeOfIncludingThis(mallocSizeOf);
     }
 
-    bool isNative() const {
-        MOZ_ASSERT(!(flags & NON_NATIVE) == getObjectClass()->isNative());
-        return !(flags & NON_NATIVE);
-    }
-
     bool isAccessorShape() const {
         MOZ_ASSERT_IF(flags & ACCESSOR_SHAPE, getAllocKind() == gc::AllocKind::ACCESSOR_SHAPE);
         return flags & ACCESSOR_SHAPE;
     }
     AccessorShape& asAccessorShape() const {
         MOZ_ASSERT(isAccessorShape());
         return *(AccessorShape*)this;
     }
@@ -700,35 +700,34 @@ class Shape : public gc::TenuredCell
 
   protected:
     /*
      * Implementation-private bits stored in shape->flags. See public: enum {}
      * flags further below, which were allocated FCFS over time, so interleave
      * with these bits.
      */
     enum {
-        /* Property is placeholder for a non-native class. */
-        NON_NATIVE      = 0x01,
-
         /* Property stored in per-object dictionary, not shared property tree. */
-        IN_DICTIONARY   = 0x02,
+        IN_DICTIONARY   = 0x01,
 
         /*
          * Slotful property was stored to more than once. This is used as a
          * hint for type inference.
          */
-        OVERWRITTEN     = 0x04,
+        OVERWRITTEN     = 0x02,
 
         /*
          * This shape is an AccessorShape, a fat Shape that can store
          * getter/setter information.
          */
-        ACCESSOR_SHAPE  = 0x08,
+        ACCESSOR_SHAPE  = 0x04,
 
-        UNUSED_BITS     = 0x3C
+        /* Flags used to speed up isBigEnoughForAShapeTable(). */
+        HAS_CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE = 0x08,
+        CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE = 0x10,
     };
 
     /* Get a shape identical to this one, without parent/kids information. */
     inline Shape(const StackShape& other, uint32_t nfixed);
 
     /* Used by EmptyShape (see jsscopeinlines.h). */
     inline Shape(UnownedBaseShape* base, uint32_t nfixed);
 
@@ -902,28 +901,50 @@ class Shape : public gc::TenuredCell
         if (hasTable())
             return table().entryCount();
         uint32_t count = 0;
         for (Shape::Range<NoGC> r(this); !r.empty(); r.popFront())
             ++count;
         return count;
     }
 
-    bool isBigEnoughForAShapeTable() {
-        MOZ_ASSERT(!hasTable());
-        Shape* shape = this;
+  private:
+    bool isBigEnoughForAShapeTableSlow() {
         uint32_t count = 0;
-        for (Shape::Range<NoGC> r(shape); !r.empty(); r.popFront()) {
+        for (Shape::Range<NoGC> r(this); !r.empty(); r.popFront()) {
             ++count;
             if (count >= ShapeTable::MIN_ENTRIES)
                 return true;
         }
         return false;
     }
 
+  public:
+    bool isBigEnoughForAShapeTable() {
+        MOZ_ASSERT(!inDictionary());
+        MOZ_ASSERT(!hasTable());
+
+        // isBigEnoughForAShapeTableSlow is pretty inefficient so we only call
+        // it once and cache the result.
+
+        if (flags & HAS_CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE) {
+            bool res = flags & CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE;
+            MOZ_ASSERT(res == isBigEnoughForAShapeTableSlow());
+            return res;
+        }
+
+        MOZ_ASSERT(!(flags & CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE));
+
+        bool res = isBigEnoughForAShapeTableSlow();
+        if (res)
+            flags |= CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE;
+        flags |= HAS_CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE;
+        return res;
+    }
+
 #ifdef DEBUG
     void dump(JSContext* cx, FILE* fp) const;
     void dumpSubtree(JSContext* cx, int level, FILE* fp) const;
 #endif
 
     void sweep();
     void finalize(FreeOp* fop);
     void removeChild(Shape* child);
@@ -1010,21 +1031,17 @@ class MOZ_RAII AutoRooterGetterSetter
     mozilla::Maybe<Inner> inner;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 struct EmptyShape : public js::Shape
 {
     EmptyShape(UnownedBaseShape* base, uint32_t nfixed)
       : js::Shape(base, nfixed)
-    {
-        // Only empty shapes can be NON_NATIVE.
-        if (!getObjectClass()->isNative())
-            flags |= NON_NATIVE;
-    }
+    { }
 
     static Shape* new_(ExclusiveContext* cx, Handle<UnownedBaseShape*> base, uint32_t nfixed);
 
     /*
      * Lookup an initial shape matching the given parameters, creating an empty
      * shape if none was found.
      */
     static Shape* getInitialShape(ExclusiveContext* cx, const Class* clasp,
@@ -1364,17 +1381,17 @@ Shape::searchLinear(jsid id)
 inline Shape*
 Shape::searchNoHashify(Shape* start, jsid id)
 {
     /*
      * If we have a table, search in the shape table, else do a linear
      * search. We never hashify into a table in parallel.
      */
     if (start->hasTable()) {
-        ShapeTable::Entry& entry = start->table().search(id, false);
+        ShapeTable::Entry& entry = start->table().search<MaybeAdding::NotAdding>(id);
         return entry.shape();
     }
 
     return start->searchLinear(id);
 }
 
 inline bool
 Shape::matches(const StackShape& other) const
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -18,16 +18,17 @@
 #include "mozilla/EnumSet.h"
 #include "nsCOMPtr.h"
 #include "nsContainerFrame.h"
 #include "nsPoint.h"
 #include "nsRect.h"
 #include "plarena.h"
 #include "nsRegion.h"
 #include "nsDisplayListInvalidation.h"
+#include "nsRenderingContext.h"
 #include "DisplayListClipState.h"
 #include "LayerState.h"
 #include "FrameMetrics.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/gfx/UserData.h"
 
 #include <stdint.h>
 #include "nsTHashtable.h"
@@ -2353,58 +2354,91 @@ public:
  * 
  * This should not be used for display items which are created frequently,
  * because each item is one or two pointers bigger than an item from a
  * custom display item class could be, and fractionally slower. However it does
  * save code size. We use this for infrequently-used item types.
  */
 class nsDisplayGeneric : public nsDisplayItem {
 public:
-  typedef void (* PaintCallback)(nsIFrame* aFrame, nsRenderingContext* aCtx,
+  typedef class mozilla::gfx::DrawTarget DrawTarget;
+
+  typedef void (* PaintCallback)(nsIFrame* aFrame, DrawTarget* aDrawTarget,
                                  const nsRect& aDirtyRect, nsPoint aFramePt);
 
+  // XXX: should be removed eventually
+  typedef void (* OldPaintCallback)(nsIFrame* aFrame, nsRenderingContext* aCtx,
+                                    const nsRect& aDirtyRect, nsPoint aFramePt);
+
   nsDisplayGeneric(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                    PaintCallback aPaint, const char* aName, Type aType)
-    : nsDisplayItem(aBuilder, aFrame), mPaint(aPaint)
-      , mName(aName)
-      , mType(aType)
+    : nsDisplayItem(aBuilder, aFrame)
+    , mPaint(aPaint)
+    , mOldPaint(nullptr)
+    , mName(aName)
+    , mType(aType)
+  {
+    MOZ_COUNT_CTOR(nsDisplayGeneric);
+  }
+
+  // XXX: should be removed eventually
+  nsDisplayGeneric(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
+                   OldPaintCallback aOldPaint, const char* aName, Type aType)
+    : nsDisplayItem(aBuilder, aFrame)
+    , mPaint(nullptr)
+    , mOldPaint(aOldPaint)
+    , mName(aName)
+    , mType(aType)
   {
     MOZ_COUNT_CTOR(nsDisplayGeneric);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayGeneric() {
     MOZ_COUNT_DTOR(nsDisplayGeneric);
   }
 #endif
   
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsRenderingContext* aCtx) override {
-    mPaint(mFrame, aCtx, mVisibleRect, ToReferenceFrame());
+    MOZ_ASSERT(!!mPaint != !!mOldPaint);
+    if (mPaint) {
+      mPaint(mFrame, aCtx->GetDrawTarget(), mVisibleRect, ToReferenceFrame());
+    } else {
+      mOldPaint(mFrame, aCtx, mVisibleRect, ToReferenceFrame());
+    }
   }
   NS_DISPLAY_DECL_NAME(mName, mType)
 
 protected:
   PaintCallback mPaint;
-  const char*   mName;
+  OldPaintCallback mOldPaint;   // XXX: should be removed eventually
+  const char* mName;
   Type mType;
 };
 
 /**
  * Generic display item that can contain overflow. Use this in lieu of
  * nsDisplayGeneric if you have a frame that should use the visual overflow
  * rect of its frame when drawing items, instead of the frame's bounds.
  */
 class nsDisplayGenericOverflow : public nsDisplayGeneric {
   public:
     nsDisplayGenericOverflow(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                              PaintCallback aPaint, const char* aName, Type aType)
       : nsDisplayGeneric(aBuilder, aFrame, aPaint, aName, aType)
     {
       MOZ_COUNT_CTOR(nsDisplayGenericOverflow);
     }
+    // XXX: should be removed eventually
+    nsDisplayGenericOverflow(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
+                             OldPaintCallback aOldPaint, const char* aName, Type aType)
+      : nsDisplayGeneric(aBuilder, aFrame, aOldPaint, aName, aType)
+    {
+      MOZ_COUNT_CTOR(nsDisplayGenericOverflow);
+    }
   #ifdef NS_BUILD_REFCNT_LOGGING
     virtual ~nsDisplayGenericOverflow() {
       MOZ_COUNT_DTOR(nsDisplayGenericOverflow);
     }
   #endif
 
     /**
      * Returns the frame's visual overflow rect instead of the frame's bounds.
--- a/layout/forms/nsGfxCheckboxControlFrame.cpp
+++ b/layout/forms/nsGfxCheckboxControlFrame.cpp
@@ -15,17 +15,17 @@
 #include "nsDisplayList.h"
 #include <algorithm>
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
 static void
 PaintCheckMark(nsIFrame* aFrame,
-               nsRenderingContext* aCtx,
+               DrawTarget* aDrawTarget,
                const nsRect& aDirtyRect,
                nsPoint aPt)
 {
   nsRect rect(aPt, aFrame->GetSize());
   rect.Deflate(aFrame->GetUsedBorderAndPadding());
 
   // Points come from the coordinates on a 7X7 unit box centered at 0,0
   const int32_t checkPolygonX[] = { -3, -1,  3,  3, -1, -3 };
@@ -34,51 +34,49 @@ PaintCheckMark(nsIFrame* aFrame,
   const int32_t checkSize      = 9; // 2 units of padding on either side
                                     // of the 7x7 unit checkmark
 
   // Scale the checkmark based on the smallest dimension
   nscoord paintScale = std::min(rect.width, rect.height) / checkSize;
   nsPoint paintCenter(rect.x + rect.width  / 2,
                       rect.y + rect.height / 2);
 
-  DrawTarget* drawTarget = aCtx->GetDrawTarget();
-  RefPtr<PathBuilder> builder = drawTarget->CreatePathBuilder();
+  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
   nsPoint p = paintCenter + nsPoint(checkPolygonX[0] * paintScale,
                                     checkPolygonY[0] * paintScale);
 
   int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
   builder->MoveTo(NSPointToPoint(p, appUnitsPerDevPixel));
   for (int32_t polyIndex = 1; polyIndex < checkNumPoints; polyIndex++) {
     p = paintCenter + nsPoint(checkPolygonX[polyIndex] * paintScale,
                               checkPolygonY[polyIndex] * paintScale);
     builder->LineTo(NSPointToPoint(p, appUnitsPerDevPixel));
   }
   RefPtr<Path> path = builder->Finish();
-  drawTarget->Fill(path,
-                   ColorPattern(ToDeviceColor(aFrame->StyleColor()->mColor)));
+  aDrawTarget->Fill(path,
+                    ColorPattern(ToDeviceColor(aFrame->StyleColor()->mColor)));
 }
 
 static void
 PaintIndeterminateMark(nsIFrame* aFrame,
-                       nsRenderingContext* aCtx,
+                       DrawTarget* aDrawTarget,
                        const nsRect& aDirtyRect,
                        nsPoint aPt)
 {
-  DrawTarget* drawTarget = aCtx->GetDrawTarget();
   int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
 
   nsRect rect(aPt, aFrame->GetSize());
   rect.Deflate(aFrame->GetUsedBorderAndPadding());
   rect.y += (rect.height - rect.height/4) / 2;
   rect.height /= 4;
 
-  Rect devPxRect = NSRectToSnappedRect(rect, appUnitsPerDevPixel, *drawTarget);
+  Rect devPxRect = NSRectToSnappedRect(rect, appUnitsPerDevPixel, *aDrawTarget);
 
-  drawTarget->FillRect(devPxRect,
-                    ColorPattern(ToDeviceColor(aFrame->StyleColor()->mColor)));
+  aDrawTarget->FillRect(
+    devPxRect, ColorPattern(ToDeviceColor(aFrame->StyleColor()->mColor)));
 }
 
 //------------------------------------------------------------
 nsIFrame*
 NS_NewGfxCheckboxControlFrame(nsIPresShell* aPresShell,
                               nsStyleContext* aContext)
 {
   return new (aPresShell) nsGfxCheckboxControlFrame(aContext);
--- a/layout/forms/nsGfxRadioControlFrame.cpp
+++ b/layout/forms/nsGfxRadioControlFrame.cpp
@@ -40,38 +40,37 @@ nsGfxRadioControlFrame::AccessibleType()
   return a11y::eHTMLRadioButtonType;
 }
 #endif
 
 //--------------------------------------------------------------
 // Draw the dot for a non-native radio button in the checked state.
 static void
 PaintCheckedRadioButton(nsIFrame* aFrame,
-                        nsRenderingContext* aCtx,
+                        DrawTarget* aDrawTarget,
                         const nsRect& aDirtyRect,
                         nsPoint aPt)
 {
   // The dot is an ellipse 2px on all sides smaller than the content-box,
   // drawn in the foreground color.
   nsRect rect(aPt, aFrame->GetSize());
   rect.Deflate(aFrame->GetUsedBorderAndPadding());
   rect.Deflate(nsPresContext::CSSPixelsToAppUnits(2),
                nsPresContext::CSSPixelsToAppUnits(2));
 
   Rect devPxRect =
     ToRect(nsLayoutUtils::RectToGfxRect(rect,
                                         aFrame->PresContext()->AppUnitsPerDevPixel()));
 
   ColorPattern color(ToDeviceColor(aFrame->StyleColor()->mColor));
 
-  DrawTarget* drawTarget = aCtx->GetDrawTarget();
-  RefPtr<PathBuilder> builder = drawTarget->CreatePathBuilder();
+  RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder();
   AppendEllipseToPath(builder, devPxRect.Center(), devPxRect.Size());
   RefPtr<Path> ellipse = builder->Finish();
-  drawTarget->Fill(ellipse, color);
+  aDrawTarget->Fill(ellipse, color);
 }
 
 void
 nsGfxRadioControlFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                          const nsRect&           aDirtyRect,
                                          const nsDisplayListSet& aLists)
 {
   nsFormControlFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1733,34 +1733,34 @@ ApplyOverflowClipping(nsDisplayListBuild
     clipRect = aFrame->GetContentRectRelativeToSelf() +
       aBuilder->ToReferenceFrame(aFrame);
     // XXX border-radius
   }
   aClipState.ClipContainingBlockDescendantsExtra(clipRect, haveRadii ? radii : nullptr);
 }
 
 #ifdef DEBUG
-static void PaintDebugBorder(nsIFrame* aFrame, nsRenderingContext* aCtx,
-     const nsRect& aDirtyRect, nsPoint aPt) {
+static void PaintDebugBorder(nsIFrame* aFrame, DrawTarget* aDrawTarget,
+     const nsRect& aDirtyRect, nsPoint aPt)
+{
   nsRect r(aPt, aFrame->GetSize());
-  DrawTarget* drawTarget = aCtx->GetDrawTarget();
   int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
   Color blueOrRed(aFrame->HasView() ? Color(0.f, 0.f, 1.f, 1.f) :
                                       Color(1.f, 0.f, 0.f, 1.f));
-  drawTarget->StrokeRect(NSRectToRect(r, appUnitsPerDevPixel),
-                         ColorPattern(ToDeviceColor(blueOrRed)));
-}
-
-static void PaintEventTargetBorder(nsIFrame* aFrame, nsRenderingContext* aCtx,
-     const nsRect& aDirtyRect, nsPoint aPt) {
+  aDrawTarget->StrokeRect(NSRectToRect(r, appUnitsPerDevPixel),
+                          ColorPattern(ToDeviceColor(blueOrRed)));
+}
+
+static void PaintEventTargetBorder(nsIFrame* aFrame, DrawTarget* aDrawTarget,
+     const nsRect& aDirtyRect, nsPoint aPt)
+{
   nsRect r(aPt, aFrame->GetSize());
-  DrawTarget* drawTarget = aCtx->GetDrawTarget();
   int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
   ColorPattern purple(ToDeviceColor(Color(.5f, 0.f, .5f, 1.f)));
-  drawTarget->StrokeRect(NSRectToRect(r, appUnitsPerDevPixel), purple);
+  aDrawTarget->StrokeRect(NSRectToRect(r, appUnitsPerDevPixel), purple);
 }
 
 static void
 DisplayDebugBorders(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                     const nsDisplayListSet& aLists) {
   // Draw a border around the child
   // REVIEW: From nsContainerFrame::PaintChild
   if (nsFrame::GetShowFrameBorders() && !aFrame->GetRect().IsEmpty()) {
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1452,29 +1452,28 @@ nsImageFrame::DisplayAltFeedback(nsRende
   }
 
   aRenderingContext.ThebesContext()->Restore();
 
   return result;
 }
 
 #ifdef DEBUG
-static void PaintDebugImageMap(nsIFrame* aFrame, nsRenderingContext* aCtx,
+static void PaintDebugImageMap(nsIFrame* aFrame, DrawTarget* aDrawTarget,
                                const nsRect& aDirtyRect, nsPoint aPt)
 {
   nsImageFrame* f = static_cast<nsImageFrame*>(aFrame);
   nsRect inner = f->GetInnerArea() + aPt;
   gfxPoint devPixelOffset =
     nsLayoutUtils::PointToGfxPoint(inner.TopLeft(),
                                    aFrame->PresContext()->AppUnitsPerDevPixel());
-  DrawTarget* drawTarget = aCtx->GetDrawTarget();
-  AutoRestoreTransform autoRestoreTransform(drawTarget);
-  drawTarget->SetTransform(
-    drawTarget->GetTransform().PreTranslate(ToPoint(devPixelOffset)));
-  f->GetImageMap()->Draw(aFrame, *drawTarget,
+  AutoRestoreTransform autoRestoreTransform(aDrawTarget);
+  aDrawTarget->SetTransform(
+    aDrawTarget->GetTransform().PreTranslate(ToPoint(devPixelOffset)));
+  f->GetImageMap()->Draw(aFrame, *aDrawTarget,
                          ColorPattern(ToDeviceColor(Color(0.f, 0.f, 0.f, 1.f))));
 }
 #endif
 
 void
 nsDisplayImage::Paint(nsDisplayListBuilder* aBuilder,
                       nsRenderingContext* aCtx) {
   uint32_t flags = imgIContainer::FLAG_NONE;
--- a/layout/generic/nsPlaceholderFrame.cpp
+++ b/layout/generic/nsPlaceholderFrame.cpp
@@ -210,34 +210,33 @@ nsPlaceholderFrame::GetParentStyleContex
   // {ib} split gunk here.
   *aProviderFrame = CorrectStyleParentFrame(GetParent(), nsGkAtoms::placeholderFrame);
   return *aProviderFrame ? (*aProviderFrame)->StyleContext() : nullptr;
 }
 
 
 #ifdef DEBUG
 static void
-PaintDebugPlaceholder(nsIFrame* aFrame, nsRenderingContext* aCtx,
+PaintDebugPlaceholder(nsIFrame* aFrame, DrawTarget* aDrawTarget,
                       const nsRect& aDirtyRect, nsPoint aPt)
 {
   ColorPattern cyan(ToDeviceColor(Color(0.f, 1.f, 1.f, 1.f)));
-  DrawTarget* drawTarget = aCtx->GetDrawTarget();
   int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
 
   nscoord x = nsPresContext::CSSPixelsToAppUnits(-5);
   nsRect r(aPt.x + x, aPt.y,
            nsPresContext::CSSPixelsToAppUnits(13),
            nsPresContext::CSSPixelsToAppUnits(3));
-  drawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), cyan);
+  aDrawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), cyan);
 
   nscoord y = nsPresContext::CSSPixelsToAppUnits(-10);
   r = nsRect(aPt.x, aPt.y + y,
              nsPresContext::CSSPixelsToAppUnits(3),
              nsPresContext::CSSPixelsToAppUnits(10));
-  drawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), cyan);
+  aDrawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), cyan);
 }
 #endif // DEBUG
 
 #if defined(DEBUG) || (defined(MOZ_REFLOW_PERF_DSP) && defined(MOZ_REFLOW_PERF))
 
 void
 nsPlaceholderFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                      const nsRect&           aDirtyRect,
--- a/layout/reftests/dom/xbl-children-4.xhtml
+++ b/layout/reftests/dom/xbl-children-4.xhtml
@@ -13,17 +13,17 @@
     <script>
         onload = function() {
             /* First, schedule a pending restyle of the whole tree. */
             var newSheet = document.createElementNS("http://www.w3.org/1999/xhtml", "style");
             newSheet.appendChild(document.createTextNode("#nosuchelement { }"));
             document.head.appendChild(newSheet);
 
             /* Now, append a frame to our children element, causing the pending restyle to descend into it. */
-            var children = document.getElementsByTagName("children")[0];
+            var children = document.getElementsByTagName("xbl:children")[0];
             var span = document.createElementNS("http://www.w3.org/1999/xhtml", "span");
             span.appendChild(document.createTextNode("PASS"));
             children.appendChild(span);
             document.documentElement.removeAttribute("class");
         }
     </script>
 </body>
 </html>
--- a/layout/style/CSS.cpp
+++ b/layout/style/CSS.cpp
@@ -80,20 +80,15 @@ CSS::Supports(const GlobalObject& aGloba
 
   return parser.EvaluateSupportsCondition(aCondition, info.mDocURI,
                                           info.mBaseURI, info.mPrincipal);
 }
 
 /* static */ void
 CSS::Escape(const GlobalObject& aGlobal,
             const nsAString& aIdent,
-            nsAString& aReturn,
-            ErrorResult& aRv)
+            nsAString& aReturn)
 {
-  bool success = nsStyleUtil::AppendEscapedCSSIdent(aIdent, aReturn);
-
-  if (!success) {
-    aRv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
-  }
+  nsStyleUtil::AppendEscapedCSSIdent(aIdent, aReturn);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/layout/style/CSS.h
+++ b/layout/style/CSS.h
@@ -30,16 +30,15 @@ public:
                        ErrorResult& aRv);
 
   static bool Supports(const GlobalObject& aGlobal,
                        const nsAString& aDeclaration,
                        ErrorResult& aRv);
 
   static void Escape(const GlobalObject& aGlobal,
                      const nsAString& aIdent,
-                     nsAString& aReturn,
-                     ErrorResult& aRv);
+                     nsAString& aReturn);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_CSS_h_
--- a/layout/style/nsStyleUtil.cpp
+++ b/layout/style/nsStyleUtil.cpp
@@ -74,17 +74,17 @@ void nsStyleUtil::AppendEscapedCSSString
       }
       aReturn.Append(*in);
     }
   }
 
   aReturn.Append(quoteChar);
 }
 
-/* static */ bool
+/* static */ void
 nsStyleUtil::AppendEscapedCSSIdent(const nsAString& aIdent, nsAString& aReturn)
 {
   // The relevant parts of the CSS grammar are:
   //   ident    ([-]?{nmstart}|[-][-]){nmchar}*
   //   nmstart  [_a-z]|{nonascii}|{escape}
   //   nmchar   [_a-z0-9-]|{nonascii}|{escape}
   //   nonascii [^\0-\177]
   //   escape   {unicode}|\\[^\n\r\f0-9a-f]
@@ -93,25 +93,25 @@ nsStyleUtil::AppendEscapedCSSIdent(const
   // modified for idents by
   // http://dev.w3.org/csswg/cssom/#serialize-an-identifier and
   // http://dev.w3.org/csswg/css-syntax/#would-start-an-identifier
 
   const char16_t* in = aIdent.BeginReading();
   const char16_t* const end = aIdent.EndReading();
 
   if (in == end)
-    return true;
+    return;
 
   // A leading dash does not need to be escaped as long as it is not the
   // *only* character in the identifier.
   if (*in == '-') {
     if (in + 1 == end) {
       aReturn.Append(char16_t('\\'));
       aReturn.Append(char16_t('-'));
-      return true;
+      return;
     }
 
     aReturn.Append(char16_t('-'));
     ++in;
   }
 
   // Escape a digit at the start (including after a dash),
   // numerically.  If we didn't escape it numerically, it would get
@@ -119,35 +119,33 @@ nsStyleUtil::AppendEscapedCSSIdent(const
   if (in != end && ('0' <= *in && *in <= '9')) {
     aReturn.AppendPrintf("\\%hx ", *in);
     ++in;
   }
 
   for (; in != end; ++in) {
     char16_t ch = *in;
     if (ch == 0x00) {
-      return false;
-    }
-    if (ch < 0x20 || (0x7F <= ch && ch < 0xA0)) {
+      aReturn.Append(char16_t(0xFFFD));
+    } else if (ch < 0x20 || (0x7F <= ch && ch < 0xA0)) {
       // Escape U+0000 through U+001F and U+007F through U+009F numerically.
       aReturn.AppendPrintf("\\%hx ", *in);
     } else {
       // Escape ASCII non-identifier printables as a backslash plus
       // the character.
       if (ch < 0x7F &&
           ch != '_' && ch != '-' &&
           (ch < '0' || '9' < ch) &&
           (ch < 'A' || 'Z' < ch) &&
           (ch < 'a' || 'z' < ch)) {
         aReturn.Append(char16_t('\\'));
       }
       aReturn.Append(ch);
     }
   }
-  return true;
 }
 
 // unquoted family names must be a sequence of idents
 // so escape any parts that require escaping
 static void
 AppendUnquotedFamilyName(const nsAString& aFamilyName, nsAString& aResult)
 {
   const char16_t *p, *p_end;
--- a/layout/style/nsStyleUtil.h
+++ b/layout/style/nsStyleUtil.h
@@ -34,20 +34,19 @@ public:
   // Append a quoted (with 'quoteChar') and escaped version of aString
   // to aResult.  'quoteChar' must be ' or ".
   static void AppendEscapedCSSString(const nsAString& aString,
                                      nsAString& aResult,
                                      char16_t quoteChar = '"');
 
   // Append the identifier given by |aIdent| to |aResult|, with
   // appropriate escaping so that it can be reparsed to the same
-  // identifier.
-  // Returns false if |aIdent| contains U+0000
-  // Returns true for all other cases
-  static bool AppendEscapedCSSIdent(const nsAString& aIdent,
+  // identifier.  An exception is if aIdent contains U+0000, which
+  // will be escaped as U+FFFD and then reparsed back to U+FFFD.
+  static void AppendEscapedCSSIdent(const nsAString& aIdent,
                                     nsAString& aResult);
 
   static void
   AppendEscapedCSSFontFamilyList(const mozilla::FontFamilyList& aFamilyList,
                                  nsAString& aResult);
 
   // Append a bitmask-valued property's value(s) (space-separated) to aResult.
   static void AppendBitmaskCSSValue(nsCSSProperty aProperty,
--- a/layout/style/test/test_css_escape_api.html
+++ b/layout/style/test/test_css_escape_api.html
@@ -16,21 +16,28 @@ https://bugzilla.mozilla.org/show_bug.cg
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script>
 // Tests taken from:
 // https://github.com/mathiasbynens/CSS.escape/blob/master/tests/tests.js
 
-SimpleTest.doesThrow(() => CSS.escape('\0'), "InvalidCharacterError Character :\\0");
-SimpleTest.doesThrow(() => CSS.escape('a\0'), "InvalidCharacterError Character : a\\0");
-SimpleTest.doesThrow(() => CSS.escape('\0b'), "InvalidCharacterError Character : \\0b");
-SimpleTest.doesThrow(() => CSS.escape('a\0b'), "InvalidCharacterError Character : a\\0b");
 SimpleTest.doesThrow(() => CSS.escape(), 'undefined');
+
+is(CSS.escape('\0'), '\uFFFD', "escaping for 0 char (1)");
+is(CSS.escape('a\0'), 'a\uFFFD', "escaping for 0 char (2)");
+is(CSS.escape('\0b'), '\uFFFDb', "escaping for 0 char (3)");
+is(CSS.escape('a\0b'), 'a\uFFFDb', "escaping for 0 char (4)");
+
+is(CSS.escape('\uFFFD'), '\uFFFD', "escaping for replacement char (1)");
+is(CSS.escape('a\uFFFD'), 'a\uFFFD', "escaping replacement char (2)");
+is(CSS.escape('\uFFFDb'), '\uFFFDb', "escaping replacement char (3)");
+is(CSS.escape('a\uFFFDb'), 'a\uFFFDb', "escaping replacement char (4)");
+
 is(CSS.escape(true), 'true', "escapingFailed Character : true(bool)");
 is(CSS.escape(false), 'false', "escapingFailed Character : false(bool)");
 is(CSS.escape(null), 'null', "escapingFailed Character : null");
 is(CSS.escape(''), '', "escapingFailed Character : '' ");
 
 is(CSS.escape('\x01\x02\x1E\x1F'), '\\1 \\2 \\1e \\1f ',"escapingFailed Char: \\x01\\x02\\x1E\\x1F");
 
 is(CSS.escape('0a'), '\\30 a', "escapingFailed Char: 0a");
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -295,18 +295,17 @@ inline nscolor EnsureDifferentColors(nsc
                    NS_GET_G(colorA) ^ 0xff,
                    NS_GET_B(colorA) ^ 0xff);
       return res;
     }
     return colorA;
 }
 
 void
-nsTableCellFrame::DecorateForSelection(nsRenderingContext& aRenderingContext,
-                                       nsPoint aPt)
+nsTableCellFrame::DecorateForSelection(DrawTarget* aDrawTarget, nsPoint aPt)
 {
   NS_ASSERTION(IsSelected(), "Should only be called for selected cells");
   int16_t displaySelection;
   nsPresContext* presContext = PresContext();
   displaySelection = DisplaySelection(presContext);
   if (displaySelection) {
     RefPtr<nsFrameSelection> frameSelection =
       presContext->PresShell()->FrameSelection();
@@ -325,48 +324,47 @@ nsTableCellFrame::DecorateForSelection(n
       {
         //compare bordercolor to ((nsStyleColor *)myColor)->mBackgroundColor)
         bordercolor = EnsureDifferentColors(bordercolor,
                                             StyleBackground()->mBackgroundColor);
 
         int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
         Point devPixelOffset = NSPointToPoint(aPt, appUnitsPerDevPixel);
 
-        DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
-        AutoRestoreTransform autoRestoreTransform(drawTarget);
-        drawTarget->SetTransform(
-          drawTarget->GetTransform().PreTranslate(devPixelOffset));
+        AutoRestoreTransform autoRestoreTransform(aDrawTarget);
+        aDrawTarget->SetTransform(
+          aDrawTarget->GetTransform().PreTranslate(devPixelOffset));
 
         ColorPattern color(ToDeviceColor(bordercolor));
 
         nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
 
         StrokeLineWithSnapping(nsPoint(onePixel, 0), nsPoint(mRect.width, 0),
-                               appUnitsPerDevPixel, *drawTarget, color);
+                               appUnitsPerDevPixel, *aDrawTarget, color);
         StrokeLineWithSnapping(nsPoint(0, onePixel), nsPoint(0, mRect.height),
-                               appUnitsPerDevPixel, *drawTarget, color);
+                               appUnitsPerDevPixel, *aDrawTarget, color);
         StrokeLineWithSnapping(nsPoint(onePixel, mRect.height),
                                nsPoint(mRect.width, mRect.height),
-                               appUnitsPerDevPixel, *drawTarget, color);
+                               appUnitsPerDevPixel, *aDrawTarget, color);
         StrokeLineWithSnapping(nsPoint(mRect.width, onePixel),
                                nsPoint(mRect.width, mRect.height),
-                               appUnitsPerDevPixel, *drawTarget, color);
+                               appUnitsPerDevPixel, *aDrawTarget, color);
         //middle
         nsRect r(onePixel, onePixel,
                  mRect.width - onePixel, mRect.height - onePixel);
         Rect devPixelRect =
-          NSRectToSnappedRect(r, appUnitsPerDevPixel, *drawTarget);
-        drawTarget->StrokeRect(devPixelRect, color);
+          NSRectToSnappedRect(r, appUnitsPerDevPixel, *aDrawTarget);
+        aDrawTarget->StrokeRect(devPixelRect, color);
         //shading
         StrokeLineWithSnapping(nsPoint(2*onePixel, mRect.height-2*onePixel),
                                nsPoint(mRect.width-onePixel, mRect.height- (2*onePixel)),
-                               appUnitsPerDevPixel, *drawTarget, color);
+                               appUnitsPerDevPixel, *aDrawTarget, color);
         StrokeLineWithSnapping(nsPoint(mRect.width - (2*onePixel), 2*onePixel),
                                nsPoint(mRect.width - (2*onePixel), mRect.height-onePixel),
-                               appUnitsPerDevPixel, *drawTarget, color);
+                               appUnitsPerDevPixel, *aDrawTarget, color);
       }
     }
   }
 }
 
 DrawResult
 nsTableCellFrame::PaintBackground(nsRenderingContext& aRenderingContext,
                                   const nsRect&        aDirtyRect,
@@ -464,20 +462,21 @@ void nsTableCellFrame::InvalidateFrameWi
   nsIFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey);
   // If we have filters applied that would affects our bounds, then
   // we get an inactive layer created and this is computed
   // within FrameLayerBuilder
   GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey);
 }
 
 static void
-PaintTableCellSelection(nsIFrame* aFrame, nsRenderingContext* aCtx,
+PaintTableCellSelection(nsIFrame* aFrame, DrawTarget* aDrawTarget,
                         const nsRect& aRect, nsPoint aPt)
 {
-  static_cast<nsTableCellFrame*>(aFrame)->DecorateForSelection(*aCtx, aPt);
+  static_cast<nsTableCellFrame*>(aFrame)->DecorateForSelection(aDrawTarget,
+                                                               aPt);
 }
 
 void
 nsTableCellFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                    const nsRect&           aDirtyRect,
                                    const nsDisplayListSet& aLists)
 {
   DO_GLOBAL_REFLOW_COUNT_DSP("nsTableCellFrame");
--- a/layout/tables/nsTableCellFrame.h
+++ b/layout/tables/nsTableCellFrame.h
@@ -29,16 +29,17 @@
  * no actual support is under the hood.
  *
  * @author  sclark
  */
 class nsTableCellFrame : public nsContainerFrame,
                          public nsITableCellLayout,
                          public nsIPercentBSizeObserver
 {
+  typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::image::DrawResult DrawResult;
 
 protected:
   typedef mozilla::WritingMode WritingMode;
   typedef mozilla::LogicalSide LogicalSide;
   typedef mozilla::LogicalMargin LogicalMargin;
 
 public:
@@ -222,18 +223,17 @@ public:
 
   virtual LogicalMargin GetBorderWidth(WritingMode aWM) const;
 
   virtual DrawResult PaintBackground(nsRenderingContext& aRenderingContext,
                                      const nsRect&        aDirtyRect,
                                      nsPoint              aPt,
                                      uint32_t             aFlags);
 
-  void DecorateForSelection(nsRenderingContext& aRenderingContext,
-                            nsPoint              aPt);
+  void DecorateForSelection(DrawTarget* aDrawTarget, nsPoint aPt);
 
   virtual bool UpdateOverflow() override;
 
   virtual bool IsFrameOfType(uint32_t aFlags) const override
   {
     return nsContainerFrame::IsFrameOfType(aFlags & ~(nsIFrame::eTablePart));
   }
   
--- a/layout/xul/nsBoxFrame.cpp
+++ b/layout/xul/nsBoxFrame.cpp
@@ -1292,20 +1292,20 @@ void
 nsDisplayXULDebug::Paint(nsDisplayListBuilder* aBuilder,
                          nsRenderingContext* aCtx)
 {
   static_cast<nsBoxFrame*>(mFrame)->
     PaintXULDebugOverlay(*aCtx->GetDrawTarget(), ToReferenceFrame());
 }
 
 static void
-PaintXULDebugBackground(nsIFrame* aFrame, nsRenderingContext* aCtx,
+PaintXULDebugBackground(nsIFrame* aFrame, DrawTarget* aDrawTarget,
                         const nsRect& aDirtyRect, nsPoint aPt)
 {
-  static_cast<nsBoxFrame*>(aFrame)->PaintXULDebugBackground(*aCtx, aPt);
+  static_cast<nsBoxFrame*>(aFrame)->PaintXULDebugBackground(aDrawTarget, aPt);
 }
 #endif
 
 void
 nsBoxFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                              const nsRect&           aDirtyRect,
                              const nsDisplayListSet& aLists)
 {
@@ -1390,18 +1390,17 @@ nsBoxFrame::BuildDisplayListForChildren(
 // * Actually paint the children.
 // Moved to BuildDisplayList.
 // * Paint per-kid debug information.
 // This is done by nsDisplayXULDebug, which is in the Outlines()
 // layer so it goes on top. This means it is not clipped by OVERFLOW_CLIP,
 // whereas it did used to respect OVERFLOW_CLIP, but too bad.
 #ifdef DEBUG_LAYOUT
 void
-nsBoxFrame::PaintXULDebugBackground(nsRenderingContext& aRenderingContext,
-                                    nsPoint aPt)
+nsBoxFrame::PaintXULDebugBackground(DrawTarget* aDrawTarget, nsPoint aPt)
 {
   nsMargin border;
   GetBorder(border);
 
   nsMargin debugBorder;
   nsMargin debugMargin;
   nsMargin debugPadding;
 
@@ -1422,46 +1421,43 @@ nsBoxFrame::PaintXULDebugBackground(nsRe
   inner.Deflate(border);
   //nsRect borderRect(inner);
 
   int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
 
   ColorPattern color(ToDeviceColor(isHorizontal ? Color(0.f, 0.f, 1.f, 1.f) :
                                                   Color(1.f, 0.f, 0.f, 1.f)));
 
-  DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
-
   //left
   nsRect r(inner);
   r.width = debugBorder.left;
-  drawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), color);
+  aDrawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), color);
 
   // top
   r = inner;
   r.height = debugBorder.top;
-  drawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), color);
+  aDrawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), color);
 
   //right
   r = inner;
   r.x = r.x + r.width - debugBorder.right;
   r.width = debugBorder.right;
-  drawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), color);
+  aDrawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), color);
 
   //bottom
   r = inner;
   r.y = r.y + r.height - debugBorder.bottom;
   r.height = debugBorder.bottom;
-  drawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), color);
-  
-  // if we have dirty children or we are dirty 
-  // place a green border around us.
+  aDrawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), color);
+
+  // If we have dirty children or we are dirty place a green border around us.
   if (NS_SUBTREE_DIRTY(this)) {
     nsRect dirty(inner);
     ColorPattern green(ToDeviceColor(Color(0.f, 1.f, 0.f, 1.f)));
-    drawTarget->StrokeRect(NSRectToRect(dirty, appUnitsPerDevPixel), green);
+    aDrawTarget->StrokeRect(NSRectToRect(dirty, appUnitsPerDevPixel), green);
   }
 }
 
 void
 nsBoxFrame::PaintXULDebugOverlay(DrawTarget& aDrawTarget, nsPoint aPt)
 {
   nsMargin border;
   GetBorder(border);
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoAppShell.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoAppShell.java
@@ -579,138 +579,195 @@ public class GeckoAppShell
 
     @WrapForJNI
     public static void disableAlarm() {
         AlarmManager am = (AlarmManager)
             getApplicationContext().getSystemService(Context.ALARM_SERVICE);
 
         Intent intent = new Intent(getApplicationContext(), AlarmReceiver.class);
         PendingIntent pi = PendingIntent.getBroadcast(
-                getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+                getApplicationContext(), 0, intent,
+                PendingIntent.FLAG_UPDATE_CURRENT);
         am.cancel(pi);
     }
 
     @WrapForJNI
     public static void enableSensor(int aSensortype) {
         GeckoInterface gi = getGeckoInterface();
-        if (gi == null)
+        if (gi == null) {
             return;
+        }
         SensorManager sm = (SensorManager)
             getApplicationContext().getSystemService(Context.SENSOR_SERVICE);
 
         switch(aSensortype) {
+        case GeckoHalDefines.SENSOR_GAME_ROTATION_VECTOR:
+            if (gGameRotationVectorSensor == null) {
+                gGameRotationVectorSensor = sm.getDefaultSensor(15);
+                    // sm.getDefaultSensor(
+                    //     Sensor.TYPE_GAME_ROTATION_VECTOR); // API >= 18
+            }
+            if (gGameRotationVectorSensor != null) {
+                sm.registerListener(gi.getSensorEventListener(),
+                                    gGameRotationVectorSensor,
+                                    SensorManager.SENSOR_DELAY_FASTEST);
+            }
+            if (gGameRotationVectorSensor != null) {
+              break;
+            }
+            // Fallthrough
+
+        case GeckoHalDefines.SENSOR_ROTATION_VECTOR:
+            if (gRotationVectorSensor == null) {
+                gRotationVectorSensor = sm.getDefaultSensor(
+                    Sensor.TYPE_ROTATION_VECTOR);
+            }
+            if (gRotationVectorSensor != null) {
+                sm.registerListener(gi.getSensorEventListener(),
+                                    gRotationVectorSensor,
+                                    SensorManager.SENSOR_DELAY_FASTEST);
+            }
+            if (gRotationVectorSensor != null) {
+              break;
+            }
+            // Fallthrough
+
         case GeckoHalDefines.SENSOR_ORIENTATION:
-            if (gOrientationSensor == null)
-                gOrientationSensor = sm.getDefaultSensor(Sensor.TYPE_ORIENTATION);
-            if (gOrientationSensor != null)
-                sm.registerListener(gi.getSensorEventListener(), gOrientationSensor, SensorManager.SENSOR_DELAY_FASTEST);
+            if (gOrientationSensor == null) {
+                gOrientationSensor = sm.getDefaultSensor(
+                    Sensor.TYPE_ORIENTATION);
+            }
+            if (gOrientationSensor != null) {
+                sm.registerListener(gi.getSensorEventListener(),
+                                    gOrientationSensor,
+                                    SensorManager.SENSOR_DELAY_FASTEST);
+            }
             break;
 
         case GeckoHalDefines.SENSOR_ACCELERATION:
-            if (gAccelerometerSensor == null)
-                gAccelerometerSensor = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
-            if (gAccelerometerSensor != null)
-                sm.registerListener(gi.getSensorEventListener(), gAccelerometerSensor, SensorManager.SENSOR_DELAY_FASTEST);
+            if (gAccelerometerSensor == null) {
+                gAccelerometerSensor = sm.getDefaultSensor(
+                    Sensor.TYPE_ACCELEROMETER);
+            }
+            if (gAccelerometerSensor != null) {
+                sm.registerListener(gi.getSensorEventListener(),
+                                    gAccelerometerSensor,
+                                    SensorManager.SENSOR_DELAY_FASTEST);
+            }
             break;
 
         case GeckoHalDefines.SENSOR_PROXIMITY:
-            if (gProximitySensor == null)
+            if (gProximitySensor == null) {
                 gProximitySensor = sm.getDefaultSensor(Sensor.TYPE_PROXIMITY);
-            if (gProximitySensor != null)
-                sm.registerListener(gi.getSensorEventListener(), gProximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
+            }
+            if (gProximitySensor != null) {
+                sm.registerListener(gi.getSensorEventListener(),
+                                    gProximitySensor,
+                                    SensorManager.SENSOR_DELAY_NORMAL);
+            }
             break;
 
         case GeckoHalDefines.SENSOR_LIGHT:
-            if (gLightSensor == null)
+            if (gLightSensor == null) {
                 gLightSensor = sm.getDefaultSensor(Sensor.TYPE_LIGHT);
-            if (gLightSensor != null)
-                sm.registerListener(gi.getSensorEventListener(), gLightSensor, SensorManager.SENSOR_DELAY_NORMAL);
+            }
+            if (gLightSensor != null) {
+                sm.registerListener(gi.getSensorEventListener(),
+                                    gLightSensor,
+                                    SensorManager.SENSOR_DELAY_NORMAL);
+            }
             break;
 
         case GeckoHalDefines.SENSOR_LINEAR_ACCELERATION:
-            if (gLinearAccelerometerSensor == null)
-                gLinearAccelerometerSensor = sm.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
-            if (gLinearAccelerometerSensor != null)
-                sm.registerListener(gi.getSensorEventListener(), gLinearAccelerometerSensor, SensorManager.SENSOR_DELAY_FASTEST);
+            if (gLinearAccelerometerSensor == null) {
+                gLinearAccelerometerSensor = sm.getDefaultSensor(
+                    Sensor.TYPE_LINEAR_ACCELERATION);
+            }
+            if (gLinearAccelerometerSensor != null) {
+                sm.registerListener(gi.getSensorEventListener(),
+                                    gLinearAccelerometerSensor,
+                                    SensorManager.SENSOR_DELAY_FASTEST);
+            }
             break;
 
         case GeckoHalDefines.SENSOR_GYROSCOPE:
-            if (gGyroscopeSensor == null)
+            if (gGyroscopeSensor == null) {
                 gGyroscopeSensor = sm.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
-            if (gGyroscopeSensor != null)
-                sm.registerListener(gi.getSensorEventListener(), gGyroscopeSensor, SensorManager.SENSOR_DELAY_FASTEST);
-            break;
-
-        case GeckoHalDefines.SENSOR_ROTATION_VECTOR:
-            if (gRotationVectorSensor == null)
-                gRotationVectorSensor = sm.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
-            if (gRotationVectorSensor != null)
-                sm.registerListener(gi.getSensorEventListener(), gRotationVectorSensor, SensorManager.SENSOR_DELAY_FASTEST);
-            break;
-
-        case GeckoHalDefines.SENSOR_GAME_ROTATION_VECTOR:
-            if (gGameRotationVectorSensor == null)
-                gGameRotationVectorSensor = sm.getDefaultSensor(15 /* Sensor.TYPE_GAME_ROTATION_VECTOR */); // API >= 18
-            if (gGameRotationVectorSensor != null)
-                sm.registerListener(gi.getSensorEventListener(), gGameRotationVectorSensor, SensorManager.SENSOR_DELAY_FASTEST);
+            }
+            if (gGyroscopeSensor != null) {
+                sm.registerListener(gi.getSensorEventListener(),
+                                    gGyroscopeSensor,
+                                    SensorManager.SENSOR_DELAY_FASTEST);
+            }
             break;
 
         default:
-            Log.w(LOGTAG, "Error! Can't enable unknown SENSOR type " + aSensortype);
+            Log.w(LOGTAG, "Error! Can't enable unknown SENSOR type " +
+                  aSensortype);
         }
     }
 
     @WrapForJNI
     public static void disableSensor(int aSensortype) {
         GeckoInterface gi = getGeckoInterface();
         if (gi == null)
             return;
 
         SensorManager sm = (SensorManager)
             getApplicationContext().getSystemService(Context.SENSOR_SERVICE);
 
         switch (aSensortype) {
+        case GeckoHalDefines.SENSOR_GAME_ROTATION_VECTOR:
+            if (gGameRotationVectorSensor != null) {
+                sm.unregisterListener(gi.getSensorEventListener(), gGameRotationVectorSensor);
+              break;
+            }
+            // Fallthrough
+
+        case GeckoHalDefines.SENSOR_ROTATION_VECTOR:
+            if (gRotationVectorSensor != null) {
+                sm.unregisterListener(gi.getSensorEventListener(), gRotationVectorSensor);
+              break;
+            }
+            // Fallthrough
+
         case GeckoHalDefines.SENSOR_ORIENTATION:
-            if (gOrientationSensor != null)
+            if (gOrientationSensor != null) {
                 sm.unregisterListener(gi.getSensorEventListener(), gOrientationSensor);
+            }
             break;
 
         case GeckoHalDefines.SENSOR_ACCELERATION:
-            if (gAccelerometerSensor != null)
+            if (gAccelerometerSensor != null) {
                 sm.unregisterListener(gi.getSensorEventListener(), gAccelerometerSensor);
+            }
             break;
 
         case GeckoHalDefines.SENSOR_PROXIMITY:
-            if (gProximitySensor != null)
+            if (gProximitySensor != null) {
                 sm.unregisterListener(gi.getSensorEventListener(), gProximitySensor);
+            }
             break;
 
         case GeckoHalDefines.SENSOR_LIGHT:
-            if (gLightSensor != null)
+            if (gLightSensor != null) {
                 sm.unregisterListener(gi.getSensorEventListener(), gLightSensor);
+            }
             break;
 
         case GeckoHalDefines.SENSOR_LINEAR_ACCELERATION:
-            if (gLinearAccelerometerSensor != null)
+            if (gLinearAccelerometerSensor != null) {
                 sm.unregisterListener(gi.getSensorEventListener(), gLinearAccelerometerSensor);
-            break;
-
-        case GeckoHalDefines.SENSOR_ROTATION_VECTOR:
-            if (gRotationVectorSensor != null)
-                sm.unregisterListener(gi.getSensorEventListener(), gRotationVectorSensor);
-            break;
-
-        case GeckoHalDefines.SENSOR_GAME_ROTATION_VECTOR:
-            if (gGameRotationVectorSensor != null)
-                sm.unregisterListener(gi.getSensorEventListener(), gGameRotationVectorSensor);
+            }
             break;
 
         case GeckoHalDefines.SENSOR_GYROSCOPE:
-            if (gGyroscopeSensor != null)
+            if (gGyroscopeSensor != null) {
                 sm.unregisterListener(gi.getSensorEventListener(), gGyroscopeSensor);
+            }
             break;
         default:
             Log.w(LOGTAG, "Error! Can't disable unknown SENSOR type " + aSensortype);
         }
     }
 
     @WrapForJNI
     public static void startMonitoringGamepad() {
--- a/testing/docker/phone-builder/Dockerfile
+++ b/testing/docker/phone-builder/Dockerfile
@@ -1,9 +1,9 @@
-FROM          taskcluster/builder:0.5.10
+FROM          taskcluster/builder:0.5.11
 MAINTAINER    Wander Lairson Costa <wcosta@mozilla.com>
 
 ENV           SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE /home/worker/socorro.token
 
 # Add utilities and configuration
 ADD           bin                   /home/worker/bin
 ADD           config                /home/worker/.aws/config
 ADD           socorro.token         /home/worker/socorro.token
--- a/testing/docker/phone-builder/VERSION
+++ b/testing/docker/phone-builder/VERSION
@@ -1,1 +1,1 @@
-0.0.23
+0.0.24
--- a/testing/docker/tester/Dockerfile
+++ b/testing/docker/tester/Dockerfile
@@ -17,16 +17,17 @@ RUN chmod u+x /home/worker/bin/buildbot_
 RUN chmod u+x /usr/local/bin/linux64-minidump_stackwalk
 RUN apt-get install -y python-pip && pip install virtualenv;
 RUN mkdir Documents; mkdir Pictures; mkdir Music; mkdir Videos; mkdir artifacts
 RUN npm install -g taskcluster-vcs@2.3.12
 RUN npm install -g taskcluster-npm-cache@1.3.1
 RUN npm install -g node-gyp
 RUN rm -Rf .cache && mkdir -p .cache
 ENV PATH $PATH:/home/worker/bin
+ENV MINIDUMP_STACKWALK /usr/local/bin/linux64-minidump_stackwalk
 
 # Remove once running under 'worker' user.  This is necessary for pulseaudio to start
 # XXX: change this back to worker:worker once permissions issues are resolved
 RUN            chown -R root:root /home/worker
 
 
 # TODO Re-enable worker when bug 1093833 lands
 #USER          worker
--- a/testing/docker/tester/VERSION
+++ b/testing/docker/tester/VERSION
@@ -1,1 +1,1 @@
-0.4.4
+0.4.5
--- a/testing/mozharness/scripts/b2g_emulator_unittest.py
+++ b/testing/mozharness/scripts/b2g_emulator_unittest.py
@@ -98,16 +98,23 @@ class B2GEmulatorTest(TestingMixin, VCSM
          "help": "Number of this chunk",
          }
     ], [
         ["--test-path"],
         {"action": "store",
          "dest": "test_path",
          "help": "Path of tests to run",
          }
+    ], [
+        ["--symbols-url"],
+        {"action": "store",
+         "dest": "symbols_url",
+         "default": None,
+         "help": "URL to the symbols which is used for crash reporter",
+         }
     ]] + copy.deepcopy(testing_config_options) \
        + copy.deepcopy(blobupload_config_options)
 
     error_list = [
         {'substr': 'FAILED (errors=', 'level': ERROR},