Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 25 Jun 2015 11:39:22 +0200
changeset 280969 3ce3e827434e3cc68e0e69dc04b205943b404be6
parent 280968 516232663a0b9a80cdb311200487c8f55f7c3db2 (current diff)
parent 280955 0b2f5e8b7be546f2cf649b13adea51b800e940d2 (diff)
child 280970 bca014d5508abae9d9cfd674be74fa62d3db6b03
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to fx-team
--- a/accessible/generic/HyperTextAccessible.cpp
+++ b/accessible/generic/HyperTextAccessible.cpp
@@ -980,16 +980,19 @@ HyperTextAccessible::NativeAttributes()
     GetAccService()->MarkupAttributes(mContent, attributes);
 
   return attributes.forget();
 }
 
 nsIAtom*
 HyperTextAccessible::LandmarkRole() const
 {
+  if (!HasOwnContent())
+    return nullptr;
+
   // For the html landmark elements we expose them like we do ARIA landmarks to
   // make AT navigation schemes "just work".
   if (mContent->IsHTMLElement(nsGkAtoms::nav)) {
     return nsGkAtoms::navigation;
   }
 
   if (mContent->IsAnyOfHTMLElements(nsGkAtoms::header,
                                     nsGkAtoms::footer)) {
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="eb0d4aefa62b20420d6fa0642515a110daca5d97"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="038e917076271d304b906a41b4de670e505c67ae"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="35d2750e2e09e2b6ca00f7679ef15066856b8d15"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5642f6578ec8ebcc73e1b9aa3826d37010a5b7ce"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="a32003194f707f66a2d8cdb913ed1869f1926c5d"/>
   <project name="device/common" path="device/common" revision="96d4d2006c4fcb2f19a3fa47ab10cb409faa017b"/>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="eb0d4aefa62b20420d6fa0642515a110daca5d97"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="038e917076271d304b906a41b4de670e505c67ae"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="35d2750e2e09e2b6ca00f7679ef15066856b8d15"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5642f6578ec8ebcc73e1b9aa3826d37010a5b7ce"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="83760d213fb3bec7b4117d266fcfbf6fe2ba14ab"/>
   <project name="device/common" path="device/common" revision="6a2995683de147791e516aae2ccb31fdfbe2ad30"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="eb0d4aefa62b20420d6fa0642515a110daca5d97"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="038e917076271d304b906a41b4de670e505c67ae"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="87a2d8ab9248540910e56921654367b78a587095"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9d0e5057ee5404a31ec1bf76131cb11336a7c3b6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="eb0d4aefa62b20420d6fa0642515a110daca5d97"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="038e917076271d304b906a41b4de670e505c67ae"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="35d2750e2e09e2b6ca00f7679ef15066856b8d15"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5642f6578ec8ebcc73e1b9aa3826d37010a5b7ce"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="eb0d4aefa62b20420d6fa0642515a110daca5d97"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="038e917076271d304b906a41b4de670e505c67ae"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="35d2750e2e09e2b6ca00f7679ef15066856b8d15"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5642f6578ec8ebcc73e1b9aa3826d37010a5b7ce"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="1950e4760fa14688b83cdbb5acaa1af9f82ef434"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="ac6eb97a37035c09fb5ede0852f0881e9aadf9ad"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="737f591c5f95477148d26602c7be56cbea0cdeb9"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="51da9b1981be481b92a59a826d4d78dc73d0989a"/>
   <project name="device/common" path="device/common" revision="798a3664597e6041985feab9aef42e98d458bc3d"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="eb0d4aefa62b20420d6fa0642515a110daca5d97"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="038e917076271d304b906a41b4de670e505c67ae"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="35d2750e2e09e2b6ca00f7679ef15066856b8d15"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5642f6578ec8ebcc73e1b9aa3826d37010a5b7ce"/>
   <!-- Stock Android things -->
   <project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="ffc05a232799fe8fcb3e47b7440b52b1fb4244c0"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="337e0ef5e40f02a1ae59b90db0548976c70a7226"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" revision="8af5ff6f5dced9eb5a8127459df6c75d24342204"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" revision="30915518fa7ea07166efedc191a4f40aef516fe7"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" revision="96eee58e3389fb05a835310d6a06a6ba4486097a"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" revision="7c8a46698171aa2e0be09edb43d15a6acf832770"/>
   <project groups="pdk,linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" path="prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" revision="24b2038be8a636fd4a5d21f0abae1e466b07bcf7"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="eb0d4aefa62b20420d6fa0642515a110daca5d97"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="038e917076271d304b906a41b4de670e505c67ae"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="87a2d8ab9248540910e56921654367b78a587095"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="9d0e5057ee5404a31ec1bf76131cb11336a7c3b6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="eb0d4aefa62b20420d6fa0642515a110daca5d97"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="038e917076271d304b906a41b4de670e505c67ae"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="35d2750e2e09e2b6ca00f7679ef15066856b8d15"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5642f6578ec8ebcc73e1b9aa3826d37010a5b7ce"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="a32003194f707f66a2d8cdb913ed1869f1926c5d"/>
   <project name="device/common" path="device/common" revision="96d4d2006c4fcb2f19a3fa47ab10cb409faa017b"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "eb0d4aefa62b20420d6fa0642515a110daca5d97", 
+        "git_revision": "038e917076271d304b906a41b4de670e505c67ae", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "28b5133c0a062776dac8282abcc7810dc317a0bc", 
+    "revision": "7c48140d54ceee5131147e4960d860bb44ac6bb1", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="eb0d4aefa62b20420d6fa0642515a110daca5d97"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="038e917076271d304b906a41b4de670e505c67ae"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="35d2750e2e09e2b6ca00f7679ef15066856b8d15"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5642f6578ec8ebcc73e1b9aa3826d37010a5b7ce"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="eb0d4aefa62b20420d6fa0642515a110daca5d97"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="038e917076271d304b906a41b4de670e505c67ae"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="35d2750e2e09e2b6ca00f7679ef15066856b8d15"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5642f6578ec8ebcc73e1b9aa3826d37010a5b7ce"/>
   <!-- Stock Android things -->
   <project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="ffc05a232799fe8fcb3e47b7440b52b1fb4244c0"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="337e0ef5e40f02a1ae59b90db0548976c70a7226"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" revision="8af5ff6f5dced9eb5a8127459df6c75d24342204"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" revision="30915518fa7ea07166efedc191a4f40aef516fe7"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" revision="96eee58e3389fb05a835310d6a06a6ba4486097a"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" revision="7c8a46698171aa2e0be09edb43d15a6acf832770"/>
   <project groups="pdk,linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" path="prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" revision="24b2038be8a636fd4a5d21f0abae1e466b07bcf7"/>
--- a/browser/base/content/browser-fullScreen.js
+++ b/browser/base/content/browser-fullScreen.js
@@ -472,27 +472,28 @@ var FullScreen = {
     gNavToolbox.removeAttribute("fullscreenShouldAnimate");
     gNavToolbox.style.marginTop = "";
 
     if (!this._isChromeCollapsed) {
       return;
     }
 
     // Track whether mouse is near the toolbox
-    this._isChromeCollapsed = false;
     if (trackMouse && !this.useLionFullScreen) {
       let rect = gBrowser.mPanelContainer.getBoundingClientRect();
       this._mouseTargetRect = {
         top: rect.top + 50,
         bottom: rect.bottom,
         left: rect.left,
         right: rect.right
       };
       MousePosTracker.addListener(this);
     }
+
+    this._isChromeCollapsed = false;
   },
 
   hideNavToolbox: function (aAnimate = false) {
     if (this._isChromeCollapsed || !this._safeToCollapse())
       return;
 
     this._fullScrToggler.hidden = false;
 
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -23,17 +23,17 @@ namespace mozilla {
 
 using dom::URLSearchParams;
 
 void
 OriginAttributes::CreateSuffix(nsACString& aStr) const
 {
   MOZ_RELEASE_ASSERT(mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
 
-  nsRefPtr<URLSearchParams> usp = new URLSearchParams();
+  nsRefPtr<URLSearchParams> usp = new URLSearchParams(nullptr);
   nsAutoString value;
 
   if (mAppId != nsIScriptSecurityManager::NO_APP_ID) {
     value.AppendInt(mAppId);
     usp->Set(NS_LITERAL_STRING("appId"), value);
   }
 
   if (mInBrowser) {
@@ -103,18 +103,18 @@ OriginAttributes::PopulateFromSuffix(con
   if (aStr.IsEmpty()) {
     return true;
   }
 
   if (aStr[0] != '!') {
     return false;
   }
 
-  nsRefPtr<URLSearchParams> usp = new URLSearchParams();
-  usp->ParseInput(Substring(aStr, 1, aStr.Length() - 1), nullptr);
+  nsRefPtr<URLSearchParams> usp = new URLSearchParams(nullptr);
+  usp->ParseInput(Substring(aStr, 1, aStr.Length() - 1));
 
   PopulateFromSuffixIterator iterator(this);
   return usp->ForEach(iterator);
 }
 
 void
 OriginAttributes::CookieJar(nsACString& aStr)
 {
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -188,17 +188,16 @@
 #include "nsIStringBundle.h"
 #include "nsISupportsArray.h"
 #include "nsIURIFixup.h"
 #include "nsIURILoader.h"
 #include "nsIWebBrowserFind.h"
 #include "nsIWidget.h"
 #include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/dom/ScriptSettings.h"
-#include "mozilla/dom/URLSearchParams.h"
 #include "nsPerformance.h"
 
 #ifdef MOZ_TOOLKIT_SEARCH
 #include "nsIBrowserSearchService.h"
 #endif
 
 #include "mozIThirdPartyUtil.h"
 // Values for the network.cookie.cookieBehavior pref are documented in
@@ -2017,34 +2016,16 @@ nsDocShell::SetCurrentURI(nsIURI* aURI, 
   if (root.get() == static_cast<nsIDocShellTreeItem*>(this)) {
     // This is the root docshell
     isRoot = true;
   }
   if (mLSHE) {
     mLSHE->GetIsSubFrame(&isSubFrame);
   }
 
-  // nsDocShell owns a URLSearchParams that is used by
-  // window.location.searchParams to be in sync with the current location.
-  if (!mURLSearchParams) {
-    mURLSearchParams = new URLSearchParams();
-  }
-
-  nsAutoCString search;
-
-  nsCOMPtr<nsIURL> url(do_QueryInterface(mCurrentURI));
-  if (url) {
-    nsresult rv = url->GetQuery(search);
-    if (NS_FAILED(rv)) {
-      NS_WARNING("Failed to get the query from a nsIURL.");
-    }
-  }
-
-  mURLSearchParams->ParseInput(search, nullptr);
-
   if (!isSubFrame && !isRoot) {
     /*
      * We don't want to send OnLocationChange notifications when
      * a subframe is being loaded for the first time, while
      * visiting a frameset page
      */
     return false;
   }
@@ -5841,21 +5822,16 @@ nsDocShell::Destroy()
     mContentViewer = nullptr;
   }
 
   nsDocLoader::Destroy();
 
   mParentWidget = nullptr;
   mCurrentURI = nullptr;
 
-  if (mURLSearchParams) {
-    mURLSearchParams->RemoveObservers();
-    mURLSearchParams = nullptr;
-  }
-
   if (mScriptGlobal) {
     mScriptGlobal->DetachFromDocShell();
     mScriptGlobal = nullptr;
   }
 
   if (mSessionHistory) {
     // We want to destroy these content viewers now rather than
     // letting their destruction wait for the session history
@@ -13926,22 +13902,16 @@ nsDocShell::SetOpener(nsITabParent* aOpe
 
 nsITabParent*
 nsDocShell::GetOpener()
 {
   nsCOMPtr<nsITabParent> opener(do_QueryReferent(mOpener));
   return opener;
 }
 
-URLSearchParams*
-nsDocShell::GetURLSearchParams()
-{
-  return mURLSearchParams;
-}
-
 class JavascriptTimelineMarker : public TimelineMarker
 {
 public:
   JavascriptTimelineMarker(nsDocShell* aDocShell, const char* aName,
                            const char* aReason,
                            const char16_t* aFunctionName,
                            const char16_t* aFileName,
                            uint32_t aLineNumber)
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -55,17 +55,16 @@
 #include "nsCRT.h"
 #include "prtime.h"
 #include "nsRect.h"
 #include "Units.h"
 
 namespace mozilla {
 namespace dom {
 class EventTarget;
-class URLSearchParams;
 }
 }
 
 class nsDocShell;
 class nsDOMNavigationTiming;
 class nsGlobalWindow;
 class nsIController;
 class nsIScrollableFrame;
@@ -836,19 +835,16 @@ protected:
 
   // Set in LoadErrorPage from the method argument and used later
   // in CreateContentViewer. We have to delay an shistory entry creation
   // for which these objects are needed.
   nsCOMPtr<nsIURI> mFailedURI;
   nsCOMPtr<nsIChannel> mFailedChannel;
   uint32_t mFailedLoadType;
 
-  // window.location.searchParams is updated in sync with this object.
-  nsRefPtr<mozilla::dom::URLSearchParams> mURLSearchParams;
-
   // Set in DoURILoad when either the LOAD_RELOAD_ALLOW_MIXED_CONTENT flag or
   // the LOAD_NORMAL_ALLOW_MIXED_CONTENT flag is set.
   // Checked in nsMixedContentBlocker, to see if the channels match.
   nsCOMPtr<nsIChannel> mMixedContentChannel;
 
   // WEAK REFERENCES BELOW HERE.
   // Note these are intentionally not addrefd. Doing so will create a cycle.
   // For that reasons don't use nsCOMPtr.
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -6,32 +6,24 @@
 
 #include "domstubs.idl"
 #include "nsIDocShellTreeItem.idl"
 
 %{ C++
 #include "js/TypeDecls.h"
 class nsPresContext;
 class nsIPresShell;
-
-namespace mozilla {
-namespace dom {
-class URLSearchParams;
-}
-}
-
 %}
 
 /**
  * The nsIDocShell interface.
  */
 
 [ptr] native nsPresContext(nsPresContext);
 [ptr] native nsIPresShell(nsIPresShell);
-[ptr] native URLSearchParams(mozilla::dom::URLSearchParams);
 
 interface nsIURI;
 interface nsIChannel;
 interface nsIContentViewer;
 interface nsIURIContentListener;
 interface nsIDOMEventTarget;
 interface nsIDocShellLoadInfo;
 interface nsIEditor;
@@ -49,17 +41,17 @@ interface nsIWebBrowserPrint;
 interface nsIVariant;
 interface nsIPrivacyTransitionObserver;
 interface nsIReflowObserver;
 interface nsIScrollObserver;
 interface nsITabParent;
  
 typedef unsigned long nsLoadFlags;
 
-[scriptable, builtinclass, uuid(696b32a1-3cf1-4909-b501-474b25fc7954)]
+[scriptable, builtinclass, uuid(b3137b7c-d589-48aa-b89b-e02aa451d42c)]
 interface nsIDocShell : nsIDocShellTreeItem
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -1026,19 +1018,16 @@ interface nsIDocShell : nsIDocShellTreeI
    * Regarding setOpener / getOpener - We can't use XPIDL's "attribute"
    * for notxpcom, so we're relegated to using explicit gets / sets. This
    * should be fine, considering that these methods should only ever be
    * called from native code.
    */
   [noscript,notxpcom,nostdcall] void setOpener(in nsITabParent aOpener);
   [noscript,notxpcom,nostdcall] nsITabParent getOpener();
 
-  // URLSearchParams for the window.location is owned by the docShell.
-  [noscript,notxpcom] URLSearchParams getURLSearchParams();
-
   /**
    * Notify DocShell when the browser is about to start executing JS, and after
    * that execution has stopped.  This only occurs when the Timeline devtool
    * is collecting information.
    */
   [noscript,notxpcom,nostdcall] void notifyJSRunToCompletionStart(in string aReason,
                                                                   in wstring functionName,
                                                                   in wstring fileName,
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -14,10 +14,19 @@ namespace dom {
 ChromeUtils::OriginAttributesToCookieJar(GlobalObject& aGlobal,
                                          const OriginAttributesDictionary& aAttrs,
                                          nsCString& aCookieJar)
 {
   OriginAttributes attrs(aAttrs);
   attrs.CookieJar(aCookieJar);
 }
 
+/* static */ void
+ChromeUtils::OriginAttributesToSuffix(dom::GlobalObject& aGlobal,
+                                      const dom::OriginAttributesDictionary& aAttrs,
+                                      nsCString& aSuffix)
+
+{
+  OriginAttributes attrs(aAttrs);
+  attrs.CreateSuffix(aSuffix);
+}
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/ChromeUtils.h
+++ b/dom/base/ChromeUtils.h
@@ -39,14 +39,19 @@ public:
 
 class ChromeUtils : public ThreadSafeChromeUtils
 {
 public:
   static void
   OriginAttributesToCookieJar(dom::GlobalObject& aGlobal,
                               const dom::OriginAttributesDictionary& aAttrs,
                               nsCString& aCookieJar);
+
+  static void
+  OriginAttributesToSuffix(dom::GlobalObject& aGlobal,
+                           const dom::OriginAttributesDictionary& aAttrs,
+                           nsCString& aSuffix);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ChromeUtils__
--- a/dom/base/Link.cpp
+++ b/dom/base/Link.cpp
@@ -574,31 +574,16 @@ Link::SizeOfExcludingThis(mozilla::Mallo
 URLSearchParams*
 Link::SearchParams()
 {
   CreateSearchParamsIfNeeded();
   return mSearchParams;
 }
 
 void
-Link::SetSearchParams(URLSearchParams& aSearchParams)
-{
-  if (mSearchParams) {
-    mSearchParams->RemoveObserver(this);
-  }
-
-  mSearchParams = &aSearchParams;
-  mSearchParams->AddObserver(this);
-
-  nsAutoString search;
-  mSearchParams->Serialize(search);
-  SetSearchInternal(search);
-}
-
-void
 Link::URLSearchParamsUpdated(URLSearchParams* aSearchParams)
 {
   MOZ_ASSERT(mSearchParams);
   MOZ_ASSERT(mSearchParams == aSearchParams);
 
   nsString search;
   mSearchParams->Serialize(search);
   SetSearchInternal(search);
@@ -616,34 +601,32 @@ Link::UpdateURLSearchParams()
   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
   if (url) {
     nsresult rv = url->GetQuery(search);
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to get the query from a nsIURL.");
     }
   }
 
-  mSearchParams->ParseInput(search, this);
+  mSearchParams->ParseInput(search);
 }
 
 void
 Link::CreateSearchParamsIfNeeded()
 {
   if (!mSearchParams) {
-    mSearchParams = new URLSearchParams();
-    mSearchParams->AddObserver(this);
+    mSearchParams = new URLSearchParams(this);
     UpdateURLSearchParams();
   }
 }
 
 void
 Link::Unlink()
 {
   if (mSearchParams) {
-    mSearchParams->RemoveObserver(this);
     mSearchParams = nullptr;
   }
 }
 
 void
 Link::Traverse(nsCycleCollectionTraversalCallback &cb)
 {
   Link* tmp = this;
--- a/dom/base/Link.h
+++ b/dom/base/Link.h
@@ -59,17 +59,16 @@ public:
    */
   void SetProtocol(const nsAString &aProtocol, ErrorResult& aError);
   void SetUsername(const nsAString &aUsername, ErrorResult& aError);
   void SetPassword(const nsAString &aPassword, ErrorResult& aError);
   void SetHost(const nsAString &aHost, ErrorResult& aError);
   void SetHostname(const nsAString &aHostname, ErrorResult& aError);
   void SetPathname(const nsAString &aPathname, ErrorResult& aError);
   void SetSearch(const nsAString &aSearch, ErrorResult& aError);
-  void SetSearchParams(mozilla::dom::URLSearchParams& aSearchParams);
   void SetPort(const nsAString &aPort, ErrorResult& aError);
   void SetHash(const nsAString &aHash, ErrorResult& aError);
   void GetOrigin(nsAString &aOrigin, ErrorResult& aError);
   void GetProtocol(nsAString &_protocol, ErrorResult& aError);
   void GetUsername(nsAString &aUsername, ErrorResult& aError);
   void GetPassword(nsAString &aPassword, ErrorResult& aError);
   void GetHost(nsAString &_host, ErrorResult& aError);
   void GetHostname(nsAString &_hostname, ErrorResult& aError);
--- a/dom/base/PostMessageEvent.cpp
+++ b/dom/base/PostMessageEvent.cpp
@@ -233,17 +233,23 @@ PostMessageEvent::TransferStructuredClon
 
 /* static */ void
 PostMessageEvent::FreeTransferStructuredClone(uint32_t aTag,
                                               JS::TransferableOwnership aOwnership,
                                               void *aContent,
                                               uint64_t aExtraData,
                                               void* aClosure)
 {
-  // Nothing to do.
+  if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
+    MOZ_ASSERT(aClosure);
+    MOZ_ASSERT(!aContent);
+
+    StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(aClosure);
+    MessagePort::ForceClose(scInfo->event->GetPortIdentifier(aExtraData));
+  }
 }
 
 PostMessageEvent::PostMessageEvent(nsGlobalWindow* aSource,
                                    const nsAString& aCallerOrigin,
                                    nsGlobalWindow* aTargetWindow,
                                    nsIPrincipal* aProvidedPrincipal,
                                    bool aTrustedCaller)
 : mSource(aSource),
--- a/dom/base/URL.cpp
+++ b/dom/base/URL.cpp
@@ -22,17 +22,16 @@
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(URL)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(URL)
   if (tmp->mSearchParams) {
-    tmp->mSearchParams->RemoveObserver(tmp);
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mSearchParams)
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(URL)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSearchParams)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
@@ -369,17 +368,17 @@ URL::UpdateURLSearchParams()
   nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
   if (url) {
     nsresult rv = url->GetQuery(search);
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to get the query from a nsIURL.");
     }
   }
 
-  mSearchParams->ParseInput(search, this);
+  mSearchParams->ParseInput(search);
 }
 
 void
 URL::GetHostname(nsAString& aHostname, ErrorResult& aRv) const
 {
   aHostname.Truncate();
   nsContentUtils::GetHostOrIPv6WithBrackets(mURI, aHostname);
 }
@@ -496,32 +495,16 @@ URL::SetSearchInternal(const nsAString& 
 URLSearchParams*
 URL::SearchParams()
 {
   CreateSearchParamsIfNeeded();
   return mSearchParams;
 }
 
 void
-URL::SetSearchParams(URLSearchParams& aSearchParams)
-{
-  if (mSearchParams) {
-    mSearchParams->RemoveObserver(this);
-  }
-
-  // the observer will be cleared using the cycle collector.
-  mSearchParams = &aSearchParams;
-  mSearchParams->AddObserver(this);
-
-  nsAutoString search;
-  mSearchParams->Serialize(search);
-  SetSearchInternal(search);
-}
-
-void
 URL::GetHash(nsAString& aHash, ErrorResult& aRv) const
 {
   aHash.Truncate();
 
   nsAutoCString ref;
   nsresult rv = mURI->GetRef(ref);
   if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
     aHash.Assign(char16_t('#'));
@@ -545,16 +528,15 @@ bool IsChromeURI(nsIURI* aURI)
       return isChrome;
   return false;
 }
 
 void
 URL::CreateSearchParamsIfNeeded()
 {
   if (!mSearchParams) {
-    mSearchParams = new URLSearchParams();
-    mSearchParams->AddObserver(this);
+    mSearchParams = new URLSearchParams(this);
     UpdateURLSearchParams();
   }
 }
 
 }
 }
--- a/dom/base/URL.h
+++ b/dom/base/URL.h
@@ -114,18 +114,16 @@ public:
   void SetPathname(const nsAString& aPathname, ErrorResult& aRv);
 
   void GetSearch(nsAString& aRetval, ErrorResult& aRv) const;
 
   void SetSearch(const nsAString& aArg, ErrorResult& aRv);
 
   URLSearchParams* SearchParams();
 
-  void SetSearchParams(URLSearchParams& aSearchParams);
-
   void GetHash(nsAString& aRetval, ErrorResult& aRv) const;
 
   void SetHash(const nsAString& aArg, ErrorResult& aRv);
 
   void Stringify(nsAString& aRetval, ErrorResult& aRv) const
   {
     GetHref(aRetval, aRv);
   }
--- a/dom/base/URLSearchParams.cpp
+++ b/dom/base/URLSearchParams.cpp
@@ -7,26 +7,27 @@
 #include "URLSearchParams.h"
 #include "mozilla/dom/URLSearchParamsBinding.h"
 #include "mozilla/dom/EncodingUtils.h"
 #include "nsDOMString.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(URLSearchParams, mObservers)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(URLSearchParams, mObserver)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(URLSearchParams)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(URLSearchParams)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(URLSearchParams)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
-URLSearchParams::URLSearchParams()
+URLSearchParams::URLSearchParams(URLSearchParamsObserver* aObserver)
+  : mObserver(aObserver)
 {
 }
 
 URLSearchParams::~URLSearchParams()
 {
   DeleteAll();
 }
 
@@ -36,34 +37,33 @@ URLSearchParams::WrapObject(JSContext* a
   return URLSearchParamsBinding::Wrap(aCx, this, aGivenProto);
 }
 
 /* static */ already_AddRefed<URLSearchParams>
 URLSearchParams::Constructor(const GlobalObject& aGlobal,
                              const nsAString& aInit,
                              ErrorResult& aRv)
 {
-  nsRefPtr<URLSearchParams> sp = new URLSearchParams();
-  sp->ParseInput(NS_ConvertUTF16toUTF8(aInit), nullptr);
+  nsRefPtr<URLSearchParams> sp = new URLSearchParams(nullptr);
+  sp->ParseInput(NS_ConvertUTF16toUTF8(aInit));
   return sp.forget();
 }
 
 /* static */ already_AddRefed<URLSearchParams>
 URLSearchParams::Constructor(const GlobalObject& aGlobal,
                              URLSearchParams& aInit,
                              ErrorResult& aRv)
 {
-  nsRefPtr<URLSearchParams> sp = new URLSearchParams();
+  nsRefPtr<URLSearchParams> sp = new URLSearchParams(nullptr);
   sp->mSearchParams = aInit.mSearchParams;
   return sp.forget();
 }
 
 void
-URLSearchParams::ParseInput(const nsACString& aInput,
-                            URLSearchParamsObserver* aObserver)
+URLSearchParams::ParseInput(const nsACString& aInput)
 {
   // Remove all the existing data before parsing a new input.
   DeleteAll();
 
   nsACString::const_iterator start, end;
   aInput.BeginReading(start);
   aInput.EndReading(end);
   nsACString::const_iterator iter(start);
@@ -103,18 +103,16 @@ URLSearchParams::ParseInput(const nsACSt
     nsAutoString decodedName;
     DecodeString(name, decodedName);
 
     nsAutoString decodedValue;
     DecodeString(value, decodedValue);
 
     AppendInternal(decodedName, decodedValue);
   }
-
-  NotifyObservers(aObserver);
 }
 
 void
 URLSearchParams::DecodeString(const nsACString& aInput, nsAString& aOutput)
 {
   nsACString::const_iterator start, end;
   aInput.BeginReading(start);
   aInput.EndReading(end);
@@ -204,37 +202,16 @@ URLSearchParams::ConvertString(const nsA
   }
 
   if (newOutputLength < outputLength) {
     aOutput.Truncate(newOutputLength);
   }
 }
 
 void
-URLSearchParams::AddObserver(URLSearchParamsObserver* aObserver)
-{
-  MOZ_ASSERT(aObserver);
-  MOZ_ASSERT(!mObservers.Contains(aObserver));
-  mObservers.AppendElement(aObserver);
-}
-
-void
-URLSearchParams::RemoveObserver(URLSearchParamsObserver* aObserver)
-{
-  MOZ_ASSERT(aObserver);
-  mObservers.RemoveElement(aObserver);
-}
-
-void
-URLSearchParams::RemoveObservers()
-{
-  mObservers.Clear();
-}
-
-void
 URLSearchParams::Get(const nsAString& aName, nsString& aRetval)
 {
   SetDOMStringToNull(aRetval);
 
   for (uint32_t i = 0, len = mSearchParams.Length(); i < len; ++i) {
     if (mSearchParams[i].mKey.Equals(aName)) {
       aRetval.Assign(mSearchParams[i].mValue);
       break;
@@ -275,24 +252,24 @@ URLSearchParams::Set(const nsAString& aN
 
   if (!param) {
     param = mSearchParams.AppendElement();
     param->mKey = aName;
   }
 
   param->mValue = aValue;
 
-  NotifyObservers(nullptr);
+  NotifyObserver();
 }
 
 void
 URLSearchParams::Append(const nsAString& aName, const nsAString& aValue)
 {
   AppendInternal(aName, aValue);
-  NotifyObservers(nullptr);
+  NotifyObserver();
 }
 
 void
 URLSearchParams::AppendInternal(const nsAString& aName, const nsAString& aValue)
 {
   Param* param = mSearchParams.AppendElement();
   param->mKey = aName;
   param->mValue = aValue;
@@ -319,17 +296,17 @@ URLSearchParams::Delete(const nsAString&
       mSearchParams.RemoveElementAt(i);
       found = true;
     } else {
       ++i;
     }
   }
 
   if (found) {
-    NotifyObservers(nullptr);
+    NotifyObserver();
   }
 }
 
 void
 URLSearchParams::DeleteAll()
 {
   mSearchParams.Clear();
 }
@@ -375,19 +352,17 @@ URLSearchParams::Serialize(nsAString& aV
 
     SerializeString(NS_ConvertUTF16toUTF8(mSearchParams[i].mKey), aValue);
     aValue.Append('=');
     SerializeString(NS_ConvertUTF16toUTF8(mSearchParams[i].mValue), aValue);
   }
 }
 
 void
-URLSearchParams::NotifyObservers(URLSearchParamsObserver* aExceptObserver)
+URLSearchParams::NotifyObserver()
 {
-  for (uint32_t i = 0; i < mObservers.Length(); ++i) {
-    if (mObservers[i] != aExceptObserver) {
-      mObservers[i]->URLSearchParamsUpdated(this);
-    }
+  if (mObserver) {
+    mObserver->URLSearchParamsUpdated(this);
   }
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/URLSearchParams.h
+++ b/dom/base/URLSearchParams.h
@@ -35,17 +35,17 @@ class URLSearchParams final : public nsI
                               public nsWrapperCache
 {
   ~URLSearchParams();
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(URLSearchParams)
 
-  URLSearchParams();
+  explicit URLSearchParams(URLSearchParamsObserver* aObserver);
 
   // WebIDL methods
   nsISupports* GetParentObject() const
   {
     return nullptr;
   }
 
   virtual JSObject*
@@ -54,22 +54,17 @@ public:
   static already_AddRefed<URLSearchParams>
   Constructor(const GlobalObject& aGlobal, const nsAString& aInit,
               ErrorResult& aRv);
 
   static already_AddRefed<URLSearchParams>
   Constructor(const GlobalObject& aGlobal, URLSearchParams& aInit,
               ErrorResult& aRv);
 
-  void ParseInput(const nsACString& aInput,
-                  URLSearchParamsObserver* aObserver);
-
-  void AddObserver(URLSearchParamsObserver* aObserver);
-  void RemoveObserver(URLSearchParamsObserver* aObserver);
-  void RemoveObservers();
+  void ParseInput(const nsACString& aInput);
 
   void Serialize(nsAString& aValue) const;
 
   void Get(const nsAString& aName, nsString& aRetval);
 
   void GetAll(const nsAString& aName, nsTArray<nsString >& aRetval);
 
   void Set(const nsAString& aName, const nsAString& aValue);
@@ -108,26 +103,26 @@ public:
 private:
   void AppendInternal(const nsAString& aName, const nsAString& aValue);
 
   void DeleteAll();
 
   void DecodeString(const nsACString& aInput, nsAString& aOutput);
   void ConvertString(const nsACString& aInput, nsAString& aOutput);
 
-  void NotifyObservers(URLSearchParamsObserver* aExceptObserver);
+  void NotifyObserver();
 
   struct Param
   {
     nsString mKey;
     nsString mValue;
   };
 
   nsTArray<Param> mSearchParams;
 
-  nsTArray<nsRefPtr<URLSearchParamsObserver>> mObservers;
+  nsRefPtr<URLSearchParamsObserver> mObserver;
   nsCOMPtr<nsIUnicodeDecoder> mDecoder;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_URLSearchParams_h */
--- a/dom/base/nsLocation.cpp
+++ b/dom/base/nsLocation.cpp
@@ -56,37 +56,33 @@ nsLocation::nsLocation(nsPIDOMWindow* aW
   MOZ_ASSERT(aDocShell);
   MOZ_ASSERT(mInnerWindow->IsInnerWindow());
 
   mDocShell = do_GetWeakReference(aDocShell);
 }
 
 nsLocation::~nsLocation()
 {
-  RemoveURLSearchParams();
 }
 
 // QueryInterface implementation for nsLocation
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsLocation)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsIDOMLocation)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMLocation)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsLocation)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsLocation)
-  tmp->RemoveURLSearchParams();
-
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mInnerWindow);
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsLocation)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSearchParams)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInnerWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsLocation)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsLocation)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsLocation)
@@ -1052,124 +1048,8 @@ nsLocation::CallerSubsumes()
   return subsumes;
 }
 
 JSObject*
 nsLocation::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return LocationBinding::Wrap(aCx, this, aGivenProto);
 }
-
-URLSearchParams*
-nsLocation::GetDocShellSearchParams()
-{
-  nsCOMPtr<nsIDocShell> docShell = GetDocShell();
-  if (!docShell) {
-    return nullptr;
-  }
-
-  return docShell->GetURLSearchParams();
-}
-
-URLSearchParams*
-nsLocation::SearchParams()
-{
-  if (!mSearchParams) {
-    // We must register this object to the URLSearchParams of the docshell in
-    // order to receive updates.
-    nsRefPtr<URLSearchParams> searchParams = GetDocShellSearchParams();
-    if (searchParams) {
-      searchParams->AddObserver(this);
-    }
-
-    mSearchParams = new URLSearchParams();
-    mSearchParams->AddObserver(this);
-    UpdateURLSearchParams();
-  }
-
-  return mSearchParams;
-}
-
-void
-nsLocation::SetSearchParams(URLSearchParams& aSearchParams)
-{
-  if (mSearchParams) {
-    mSearchParams->RemoveObserver(this);
-  }
-
-  // the observer will be cleared using the cycle collector.
-  mSearchParams = &aSearchParams;
-  mSearchParams->AddObserver(this);
-
-  nsAutoString search;
-  mSearchParams->Serialize(search);
-  SetSearchInternal(search);
-
-  // We don't need to inform the docShell about this new SearchParams because
-  // setting the new value the docShell will refresh its value automatically.
-}
-
-void
-nsLocation::URLSearchParamsUpdated(URLSearchParams* aSearchParams)
-{
-  MOZ_ASSERT(mSearchParams);
-
-  // This change comes from content.
-  if (aSearchParams == mSearchParams) {
-    nsAutoString search;
-    mSearchParams->Serialize(search);
-    SetSearchInternal(search);
-    return;
-  }
-
-  // This change comes from the docShell.
-#ifdef DEBUG
-  {
-    nsRefPtr<URLSearchParams> searchParams = GetDocShellSearchParams();
-    MOZ_ASSERT(searchParams);
-    MOZ_ASSERT(aSearchParams == searchParams);
-  }
-#endif
-
-  nsAutoString search;
-  aSearchParams->Serialize(search);
-  mSearchParams->ParseInput(NS_ConvertUTF16toUTF8(search), this);
-}
-
-void
-nsLocation::UpdateURLSearchParams()
-{
-  if (!mSearchParams) {
-    return;
-  }
-
-  nsAutoCString search;
-
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv = GetURI(getter_AddRefs(uri));
-  if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!uri)) {
-    return;
-  }
-
-  nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
-  if (url) {
-    nsresult rv = url->GetQuery(search);
-    if (NS_FAILED(rv)) {
-      NS_WARNING("Failed to get the query from a nsIURL.");
-    }
-  }
-
-  mSearchParams->ParseInput(search, this);
-}
-
-void
-nsLocation::RemoveURLSearchParams()
-{
-  if (mSearchParams) {
-    mSearchParams->RemoveObserver(this);
-    mSearchParams = nullptr;
-
-    nsRefPtr<URLSearchParams> docShellSearchParams = GetDocShellSearchParams();
-    if (docShellSearchParams) {
-      docShellSearchParams->RemoveObserver(this);
-    }
-  }
-}
--- a/dom/base/nsLocation.h
+++ b/dom/base/nsLocation.h
@@ -9,30 +9,28 @@
 
 #include "nsIDOMLocation.h"
 #include "nsString.h"
 #include "nsIWeakReferenceUtils.h"
 #include "nsWrapperCache.h"
 #include "nsCycleCollectionParticipant.h"
 #include "js/TypeDecls.h"
 #include "mozilla/ErrorResult.h"
-#include "mozilla/dom/URLSearchParams.h"
 #include "nsPIDOMWindow.h"
 
 class nsIURI;
 class nsIDocShell;
 class nsIDocShellLoadInfo;
 
 //*****************************************************************************
 // nsLocation: Script "location" object
 //*****************************************************************************
 
 class nsLocation final : public nsIDOMLocation
                        , public nsWrapperCache
-                       , public mozilla::dom::URLSearchParamsObserver
 {
   typedef mozilla::ErrorResult ErrorResult;
 
 public:
   nsLocation(nsPIDOMWindow* aWindow, nsIDocShell *aDocShell);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsLocation,
@@ -117,20 +115,16 @@ public:
   {
     aError = GetSearch(aSeach);
   }
   void SetSearch(const nsAString& aSeach, ErrorResult& aError)
   {
     aError = SetSearch(aSeach);
   }
 
-  mozilla::dom::URLSearchParams* SearchParams();
-
-  void SetSearchParams(mozilla::dom::URLSearchParams& aSearchParams);
-
   void GetHash(nsAString& aHash, ErrorResult& aError)
   {
     aError = GetHash(aHash);
   }
   void SetHash(const nsAString& aHash, ErrorResult& aError)
   {
     aError = SetHash(aHash);
   }
@@ -139,27 +133,20 @@ public:
     GetHref(aRetval, aError);
   }
   nsPIDOMWindow* GetParentObject() const
   {
     return mInnerWindow;
   }
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
-  // URLSearchParamsObserver
-  void URLSearchParamsUpdated(mozilla::dom::URLSearchParams* aSearchParams) override;
-
 protected:
   virtual ~nsLocation();
 
   nsresult SetSearchInternal(const nsAString& aSearch);
-  void UpdateURLSearchParams();
-  void RemoveURLSearchParams();
-
-  mozilla::dom::URLSearchParams* GetDocShellSearchParams();
 
   // In the case of jar: uris, we sometimes want the place the jar was
   // fetched from as the URI instead of the jar: uri itself.  Pass in
   // true for aGetInnermostURI when that's the case.
   nsresult GetURI(nsIURI** aURL, bool aGetInnermostURI = false);
   nsresult GetWritableURI(nsIURI** aURL);
   nsresult SetURI(nsIURI* aURL, bool aReplace = false);
   nsresult SetHrefWithBase(const nsAString& aHref, nsIURI* aBase,
@@ -168,14 +155,12 @@ protected:
                               bool aReplace);
 
   nsresult GetSourceBaseURL(JSContext* cx, nsIURI** sourceURL);
   nsresult CheckURL(nsIURI *url, nsIDocShellLoadInfo** aLoadInfo);
   bool CallerSubsumes();
 
   nsString mCachedHash;
   nsCOMPtr<nsPIDOMWindow> mInnerWindow;
-  nsRefPtr<mozilla::dom::URLSearchParams> mSearchParams;
   nsWeakPtr mDocShell;
 };
 
 #endif // nsLocation_h__
-
--- a/dom/base/test/test_XHRDocURI.html
+++ b/dom/base/test/test_XHRDocURI.html
@@ -4,34 +4,41 @@
 https://bugzilla.mozilla.org/show_bug.cgi?id=459470
 -->
 <head>
   <title>XMLHttpRequest return document URIs</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <base href="http://example.org/">
 </head>
-<body>
+<body onload="startTest();">
 <a target="_blank"
    href="https://bugzilla.mozilla.org/show_bug.cgi?id=459470">Mozilla Bug 459470</a><br />
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=859095">Mozilla Bug 859095</a>
 
 <p id="display">
 <iframe id=loader></iframe>
 </p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script class="testbody" type="application/javascript;version=1.8">
 
 SimpleTest.waitForExplicitFinish();
+var gen;
 
-gen = runTest();
-gen.next();
+function startTest() {
+  // The test uses history API, so don't do anything before load event has been
+  // handled.
+  SimpleTest.executeSoon(function() {
+    gen = runTest();
+    gen.next();
+  });
+}
 
 function testXMLDocURI(aDoc, aExpects) {
   is(aDoc.documentURI, aExpects.documentURI, "wrong url");
   is(aDoc.baseURI, aExpects.baseURI, "wrong base");
   is(aDoc.documentElement.baseURI, aExpects.elementBaseURI,
      "wrong base (xml:base)");
 }
 
--- a/dom/base/test/test_urlSearchParams.html
+++ b/dom/base/test/test_urlSearchParams.html
@@ -119,140 +119,51 @@ https://bugzilla.mozilla.org/show_bug.cg
     ok(url.searchParams.has('a'), "URL.searchParams.has('a')");
     is(url.searchParams.get('a'), 'b', "URL.searchParams.get('a')");
     ok(url.searchParams.has('c'), "URL.searchParams.has('c')");
     is(url.searchParams.get('c'), 'd', "URL.searchParams.get('c')");
 
     url.searchParams.set('e', 'f');
     ok(url.href.indexOf('e=f') != 1, 'URL right');
 
-    var u = new URLSearchParams();
-    u.append('foo', 'bar');
-    url.searchParams = u;
-    is(url.searchParams, u, "URL.searchParams is the same object");
-    is(url.searchParams.get('foo'), 'bar', "URL.searchParams.get('foo')");
-    is(url.href, 'http://www.example.net/?foo=bar', 'URL right');
-
-    try {
-      url.searchParams = null;
-      ok(false, "URLSearchParams is not nullable");
-    } catch(e) {
-      ok(true, "URLSearchParams is not nullable");
-    }
-
-    var url2 = new URL('http://www.example.net?e=f');
-    url.searchParams = url2.searchParams;
-    is(url.searchParams, url2.searchParams, "URL.searchParams is not the same object");
-    is(url.searchParams.get('e'), 'f', "URL.searchParams.get('e')");
-
-    url.href = "http://www.example.net?bar=foo";
-    is(url.searchParams.get('bar'), 'foo', "URL.searchParams.get('bar')");
-
     runTest();
   }
 
   function testElement(e) {
     ok(e, 'element exists');
     ok(e.searchParams, "e.searchParams exists!");
     ok(e.searchParams.has('a'), "e.searchParams.has('a')");
     is(e.searchParams.get('a'), 'b', "e.searchParams.get('a')");
     ok(e.searchParams.has('c'), "e.searchParams.has('c')");
     is(e.searchParams.get('c'), 'd', "e.searchParams.get('c')");
 
     e.searchParams.set('e', 'f');
     ok(e.href.indexOf('e=f') != 1, 'e is right');
 
-    var u = new URLSearchParams();
-    u.append('foo', 'bar');
-    e.searchParams = u;
-    is(e.searchParams, u, "e.searchParams is the same object");
-    is(e.searchParams.get('foo'), 'bar', "e.searchParams.get('foo')");
-    is(e.href, 'http://www.example.net/?foo=bar', 'e is right');
-
-    try {
-      e.searchParams = null;
-      ok(false, "URLSearchParams is not nullable");
-    } catch(e) {
-      ok(true, "URLSearchParams is not nullable");
-    }
-
-    var url2 = new URL('http://www.example.net?e=f');
-    e.searchParams = url2.searchParams;
-    is(e.searchParams, url2.searchParams, "e.searchParams is not the same object");
-    is(e.searchParams.get('e'), 'f', "e.searchParams.get('e')");
-
-    e.href = "http://www.example.net?bar=foo";
-    is(e.searchParams.get('bar'), 'foo', "e.searchParams.get('bar')");
-
-    e.setAttribute('href', "http://www.example.net?bar2=foo2");
-    is(e.searchParams.get('bar2'), 'foo2', "e.searchParams.get('bar2')");
-
     runTest();
   }
 
   function testEncoding() {
     var encoding = [ [ '1', '1' ],
                      [ 'a b', 'a+b' ],
                      [ '<>', '%3C%3E' ],
                      [ '\u0541', '%D5%81'] ];
 
     for (var i = 0; i < encoding.length; ++i) {
-      var a = new URLSearchParams();
-      a.set('a', encoding[i][0]);
-
       var url = new URL('http://www.example.net');
-      url.searchParams = a;
+      url.searchParams.set('a', encoding[i][0]);
       is(url.href, 'http://www.example.net/?a=' + encoding[i][1]);
 
       var url2 = new URL(url.href);
       is(url2.searchParams.get('a'), encoding[i][0], 'a is still there');
     }
 
     runTest();
   }
 
-  function testMultiURL() {
-    var a = new URL('http://www.example.net?a=b&c=d');
-    var b = new URL('http://www.example.net?e=f');
-    var c = document.createElement('a');
-    var d = document.createElement('area');
-    ok(a.searchParams.has('a'), "a.searchParams.has('a')");
-    ok(a.searchParams.has('c'), "a.searchParams.has('c')");
-    ok(b.searchParams.has('e'), "b.searchParams.has('e')");
-    ok(c.searchParams, "c.searchParams");
-    ok(d.searchParams, "d.searchParams");
-
-    var u = new URLSearchParams();
-    a.searchParams = b.searchParams = c.searchParams = d.searchParams = u;
-    is(a.searchParams, u, "a.searchParams === u");
-    is(b.searchParams, u, "b.searchParams === u");
-    is(c.searchParams, u, "c.searchParams === u");
-    is(d.searchParams, u, "d.searchParams === u");
-    ok(!a.searchParams.has('a'), "!a.searchParams.has('a')");
-    ok(!a.searchParams.has('c'), "!a.searchParams.has('c')");
-    ok(!b.searchParams.has('e'), "!b.searchParams.has('e')");
-
-    u.append('foo', 'bar');
-    is(a.searchParams.get('foo'), 'bar', "a has foo=bar");
-    is(b.searchParams.get('foo'), 'bar', "b has foo=bar");
-    is(c.searchParams.get('foo'), 'bar', "c has foo=bar");
-    is(d.searchParams.get('foo'), 'bar', "d has foo=bar");
-    is(a + "", b + "", "stringify a == b");
-    is(c.searchParams + "", b.searchParams + "", "stringify c.searchParams == b.searchParams");
-    is(d.searchParams + "", b.searchParams + "", "stringify d.searchParams == b.searchParams");
-
-    a.search = "?bar=foo";
-    is(a.searchParams.get('bar'), 'foo', "a has bar=foo");
-    is(b.searchParams.get('bar'), 'foo', "b has bar=foo");
-    is(c.searchParams.get('bar'), 'foo', "c has bar=foo");
-    is(d.searchParams.get('bar'), 'foo', "d has bar=foo");
-
-    runTest();
-  }
-
   function testOrdering() {
     var a = new URLSearchParams("a=1&a=2&b=3&c=4&c=5&a=6");
     is(a.toString(), "a=1&a=2&b=3&c=4&c=5&a=6", "Order is correct");
     is(a.getAll('a').length, 3, "Correct length of getAll()");
 
     var b = new URLSearchParams();
     b.append('a', '1');
     b.append('b', '2');
@@ -329,17 +240,16 @@ https://bugzilla.mozilla.org/show_bug.cg
   var tests = [
     testSimpleURLSearchParams,
     testCopyURLSearchParams,
     testParserURLSearchParams,
     testURL,
     function() { testElement(document.getElementById('anchor')) },
     function() { testElement(document.getElementById('area')) },
     testEncoding,
-    testMultiURL,
     testOrdering,
     testDelete,
     testGetNULL,
     testSet
   ];
 
   function runTest() {
     if (!tests.length) {
--- a/dom/bindings/DOMJSProxyHandler.cpp
+++ b/dom/bindings/DOMJSProxyHandler.cpp
@@ -176,22 +176,22 @@ DOMProxyHandler::defineProperty(JSContex
   if (desc.hasGetterObject() && desc.setter() == JS_StrictPropertyStub) {
     return result.failGetterOnly();
   }
 
   if (xpc::WrapperFactory::IsXrayWrapper(proxy)) {
     return result.succeed();
   }
 
-  JSObject* expando = EnsureExpandoObject(cx, proxy);
+  JS::Rooted<JSObject*> expando(cx, EnsureExpandoObject(cx, proxy));
   if (!expando) {
     return false;
   }
 
-  if (!js::DefineOwnProperty(cx, expando, id, desc, result)) {
+  if (!JS_DefinePropertyById(cx, expando, id, desc, result)) {
     return false;
   }
   *defined = true;
   return true;
 }
 
 bool
 DOMProxyHandler::set(JSContext *cx, Handle<JSObject*> proxy, Handle<jsid> id,
--- a/dom/bindings/TypedArray.h
+++ b/dom/bindings/TypedArray.h
@@ -185,16 +185,58 @@ private:
       memcpy(buf, data, length*sizeof(T));
     }
     return obj;
   }
 
   TypedArray(const TypedArray&) = delete;
 };
 
+template<JSObject* UnwrapArray(JSObject*),
+         void GetLengthAndData(JSObject*, uint32_t*, uint8_t**),
+         js::Scalar::Type GetViewType(JSObject*)>
+struct ArrayBufferView_base : public TypedArray_base<uint8_t, UnwrapArray,
+                                                     GetLengthAndData> {
+private:
+  typedef TypedArray_base<uint8_t, UnwrapArray, GetLengthAndData> Base;
+
+public:
+  ArrayBufferView_base()
+    : Base()
+  {
+  }
+
+  explicit ArrayBufferView_base(ArrayBufferView_base&& aOther)
+    : Base(Move(aOther)),
+      mType(aOther.mType)
+  {
+    aOther.mType = js::Scalar::MaxTypedArrayViewType;
+  }
+
+private:
+  js::Scalar::Type mType;
+
+public:
+  inline bool Init(JSObject* obj)
+  {
+    if (!Base::Init(obj)) {
+      return false;
+    }
+
+    mType = GetViewType(this->Obj());
+    return true;
+  }
+
+  inline js::Scalar::Type Type() const
+  {
+    MOZ_ASSERT(this->inited());
+    return mType;
+  }
+};
+
 typedef TypedArray<int8_t, js::UnwrapInt8Array, JS_GetInt8ArrayData,
                    js::GetInt8ArrayLengthAndData, JS_NewInt8Array>
         Int8Array;
 typedef TypedArray<uint8_t, js::UnwrapUint8Array, JS_GetUint8ArrayData,
                    js::GetUint8ArrayLengthAndData, JS_NewUint8Array>
         Uint8Array;
 typedef TypedArray<uint8_t, js::UnwrapUint8ClampedArray, JS_GetUint8ClampedArrayData,
                    js::GetUint8ClampedArrayLengthAndData, JS_NewUint8ClampedArray>
@@ -212,17 +254,19 @@ typedef TypedArray<uint32_t, js::UnwrapU
                    js::GetUint32ArrayLengthAndData, JS_NewUint32Array>
         Uint32Array;
 typedef TypedArray<float, js::UnwrapFloat32Array, JS_GetFloat32ArrayData,
                    js::GetFloat32ArrayLengthAndData, JS_NewFloat32Array>
         Float32Array;
 typedef TypedArray<double, js::UnwrapFloat64Array, JS_GetFloat64ArrayData,
                    js::GetFloat64ArrayLengthAndData, JS_NewFloat64Array>
         Float64Array;
-typedef TypedArray_base<uint8_t, js::UnwrapArrayBufferView, js::GetArrayBufferViewLengthAndData>
+typedef ArrayBufferView_base<js::UnwrapArrayBufferView,
+                             js::GetArrayBufferViewLengthAndData,
+                             JS_GetArrayBufferViewType>
         ArrayBufferView;
 typedef TypedArray<uint8_t, js::UnwrapArrayBuffer, JS_GetArrayBufferData,
                    js::GetArrayBufferLengthAndData, JS_NewArrayBuffer>
         ArrayBuffer;
 
 typedef TypedArray<int8_t, js::UnwrapSharedInt8Array, JS_GetSharedInt8ArrayData,
                    js::GetSharedInt8ArrayLengthAndData, JS_NewSharedInt8Array>
         SharedInt8Array;
@@ -245,17 +289,19 @@ typedef TypedArray<uint32_t, js::UnwrapS
                    js::GetSharedUint32ArrayLengthAndData, JS_NewSharedUint32Array>
         SharedUint32Array;
 typedef TypedArray<float, js::UnwrapSharedFloat32Array, JS_GetSharedFloat32ArrayData,
                    js::GetSharedFloat32ArrayLengthAndData, JS_NewSharedFloat32Array>
         SharedFloat32Array;
 typedef TypedArray<double, js::UnwrapSharedFloat64Array, JS_GetSharedFloat64ArrayData,
                    js::GetSharedFloat64ArrayLengthAndData, JS_NewSharedFloat64Array>
         SharedFloat64Array;
-typedef TypedArray_base<uint8_t, js::UnwrapSharedArrayBufferView, js::GetSharedArrayBufferViewLengthAndData>
+typedef ArrayBufferView_base<js::UnwrapSharedArrayBufferView,
+                             js::GetSharedArrayBufferViewLengthAndData,
+                             JS_GetSharedArrayBufferViewType>
         SharedArrayBufferView;
 typedef TypedArray<uint8_t, js::UnwrapSharedArrayBuffer, JS_GetSharedArrayBufferData,
                    js::GetSharedArrayBufferLengthAndData, JS_NewSharedArrayBuffer>
         SharedArrayBuffer;
 
 // A class for converting an nsTArray to a TypedArray
 // Note: A TypedArrayCreator must not outlive the nsTArray it was created from.
 //       So this is best used to pass from things that understand nsTArray to
--- a/dom/canvas/WebGL2ContextTextures.cpp
+++ b/dom/canvas/WebGL2ContextTextures.cpp
@@ -219,17 +219,17 @@ WebGL2Context::TexImage3D(GLenum target,
         dataLength = 0;
         jsArrayType = js::Scalar::MaxTypedArrayViewType;
     } else {
         const ArrayBufferView& view = pixels.Value();
         view.ComputeLengthAndData();
 
         data = view.Data();
         dataLength = view.Length();
-        jsArrayType = JS_GetArrayBufferViewType(view.Obj());
+        jsArrayType = view.Type();
     }
 
     const WebGLTexImageFunc func = WebGLTexImageFunc::TexImage;
     const WebGLTexDimensions dims = WebGLTexDimensions::Tex3D;
 
     if (!ValidateTexImageTarget(target, func, dims))
         return;
 
@@ -362,17 +362,17 @@ WebGL2Context::TexSubImage3D(GLenum rawT
     {
         return;
     }
 
     if (type != existingType) {
         return ErrorInvalidOperation("texSubImage3D: type differs from that of the existing image");
     }
 
-    js::Scalar::Type jsArrayType = JS_GetArrayBufferViewType(view.Obj());
+    js::Scalar::Type jsArrayType = view.Type();
     void* data = view.Data();
     size_t dataLength = view.Length();
 
     if (!ValidateTexInputData(type, jsArrayType, func, dims))
         return;
 
     const size_t bitsPerTexel = GetBitsPerTexel(existingEffectiveInternalFormat);
     MOZ_ASSERT((bitsPerTexel % 8) == 0); // should not have compressed formats here.
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -2028,17 +2028,17 @@ WebGLContext::ReadPixels(GLint x, GLint 
         requiredDataType = js::Scalar::Uint16;
         break;
 
     default:
         MOZ_CRASH("bad `type`");
     }
 
     const ArrayBufferView& pixbuf = pixels.Value();
-    int dataType = JS_GetArrayBufferViewType(pixbuf.Obj());
+    int dataType = pixbuf.Type();
 
     // Check the pixels param type
     if (dataType != requiredDataType)
         return ErrorInvalidOperation("readPixels: Mismatched type/pixels types");
 
     // Check the pixels param size
     CheckedUint32 checked_neededByteLength =
         GetImageSize(height, width, 1, bytesPerPixel, mPixelStorePackAlignment);
@@ -3406,17 +3406,17 @@ WebGLContext::TexImage2D(GLenum rawTarge
         length = 0;
         jsArrayType = js::Scalar::MaxTypedArrayViewType;
     } else {
         const ArrayBufferView& view = pixels.Value();
         view.ComputeLengthAndData();
 
         data = view.Data();
         length = view.Length();
-        jsArrayType = JS_GetArrayBufferViewType(view.Obj());
+        jsArrayType = view.Type();
     }
 
     if (!ValidateTexImageTarget(rawTarget, WebGLTexImageFunc::TexImage, WebGLTexDimensions::Tex2D))
         return;
 
     return TexImage2D_base(rawTarget, level, internalformat, width, height, 0, border, format, type,
                            data, length, jsArrayType,
                            WebGLTexelFormat::Auto, false);
@@ -3605,18 +3605,17 @@ WebGLContext::TexSubImage2D(GLenum rawTa
     const ArrayBufferView& view = pixels.Value();
     view.ComputeLengthAndData();
 
     if (!ValidateTexImageTarget(rawTarget, WebGLTexImageFunc::TexSubImage, WebGLTexDimensions::Tex2D))
         return;
 
     return TexSubImage2D_base(rawTarget, level, xoffset, yoffset,
                               width, height, 0, format, type,
-                              view.Data(), view.Length(),
-                              JS_GetArrayBufferViewType(view.Obj()),
+                              view.Data(), view.Length(), view.Type(),
                               WebGLTexelFormat::Auto, false);
 }
 
 void
 WebGLContext::TexSubImage2D(GLenum target, GLint level,
                             GLint xoffset, GLint yoffset,
                             GLenum format, GLenum type, ImageData* pixels,
                             ErrorResult& rv)
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -1595,18 +1595,18 @@ FetchBody<Derived>::ContinueConsumeBody(
         NS_NAMED_LITERAL_CSTRING(urlDataMimeType, "application/x-www-form-urlencoded");
         bool isValidUrlEncodedMimeType = StringBeginsWith(mMimeType, urlDataMimeType);
 
         if (isValidUrlEncodedMimeType && mMimeType.Length() > urlDataMimeType.Length()) {
           isValidUrlEncodedMimeType = mMimeType[urlDataMimeType.Length()] == ';';
         }
 
         if (isValidUrlEncodedMimeType) {
-          nsRefPtr<URLSearchParams> params = new URLSearchParams();
-          params->ParseInput(data, /* aObserver */ nullptr);
+          nsRefPtr<URLSearchParams> params = new URLSearchParams(nullptr);
+          params->ParseInput(data);
 
           nsRefPtr<nsFormData> fd = new nsFormData(DerivedClass()->GetParentObject());
           FillFormIterator iterator(fd);
           DebugOnly<bool> status = params->ForEach(iterator);
           MOZ_ASSERT(status);
 
           localPromise->MaybeResolve(fd);
         } else {
--- a/dom/html/HTMLAreaElement.h
+++ b/dom/html/HTMLAreaElement.h
@@ -153,17 +153,16 @@ public:
 
   using Link::GetSearch;
   using Link::SetSearch;
 
   using Link::GetHash;
   using Link::SetHash;
 
   // The Link::GetSearchParams is OK for us
-  // The Link::SetSearchParams is OK for us
 
   bool NoHref() const
   {
     return GetBoolAttr(nsGkAtoms::nohref);
   }
 
   void SetNoHref(bool aValue, ErrorResult& aError)
   {
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -15,30 +15,30 @@ interface nsIURI;
 interface nsIServiceWorkerUnregisterCallback : nsISupports
 {
   // aState is true if the unregistration succeded.
   // It's false if this ServiceWorkerRegistration doesn't exist.
   void unregisterSucceeded(in bool aState);
   void unregisterFailed();
 };
 
-[scriptable, builtinclass, uuid(103763c8-53ba-42e4-8b26-e601d5bc4afe)]
+[scriptable, builtinclass, uuid(e633b73b-a734-4d04-a09c-b7779a439f3f)]
 interface nsIServiceWorkerInfo : nsISupports
 {
   readonly attribute nsIPrincipal principal;
 
   readonly attribute DOMString scope;
   readonly attribute DOMString scriptSpec;
   readonly attribute DOMString currentWorkerURL;
 
   readonly attribute DOMString activeCacheName;
   readonly attribute DOMString waitingCacheName;
 };
 
-[scriptable, builtinclass, uuid(aee94712-9adb-4c0b-80a7-a8df34dfa2e8)]
+[scriptable, builtinclass, uuid(e9abb123-0099-4d9e-85db-c8cd0aff19e6)]
 interface nsIServiceWorkerManager : nsISupports
 {
   /**
    * Registers a ServiceWorker with script loaded from `aScriptURI` to act as
    * the ServiceWorker for aScope.  Requires a valid entry settings object on
    * the stack. This means you must call this from content code 'within'
    * a window.
    *
@@ -121,20 +121,20 @@ interface nsIServiceWorkerManager : nsIS
 
   // Note: This is meant to be used only by about:serviceworkers.
   // It calls unregister() in each child process. The callback is used to
   // inform when unregister() is completed on the current process.
   void propagateUnregister(in nsIPrincipal aPrincipal,
                            in nsIServiceWorkerUnregisterCallback aCallback,
                            in DOMString aScope);
 
-  [implicit_jscontext] void sendPushEvent(in jsval aOriginAttributes,
-                                          in ACString aScope,
-                                          in DOMString aData);
-  [implicit_jscontext] void sendPushSubscriptionChangeEvent(in jsval aOriginAttributes,
-                                                            in ACString scope);
+  void sendPushEvent(in ACString aOriginAttributes,
+                     in ACString aScope,
+                     in DOMString aData);
+  void sendPushSubscriptionChangeEvent(in ACString aOriginAttributes,
+                                       in ACString scope);
 
   void updateAllRegistrations();
 };
 
 %{ C++
 #define SERVICEWORKERMANAGER_CONTRACTID "@mozilla.org/serviceworkers/manager;1"
 %}
--- a/dom/interfaces/push/nsIPushNotificationService.idl
+++ b/dom/interfaces/push/nsIPushNotificationService.idl
@@ -6,17 +6,17 @@
 #include "nsISupports.idl"
 
 /**
  * A service for components to subscribe and receive push messages from web
  * services. This functionality is exposed to content via the Push API, which
  * uses service workers to notify applications. This interface exists to allow
  * privileged code to receive messages without migrating to service workers.
  */
-[scriptable, uuid(3da6a16c-69f8-4843-9149-1e89d58a53e2)]
+[scriptable, uuid(abde228b-7d14-4cab-b1f9-9f87750ede0f)]
 interface nsIPushNotificationService : nsISupports
 {
   /**
    * Creates a push subscription for the given |scope| URL and |pageURL|.
    * Returns a promise for the new subscription record, or the existing
    * record if this |scope| already has a subscription.
    *
    * The |pushEndpoint| property of the subscription record is a URL string
@@ -27,28 +27,28 @@ interface nsIPushNotificationService : n
    * notification, with an `nsIPushObserverNotification` as the subject and
    * the |scope| as the data.
    *
    * If the server drops a subscription, a `push-subscription-change` observer
    * will be fired, with the subject set to `null` and the data set to |scope|.
    * Servers may drop subscriptions at any time, so callers should recreate
    * subscriptions if desired.
    */
-  jsval register(in string scope, [optional] in string pageURL);
+  jsval register(in string scope, in jsval originAttributes);
 
   /**
    * Revokes a push subscription for the given |scope|. Returns a promise
    * for the revoked subscription record, or `null` if the |scope| is not
    * subscribed to receive notifications.
    */
-  jsval unregister(in string scope);
+  jsval unregister(in string scope, in jsval originAttributes);
 
   /**
    * Returns a promise for the subscription record associated with the
    * given |scope|, or `null` if the |scope| does not have a subscription.
    */
-  jsval registration(in string scope);
+  jsval registration(in string scope, in jsval originAttributes);
 
   /**
    * Clear all subscriptions
    */
    jsval clearAll();
 };
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -2622,43 +2622,61 @@ ContentParent::RecvSetClipboard(const IP
         rv = dataWrapper->SetData(text);
         NS_ENSURE_SUCCESS(rv, true);
 
         rv = trans->SetTransferData(item.flavor().get(), dataWrapper,
                                     text.Length() * sizeof(char16_t));
 
         NS_ENSURE_SUCCESS(rv, true);
       } else if (item.data().type() == IPCDataTransferData::TnsCString) {
-        const IPCDataTransferImage& imageDetails = item.imageDetails();
-        const gfxIntSize size(imageDetails.width(), imageDetails.height());
-        if (!size.width || !size.height) {
-          return true;
+        if (item.flavor().EqualsLiteral(kJPEGImageMime) ||
+            item.flavor().EqualsLiteral(kJPGImageMime) ||
+            item.flavor().EqualsLiteral(kPNGImageMime) ||
+            item.flavor().EqualsLiteral(kGIFImageMime)) {
+          const IPCDataTransferImage& imageDetails = item.imageDetails();
+          const gfxIntSize size(imageDetails.width(), imageDetails.height());
+          if (!size.width || !size.height) {
+            return true;
+          }
+
+          nsCString text = item.data().get_nsCString();
+          mozilla::RefPtr<gfx::DataSourceSurface> image =
+            new mozilla::gfx::SourceSurfaceRawData();
+          mozilla::gfx::SourceSurfaceRawData* raw =
+            static_cast<mozilla::gfx::SourceSurfaceRawData*>(image.get());
+          raw->InitWrappingData(
+            reinterpret_cast<uint8_t*>(const_cast<nsCString&>(text).BeginWriting()),
+            size, imageDetails.stride(),
+            static_cast<mozilla::gfx::SurfaceFormat>(imageDetails.format()), false);
+          raw->GuaranteePersistance();
+
+          nsRefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(image, size);
+          nsCOMPtr<imgIContainer> imageContainer(image::ImageOps::CreateFromDrawable(drawable));
+
+          nsCOMPtr<nsISupportsInterfacePointer>
+            imgPtr(do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv));
+
+          rv = imgPtr->SetData(imageContainer);
+          NS_ENSURE_SUCCESS(rv, true);
+
+          trans->SetTransferData(item.flavor().get(), imgPtr, sizeof(nsISupports*));
+        } else {
+          nsCOMPtr<nsISupportsCString> dataWrapper =
+            do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
+          NS_ENSURE_SUCCESS(rv, true);
+
+          const nsCString& text = item.data().get_nsCString();
+          rv = dataWrapper->SetData(text);
+          NS_ENSURE_SUCCESS(rv, true);
+
+          rv = trans->SetTransferData(item.flavor().get(), dataWrapper,
+                                      text.Length());
+
+          NS_ENSURE_SUCCESS(rv, true);
         }
-
-        nsCString text = item.data().get_nsCString();
-        mozilla::RefPtr<gfx::DataSourceSurface> image =
-          new mozilla::gfx::SourceSurfaceRawData();
-        mozilla::gfx::SourceSurfaceRawData* raw =
-          static_cast<mozilla::gfx::SourceSurfaceRawData*>(image.get());
-        raw->InitWrappingData(
-          reinterpret_cast<uint8_t*>(const_cast<nsCString&>(text).BeginWriting()),
-          size, imageDetails.stride(),
-          static_cast<mozilla::gfx::SurfaceFormat>(imageDetails.format()), false);
-        raw->GuaranteePersistance();
-
-        nsRefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(image, size);
-        nsCOMPtr<imgIContainer> imageContainer(image::ImageOps::CreateFromDrawable(drawable));
-
-        nsCOMPtr<nsISupportsInterfacePointer>
-          imgPtr(do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID, &rv));
-
-        rv = imgPtr->SetData(imageContainer);
-        NS_ENSURE_SUCCESS(rv, true);
-
-        trans->SetTransferData(item.flavor().get(), imgPtr, sizeof(nsISupports*));
       }
     }
 
     trans->SetIsPrivateData(aIsPrivateData);
 
     clipboard->SetData(trans, nullptr, aWhichClipboard);
     return true;
 }
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -90,17 +90,20 @@ skip-if = buildapp == 'b2g' || buildapp 
 [test_peerConnection_capturedVideo.html]
 tags=capturestream
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_captureStream_canvas_2d.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_captureStream_canvas_webgl.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_close.html]
+[test_peerConnection_closeDuringIce.html]
 [test_peerConnection_errorCallbacks.html]
+[test_peerConnection_iceFailure.html]
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # Disabling because of test failures on B2G emulator
 [test_peerConnection_forwarding_basicAudioVideoCombined.html]
 skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_noTrickleAnswer.html]
 skip-if = toolkit == 'gonk' # B2G emulator is too slow to handle a two-way audio call reliably
 [test_peerConnection_noTrickleOffer.html]
 skip-if = toolkit == 'gonk' # B2G emulator is too slow to handle a two-way audio call reliably
 [test_peerConnection_noTrickleOfferAnswer.html]
 skip-if = toolkit == 'gonk' # B2G emulator is too slow to handle a two-way audio call reliably
--- a/dom/media/tests/mochitest/templates.js
+++ b/dom/media/tests/mochitest/templates.js
@@ -400,17 +400,17 @@ var commandsPeerConnectionOfferAnswer = 
     return test.getSignalingMessage("answer").then(message => {
       ok("answer" in message, "Got an answer message");
       test._remote_answer = new mozRTCSessionDescription(message.answer);
       test._answer_constraints = message.answer_constraints;
     });
   },
 
   function PC_LOCAL_SET_REMOTE_DESCRIPTION(test) {
-    test.setRemoteDescription(test.pcLocal, test._remote_answer, STABLE)
+    return test.setRemoteDescription(test.pcLocal, test._remote_answer, STABLE)
       .then(() => {
         is(test.pcLocal.signalingState, STABLE,
            "signalingState after local setRemoteDescription is 'stable'");
       });
   },
   function PC_REMOTE_SANE_LOCAL_SDP(test) {
     test.pcRemote.verifySdp(test._remote_answer, "answer",
                             test._offer_constraints, test._offer_options,
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_closeDuringIce.html
@@ -0,0 +1,79 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "1087629",
+    title: "Close PCs during ICE connectivity check"
+  });
+
+// Test closeDuringIce to simulate problems during peer connections
+
+
+function PC_LOCAL_SETUP_NULL_ICE_HANDLER(test) {
+  test.pcLocal.setupIceCandidateHandler(test, function() {}, function () {});
+}
+function PC_REMOTE_SETUP_NULL_ICE_HANDLER(test) {
+  test.pcRemote.setupIceCandidateHandler(test, function() {}, function () {});
+}
+function PC_REMOTE_ADD_FAKE_ICE_CANDIDATE(test) {
+  var cand = new mozRTCIceCandidate({"candidate":"candidate:0 1 UDP 2130379007 192.0.2.1 12345 typ host","sdpMid":"","sdpMLineIndex":0});
+  test.pcRemote.storeOrAddIceCandidate(cand);
+  info(test.pcRemote + " Stored fake candidate: " + JSON.stringify(cand));
+}
+function PC_LOCAL_ADD_FAKE_ICE_CANDIDATE(test) {
+  var cand = new mozRTCIceCandidate({"candidate":"candidate:0 1 UDP 2130379007 192.0.2.2 56789 typ host","sdpMid":"","sdpMLineIndex":0});
+    test.pcLocal.storeOrAddIceCandidate(cand);
+  info(test.pcLocal + " Stored fake candidate: " + JSON.stringify(cand));
+}
+function PC_LOCAL_CLOSE_DURING_ICE(test) {
+  return test.pcLocal.iceChecking.then(() => {
+    test.pcLocal.onsignalingstatechange = function () {};
+    test.pcLocal.close();
+    });
+}
+function PC_REMOTE_CLOSE_DURING_ICE(test) {
+  return test.pcRemote.iceChecking.then(() => {
+    test.pcRemote.onsignalingstatechange = function () {};
+    test.pcRemote.close();
+    });
+}
+function PC_LOCAL_WAIT_FOR_ICE_CHECKING(test) {
+  var resolveIceChecking;
+  test.pcLocal.iceChecking = new Promise(r => resolveIceChecking = r);
+  test.pcLocal.ice_connection_callbacks.checkIceStatus = () => {
+	if (test.pcLocal._pc.iceConnectionState === "checking") {
+	  resolveIceChecking();
+	}
+  }
+}
+function PC_REMOTE_WAIT_FOR_ICE_CHECKING(test) {
+  var resolveIceChecking;
+  test.pcRemote.iceChecking = new Promise(r => resolveIceChecking = r);
+  test.pcRemote.ice_connection_callbacks.checkIceStatus = () => {
+	if (test.pcRemote._pc.iceConnectionState === "checking") {
+	  resolveIceChecking();
+	}
+  }
+}
+
+runNetworkTest(() => {
+  var test = new PeerConnectionTest();
+  test.setMediaConstraints([{audio: true}], [{audio: true}]);
+  test.chain.replace("PC_LOCAL_SETUP_ICE_HANDLER", PC_LOCAL_SETUP_NULL_ICE_HANDLER);
+  test.chain.replace("PC_REMOTE_SETUP_ICE_HANDLER", PC_REMOTE_SETUP_NULL_ICE_HANDLER);
+  test.chain.insertAfter("PC_REMOTE_SETUP_NULL_ICE_HANDLER", PC_LOCAL_WAIT_FOR_ICE_CHECKING);
+  test.chain.insertAfter("PC_LOCAL_WAIT_FOR_ICE_CHECKING", PC_REMOTE_WAIT_FOR_ICE_CHECKING);
+  test.chain.removeAfter("PC_LOCAL_SET_REMOTE_DESCRIPTION");
+  test.chain.append([PC_REMOTE_ADD_FAKE_ICE_CANDIDATE, PC_LOCAL_ADD_FAKE_ICE_CANDIDATE,
+	PC_LOCAL_CLOSE_DURING_ICE, PC_REMOTE_CLOSE_DURING_ICE]);
+  test.run();
+});
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_iceFailure.html
@@ -0,0 +1,83 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+  createHTML({
+    bug: "1087629",
+    title: "Wait for ICE failure"
+  });
+
+// Test iceFailure
+
+function PC_LOCAL_SETUP_NULL_ICE_HANDLER(test) {
+  test.pcLocal.setupIceCandidateHandler(test, function() {}, function () {});
+}
+function PC_REMOTE_SETUP_NULL_ICE_HANDLER(test) {
+  test.pcRemote.setupIceCandidateHandler(test, function() {}, function () {});
+}
+function PC_REMOTE_ADD_FAKE_ICE_CANDIDATE(test) {
+  var cand = new mozRTCIceCandidate({"candidate":"candidate:0 1 UDP 2130379007 192.0.2.1 12345 typ host","sdpMid":"","sdpMLineIndex":0});
+  test.pcRemote.storeOrAddIceCandidate(cand);
+  info(test.pcRemote + " Stored fake candidate: " + JSON.stringify(cand));
+}
+function PC_LOCAL_ADD_FAKE_ICE_CANDIDATE(test) {
+  var cand = new mozRTCIceCandidate({"candidate":"candidate:0 1 UDP 2130379007 192.0.2.2 56789 typ host","sdpMid":"","sdpMLineIndex":0});
+    test.pcLocal.storeOrAddIceCandidate(cand);
+  info(test.pcLocal + " Stored fake candidate: " + JSON.stringify(cand));
+}
+function PC_LOCAL_WAIT_FOR_ICE_FAILURE(test) {
+  return test.pcLocal.iceFailed.then(() => {
+    ok(true, this.pcLocal + " Ice Failure Reached.");
+	});
+}
+function PC_REMOTE_WAIT_FOR_ICE_FAILURE(test) {
+  return test.pcRemote.iceFailed.then(() => {
+    ok(true, this.pcRemote + " Ice Failure Reached.");
+    });
+}
+function PC_LOCAL_WAIT_FOR_ICE_FAILED(test) {
+  var resolveIceFailed;
+  test.pcLocal.iceFailed = new Promise(r => resolveIceFailed = r);
+  test.pcLocal.ice_connection_callbacks.checkIceStatus = () => {
+	if (test.pcLocal._pc.iceConnectionState === "failed") {
+	  resolveIceFailed();
+	}
+  }
+}
+function PC_REMOTE_WAIT_FOR_ICE_FAILED(test) {
+  var resolveIceFailed;
+  test.pcRemote.iceFailed = new Promise(r => resolveIceFailed = r);
+  test.pcRemote.ice_connection_callbacks.checkIceStatus = () => {
+	if (test.pcRemote._pc.iceConnectionState === "failed") {
+	  resolveIceFailed();
+	}
+  }
+}
+
+runNetworkTest(() => {
+  SpecialPowers.pushPrefEnv({
+    'set': [
+      ['media.peerconnection.ice.stun_client_maximum_transmits', 3],
+      ['media.peerconnection.ice.trickle_grace_period', 3000],
+    ]
+  }, function() {
+    var test = new PeerConnectionTest();
+    test.setMediaConstraints([{audio: true}], [{audio: true}]);
+    test.chain.replace("PC_LOCAL_SETUP_ICE_HANDLER", PC_LOCAL_SETUP_NULL_ICE_HANDLER);
+    test.chain.replace("PC_REMOTE_SETUP_ICE_HANDLER", PC_REMOTE_SETUP_NULL_ICE_HANDLER);
+    test.chain.insertAfter("PC_REMOTE_SETUP_NULL_ICE_HANDLER", PC_LOCAL_WAIT_FOR_ICE_FAILED);
+    test.chain.insertAfter("PC_LOCAL_WAIT_FOR_ICE_FAILED", PC_REMOTE_WAIT_FOR_ICE_FAILED);
+    test.chain.removeAfter("PC_LOCAL_SET_REMOTE_DESCRIPTION");
+    test.chain.append([PC_REMOTE_ADD_FAKE_ICE_CANDIDATE, PC_LOCAL_ADD_FAKE_ICE_CANDIDATE,
+	PC_LOCAL_WAIT_FOR_ICE_FAILURE, PC_REMOTE_WAIT_FOR_ICE_FAILURE]);
+    test.run();
+  });
+});
+</script>
+</pre>
+</body>
+</html>
--- a/dom/messagechannel/MessagePort.cpp
+++ b/dom/messagechannel/MessagePort.cpp
@@ -16,16 +16,17 @@
 #include "mozilla/dom/MessagePortChild.h"
 #include "mozilla/dom/MessagePortList.h"
 #include "mozilla/dom/PMessagePort.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/unused.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsPresContext.h"
 #include "ScriptSettings.h"
 #include "SharedMessagePortMessage.h"
 
 #include "nsIBFCacheEntry.h"
 #include "nsIDocument.h"
@@ -244,16 +245,60 @@ public:
 
 private:
   ~MessagePortFeature()
   {
     MOZ_COUNT_DTOR(MessagePortFeature);
   }
 };
 
+class ForceCloseHelper final : public nsIIPCBackgroundChildCreateCallback
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  static void ForceClose(const MessagePortIdentifier& aIdentifier)
+  {
+    PBackgroundChild* actor =
+      mozilla::ipc::BackgroundChild::GetForCurrentThread();
+    if (actor) {
+      unused << actor->SendMessagePortForceClose(aIdentifier.uuid(),
+                                                 aIdentifier.destinationUuid(),
+                                                 aIdentifier.sequenceId());
+      return;
+    }
+
+    nsRefPtr<ForceCloseHelper> helper = new ForceCloseHelper(aIdentifier);
+    if (NS_WARN_IF(!mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(helper))) {
+      MOZ_CRASH();
+    }
+  }
+
+private:
+  explicit ForceCloseHelper(const MessagePortIdentifier& aIdentifier)
+    : mIdentifier(aIdentifier)
+  {}
+
+  ~ForceCloseHelper() {}
+
+  void ActorFailed() override
+  {
+    MOZ_CRASH("Failed to create a PBackgroundChild actor!");
+  }
+
+  void ActorCreated(mozilla::ipc::PBackgroundChild* aActor) override
+  {
+    ForceClose(mIdentifier);
+  }
+
+  const MessagePortIdentifier mIdentifier;
+};
+
+NS_IMPL_ISUPPORTS(ForceCloseHelper, nsIIPCBackgroundChildCreateCallback)
+
 } // anonymous namespace
 
 MessagePort::MessagePort(nsPIDOMWindow* aWindow)
   : MessagePortBase(aWindow)
   , mInnerID(0)
   , mMessageQueueEnabled(false)
   , mIsKeptAlive(false)
 {
@@ -858,10 +903,16 @@ MessagePort::RemoveDocFromBFCache()
   nsCOMPtr<nsIBFCacheEntry> bfCacheEntry = doc->GetBFCacheEntry();
   if (!bfCacheEntry) {
     return;
   }
 
   bfCacheEntry->RemoveFromBFCacheSync();
 }
 
+/* static */ void
+MessagePort::ForceClose(const MessagePortIdentifier& aIdentifier)
+{
+  ForceCloseHelper::ForceClose(aIdentifier);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/messagechannel/MessagePort.h
+++ b/dom/messagechannel/MessagePort.h
@@ -81,16 +81,19 @@ public:
   static already_AddRefed<MessagePort>
   Create(nsPIDOMWindow* aWindow, const nsID& aUUID,
          const nsID& aDestinationUUID, ErrorResult& aRv);
 
   static already_AddRefed<MessagePort>
   Create(nsPIDOMWindow* aWindow, const MessagePortIdentifier& aIdentifier,
          ErrorResult& aRv);
 
+  static void
+  ForceClose(const MessagePortIdentifier& aIdentifier);
+
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   virtual void
   PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
               const Optional<Sequence<JS::Value>>& aTransferable,
               ErrorResult& aRv) override;
 
--- a/dom/messagechannel/MessagePortParent.cpp
+++ b/dom/messagechannel/MessagePortParent.cpp
@@ -154,10 +154,24 @@ MessagePortParent::CloseAndDelete()
 
 void
 MessagePortParent::Close()
 {
   mService = nullptr;
   mEntangled = false;
 }
 
+/* static */ bool
+MessagePortParent::ForceClose(const nsID& aUUID,
+                              const nsID& aDestinationUUID,
+                              const uint32_t& aSequenceID)
+{
+  MessagePortService* service =  MessagePortService::Get();
+  if (!service) {
+    NS_WARNING("The service must exist if we want to close an existing MessagePort.");
+    return false;
+  }
+
+  return service->ForceClose(aUUID, aDestinationUUID, aSequenceID);
+}
+
 } // dom namespace
 } // mozilla namespace
--- a/dom/messagechannel/MessagePortParent.h
+++ b/dom/messagechannel/MessagePortParent.h
@@ -31,16 +31,20 @@ public:
     return mCanSendData;
   }
 
   const nsID& ID() const
   {
     return mUUID;
   }
 
+  static bool ForceClose(const nsID& aUUID,
+                         const nsID& aDestinationUUID,
+                         const uint32_t& aSequenceID);
+
 private:
   virtual bool RecvPostMessages(nsTArray<MessagePortMessage>&& aMessages)
                                                                        override;
 
   virtual bool RecvDisentangle(nsTArray<MessagePortMessage>&& aMessages)
                                                                        override;
 
   virtual bool RecvStopSendingData() override;
--- a/dom/messagechannel/MessagePortService.cpp
+++ b/dom/messagechannel/MessagePortService.cpp
@@ -1,26 +1,37 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MessagePortService.h"
 #include "MessagePortParent.h"
 #include "SharedMessagePortMessage.h"
+#include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/unused.h"
 #include "nsDataHashtable.h"
 #include "nsTArray.h"
 
+using mozilla::ipc::AssertIsOnBackgroundThread;
+
 namespace mozilla {
 namespace dom {
 
 namespace {
+
 StaticRefPtr<MessagePortService> gInstance;
+
+void
+AssertIsInMainProcess()
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+}
+
 } // anonymous namespace
 
 class MessagePortService::MessagePortServiceData final
 {
 public:
   explicit MessagePortServiceData(const nsID& aDestinationUUID)
     : mDestinationUUID(aDestinationUUID)
     , mSequenceID(1)
@@ -49,18 +60,30 @@ public:
     MessagePortParent* mParent;
   };
 
   FallibleTArray<NextParent> mNextParents;
   FallibleTArray<nsRefPtr<SharedMessagePortMessage>> mMessages;
 };
 
 /* static */ MessagePortService*
+MessagePortService::Get()
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  return gInstance;
+}
+
+/* static */ MessagePortService*
 MessagePortService::GetOrCreate()
 {
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
   if (!gInstance) {
     gInstance = new MessagePortService();
   }
 
   return gInstance;
 }
 
 bool
@@ -243,16 +266,22 @@ MessagePortService::CloseAll(const nsID&
     parent.mParent->CloseAndDelete();
   }
 
   nsID destinationUUID = data->mDestinationUUID;
   mPorts.Remove(aUUID);
 
   CloseAll(destinationUUID);
 
+  // CloseAll calls itself recursively and it can happen that it deletes
+  // itself. Before continuing we must check if we are still alive.
+  if (!gInstance) {
+    return;
+  }
+
 #ifdef DEBUG
   mPorts.EnumerateRead(CloseAllDebugCheck, const_cast<nsID*>(&aUUID));
 #endif
 
   MaybeShutdown();
 }
 
 // This service can be dismissed when there are not active ports.
@@ -319,10 +348,31 @@ MessagePortService::ParentDestroy(Messag
        break;
       }
     }
   }
 
   CloseAll(aParent->ID());
 }
 
+bool
+MessagePortService::ForceClose(const nsID& aUUID,
+                               const nsID& aDestinationUUID,
+                               const uint32_t& aSequenceID)
+{
+  MessagePortServiceData* data;
+  if (!mPorts.Get(aUUID, &data)) {
+    NS_WARNING("Unknown MessagePort in ForceClose()");
+    return true;
+  }
+
+  if (!data->mDestinationUUID.Equals(aDestinationUUID) ||
+      data->mSequenceID != aSequenceID) {
+    NS_WARNING("DestinationUUID and/or sequenceID do not match.");
+    return false;
+  }
+
+  CloseAll(aUUID);
+  return true;
+}
+
 } // dom namespace
 } // mozilla namespace
--- a/dom/messagechannel/MessagePortService.h
+++ b/dom/messagechannel/MessagePortService.h
@@ -15,16 +15,17 @@ namespace dom {
 class MessagePortParent;
 class SharedMessagePortMessage;
 
 class MessagePortService final
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(MessagePortService)
 
+  static MessagePortService* Get();
   static MessagePortService* GetOrCreate();
 
   bool RequestEntangling(MessagePortParent* aParent,
                          const nsID& aDestinationUUID,
                          const uint32_t& aSequenceID);
 
   bool DisentanglePort(
                  MessagePortParent* aParent,
@@ -33,16 +34,20 @@ public:
   bool ClosePort(MessagePortParent* aParent);
 
   bool PostMessages(
                  MessagePortParent* aParent,
                  FallibleTArray<nsRefPtr<SharedMessagePortMessage>>& aMessages);
 
   void ParentDestroy(MessagePortParent* aParent);
 
+  bool ForceClose(const nsID& aUUID,
+                  const nsID& aDestinationUUID,
+                  const uint32_t& aSequenceID);
+
 private:
   ~MessagePortService() {}
 
   void CloseAll(const nsID& aUUID);
   void MaybeShutdown();
 
   class MessagePortServiceData;
 
--- a/dom/messagechannel/MessagePortUtils.cpp
+++ b/dom/messagechannel/MessagePortUtils.cpp
@@ -136,17 +136,17 @@ ReadTransfer(JSContext* aCx, JSStructure
   }
 
   MOZ_ASSERT(aContent == 0);
   MOZ_ASSERT(aExtraData < closure->mClosure.mMessagePortIdentifiers.Length());
 
   ErrorResult rv;
   nsRefPtr<MessagePort> port =
     MessagePort::Create(closure->mWindow,
-                        closure->mClosure.mMessagePortIdentifiers[(uint32_t)aExtraData],
+                        closure->mClosure.mMessagePortIdentifiers[aExtraData],
                         rv);
   if (NS_WARN_IF(rv.Failed())) {
     return false;
   }
 
   closure->mMessagePorts.AppendElement(port);
 
   JS::Rooted<JS::Value> value(aCx);
@@ -190,23 +190,37 @@ WriteTransfer(JSContext* aCx, JS::Handle
   *aTag = SCTAG_DOM_MAP_MESSAGEPORT;
   *aOwnership = JS::SCTAG_TMO_CUSTOM;
   *aContent = nullptr;
   *aExtraData = closure->mClosure.mMessagePortIdentifiers.Length() - 1;
 
   return true;
 }
 
+void
+FreeTransfer(uint32_t aTag, JS::TransferableOwnership aOwnership,
+             void* aContent, uint64_t aExtraData, void* aClosure)
+{
+  MOZ_ASSERT(aClosure);
+  auto* closure = static_cast<StructuredCloneClosureInternal*>(aClosure);
+
+  if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
+    MOZ_ASSERT(!aContent);
+    MOZ_ASSERT(aExtraData < closure->mClosure.mMessagePortIdentifiers.Length());
+    MessagePort::ForceClose(closure->mClosure.mMessagePortIdentifiers[(uint32_t)aExtraData]);
+  }
+}
+
 const JSStructuredCloneCallbacks gCallbacks = {
   Read,
   Write,
   Error,
   ReadTransfer,
   WriteTransfer,
-  nullptr
+  FreeTransfer,
 };
 
 } // anonymous namespace
 
 bool
 ReadStructuredCloneWithTransfer(JSContext* aCx, nsTArray<uint8_t>& aData,
                                 const StructuredCloneClosure& aClosure,
                                 JS::MutableHandle<JS::Value> aClone,
--- a/dom/messagechannel/tests/mochitest.ini
+++ b/dom/messagechannel/tests/mochitest.ini
@@ -18,8 +18,9 @@ support-files =
 [test_messageChannel_start.html]
 [test_messageChannel_transferable.html]
 [test_messageChannel_unshipped.html]
 [test_messageChannel_worker.html]
 [test_messageChannel_selfTransferring.html]
 [test_messageChannel_sharedWorker.html]
 [test_messageChannel_sharedWorker2.html]
 [test_messageChannel_any.html]
+[test_messageChannel_forceClose.html]
new file mode 100644
--- /dev/null
+++ b/dom/messagechannel/tests/test_messageChannel_forceClose.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1176034
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1176034 - start/close</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=1176034">Mozilla Bug 1176034</a>
+<div id="content"></div>
+<pre id="test">
+</pre>
+  <script type="application/javascript">
+
+  function runTests() {
+    var mc = new MessageChannel();
+
+    try {
+      postMessage(42, "*", [ mc.port1, window ]);
+      ok(false, "Something went wrong.");
+    } catch(e) {
+      ok(true, "PostMessage should fail and we should not leak.");
+    }
+
+    SimpleTest.finish();
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, runTests);
+  </script>
+</body>
+</html>
--- a/dom/push/Push.js
+++ b/dom/push/Push.js
@@ -18,21 +18,21 @@ const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 
 const PUSH_SUBSCRIPTION_CID = Components.ID("{CA86B665-BEDA-4212-8D0F-5C9F65270B58}");
 
-function PushSubscription(pushEndpoint, scope, pageURL) {
+function PushSubscription(pushEndpoint, scope, principal) {
   debug("PushSubscription Constructor");
   this._pushEndpoint = pushEndpoint;
   this._scope = scope;
-  this._pageURL = pageURL;
+  this._principal = principal;
 }
 
 PushSubscription.prototype = {
   __proto__: DOMRequestIpcHelper.prototype,
 
   contractID: "@mozilla.org/push/PushSubscription;1",
 
   classID : PUSH_SUBSCRIPTION_CID,
@@ -48,59 +48,58 @@ PushSubscription.prototype = {
       "PushService:Unregister:OK",
       "PushService:Unregister:KO",
     ]);
 
     this._cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
                    .getService(Ci.nsISyncMessageSender);
   },
 
-  __init: function(endpoint, scope, pageURL) {
+  __init: function(endpoint, scope, principal) {
     this._pushEndpoint = endpoint;
     this._scope = scope;
-    this._pageURL = pageURL;
+    this._principal = principal;
   },
 
   get endpoint() {
     return this._pushEndpoint;
   },
 
   unsubscribe: function() {
     debug("unsubscribe! ")
 
     let promiseInit = function(resolve, reject) {
       let resolverId = this.getPromiseResolverId({resolve: resolve,
                                                   reject: reject });
 
       this._cpmm.sendAsyncMessage("Push:Unregister", {
-                                  pageURL: this._pageURL,
                                   scope: this._scope,
                                   pushEndpoint: this._pushEndpoint,
                                   requestID: resolverId
-                                });
+                                }, null, this._principal);
     }.bind(this);
 
     return this.createPromise(promiseInit);
   },
 
   receiveMessage: function(aMessage) {
     debug("push subscription receiveMessage(): " + JSON.stringify(aMessage))
 
     let json = aMessage.data;
     let resolver = this.takePromiseResolver(json.requestID);
     if (resolver == null) {
       return;
     }
 
     switch (aMessage.name) {
       case "PushService:Unregister:OK":
-        resolver.resolve(false);
+        resolver.resolve(true);
         break;
       case "PushService:Unregister:KO":
-        resolver.reject(true);
+        resolver.resolve(false);
         break;
       default:
         debug("NOT IMPLEMENTED! receiveMessage for " + aMessage.name);
     }
   },
 
 };
 
@@ -128,45 +127,43 @@ Push.prototype = {
 
   init: function(aWindow) {
     // Set debug first so that all debugging actually works.
     // NOTE: We don't add an observer here like in PushService. Flipping the
     // pref will require a reload of the app/page, which seems acceptable.
     gDebuggingEnabled = Services.prefs.getBoolPref("dom.push.debug");
     debug("init()");
 
-    this._pageURL = aWindow.document.nodePrincipal.URI;
     this._window = aWindow;
 
     this.initDOMRequestHelper(aWindow, [
       "PushService:Register:OK",
       "PushService:Register:KO",
       "PushService:Registration:OK",
       "PushService:Registration:KO"
     ]);
 
     this._cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
                    .getService(Ci.nsISyncMessageSender);
+    this._principal = aWindow.document.nodePrincipal;
   },
 
   setScope: function(scope){
     debug('setScope ' + scope);
     this._scope = scope;
   },
 
   askPermission: function (aAllowCallback, aCancelCallback) {
     debug("askPermission");
 
     let principal = this._window.document.nodePrincipal;
     let type = "push";
     let permValue =
       Services.perms.testExactPermissionFromPrincipal(principal, type);
 
-    debug("Existing permission " + permValue);
-
     if (permValue == Ci.nsIPermissionManager.ALLOW_ACTION) {
         aAllowCallback();
       return;
     }
 
     if (permValue == Ci.nsIPermissionManager.DENY_ACTION) {
       aCancelCallback();
       return;
@@ -214,31 +211,32 @@ Push.prototype = {
 
     if (!resolver) {
       return;
     }
 
     switch (aMessage.name) {
       case "PushService:Register:OK":
       {
-        let subscription = new this._window.PushSubscription(json.pushEndpoint,
-                                                             this._scope,
-                                                             this._pageURL.spec);
+        let subscription =
+          new this._window.PushSubscription(json.pushEndpoint, this._scope,
+                                            this._principal);
         resolver.resolve(subscription);
         break;
       }
       case "PushService:Register:KO":
         resolver.reject(null);
         break;
       case "PushService:Registration:OK":
       {
         let subscription = null;
         try {
-          subscription = new this._window.PushSubscription(json.registration.pushEndpoint,
-                                                          this._scope, this._pageURL.spec);
+          subscription =
+            new this._window.PushSubscription(json.registration.pushEndpoint,
+                                              this._scope, this._principal);
         } catch(error) {
         }
         resolver.resolve(subscription);
         break;
       }
       case "PushService:Registration:KO":
         resolver.reject(null);
         break;
@@ -250,20 +248,19 @@ Push.prototype = {
   subscribe: function() {
     debug("subscribe()");
     let p = this.createPromise(function(resolve, reject) {
       let resolverId = this.getPromiseResolverId({ resolve: resolve, reject: reject });
 
       this.askPermission(
         function() {
           this._cpmm.sendAsyncMessage("Push:Register", {
-                                      pageURL: this._pageURL.spec,
                                       scope: this._scope,
                                       requestID: resolverId
-                                    });
+                                    }, null, this._principal);
         }.bind(this),
 
         function() {
           reject("PermissionDeniedError");
         }
       );
     }.bind(this));
     return p;
@@ -274,36 +271,38 @@ Push.prototype = {
 
     let p = this.createPromise(function(resolve, reject) {
 
       let resolverId = this.getPromiseResolverId({ resolve: resolve, reject: reject });
 
       this.askPermission(
         function() {
           this._cpmm.sendAsyncMessage("Push:Registration", {
-                                      pageURL: this._pageURL.spec,
                                       scope: this._scope,
                                       requestID: resolverId
-                                    });
+                                    }, null, this._principal);
         }.bind(this),
 
         function() {
           reject("PermissionDeniedError");
         }
       );
     }.bind(this));
     return p;
   },
 
   hasPermission: function() {
-    debug("getSubscription()" + this._scope);
+    debug("hasPermission()" + this._scope);
 
     let p = this.createPromise(function(resolve, reject) {
-      let permissionManager = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
-      let permission = permissionManager.testExactPermission(this._pageURL, "push");
+      let permissionManager = Cc["@mozilla.org/permissionmanager;1"]
+                              .getService(Ci.nsIPermissionManager);
+      let permission =
+        permissionManager.testExactPermissionFromPrincipal(this._principal,
+                                                           "push");
 
       let pushPermissionStatus = "default";
       if (permission == Ci.nsIPermissionManager.ALLOW_ACTION) {
         pushPermissionStatus = "granted";
       } else if (permission == Ci.nsIPermissionManager.DENY_ACTION) {
         pushPermissionStatus = "denied";
       }
       resolve(pushPermissionStatus);
--- a/dom/push/PushDB.jsm
+++ b/dom/push/PushDB.jsm
@@ -142,47 +142,85 @@ this.PushDB.prototype = {
           };
         },
         resolve,
         reject
       )
     );
   },
 
-
-  getByScope: function(aScope) {
-    debug("getByScope() " + aScope);
+  // Perform a unique match against { scope, originAttributes }
+  getByIdentifiers: function(aPageRecord) {
+    debug("getByIdentifiers() { " + aPageRecord.scope + ", " +
+          JSON.stringify(aPageRecord.originAttributes) + " }");
+    if (!aPageRecord.scope || aPageRecord.originAttributes == undefined) {
+      return Promise.reject(
+               new TypeError("Scope and originAttributes are required! " +
+                             JSON.stringify(aPageRecord)));
+    }
 
     return new Promise((resolve, reject) =>
       this.newTxn(
         "readonly",
         this._dbStoreName,
         function txnCb(aTxn, aStore) {
           aTxn.result = undefined;
 
-          let index = aStore.index("scope");
-          index.get(aScope).onsuccess = function setTxnResult(aEvent) {
+          let index = aStore.index("identifiers");
+          let request = index.get(IDBKeyRange.only([aPageRecord.scope, aPageRecord.originAttributes]));
+          request.onsuccess = function setTxnResult(aEvent) {
             aTxn.result = aEvent.target.result;
-            debug("Fetch successful " + aEvent.target.result);
-          };
+          }
         },
         resolve,
         reject
       )
     );
   },
 
+  _getAllByKey: function(aKeyName, aKeyValue) {
+    return new Promise((resolve, reject) =>
+      this.newTxn(
+        "readonly",
+        this._dbStoreName,
+        function txnCb(aTxn, aStore) {
+          aTxn.result = undefined;
+
+          let index = aStore.index(aKeyName);
+          // It seems ok to use getAll here, since unlike contacts or other
+          // high storage APIs, we don't expect more than a handful of
+          // registrations per domain, and usually only one.
+          let getAllReq = index.mozGetAll(aKeyValue);
+          getAllReq.onsuccess = function setTxnResult(aEvent) {
+            aTxn.result = aEvent.target.result;
+          }
+        },
+        resolve,
+        reject
+      )
+    );
+  },
+
+  // aOriginAttributes must be a string!
+  getAllByOriginAttributes: function(aOriginAttributes) {
+    if (typeof aOriginAttributes !== "string") {
+      return Promise.reject("Expected string!");
+    }
+    return this._getAllByKey("originAttributes", aOriginAttributes);
+  },
+
   getAllKeyIDs: function() {
     debug("getAllKeyIDs()");
 
     return new Promise((resolve, reject) =>
       this.newTxn(
         "readonly",
         this._dbStoreName,
         function txnCb(aTxn, aStore) {
+          aTxn.result = undefined;
           aStore.mozGetAll().onsuccess = function(event) {
             aTxn.result = event.target.result;
           };
         },
         resolve,
         reject
       )
     );
--- a/dom/push/PushNotificationService.js
+++ b/dom/push/PushNotificationService.js
@@ -33,26 +33,26 @@ PushNotificationService.prototype = {
 
   contractID: "@mozilla.org/push/NotificationService;1",
 
   _xpcom_factory: XPCOMUtils.generateSingletonFactory(PushNotificationService),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference,
                                          Ci.nsIPushNotificationService]),
 
-  register: function register(scope, pageURL) {
-    return PushService._register({scope, pageURL});
+  register: function register(scope, originAttributes) {
+    return PushService._register({scope, originAttributes});
   },
 
-  unregister: function unregister(scope) {
-    return PushService._unregister({scope});
+  unregister: function unregister(scope, originAttributes) {
+    return PushService._unregister({scope, originAttributes});
   },
 
-  registration: function registration(scope) {
-    return PushService._registration({scope});
+  registration: function registration(scope, originAttributes) {
+    return PushService._registration({scope, originAttributes});
   },
 
   clearAll: function clearAll() {
     return PushService._clearAll();
   },
 
   observe: function observe(subject, topic, data) {
     switch (topic) {
--- a/dom/push/PushService.jsm
+++ b/dom/push/PushService.jsm
@@ -111,16 +111,36 @@ this.PushService = {
     } else {
       return (this._activated) ? this._activated :
                                  this._activated = new Promise((res, rej) =>
                                    this._notifyActivated = {resolve: res,
                                                             reject: rej});
     }
   },
 
+  _makePendingKey: function(aPageRecord) {
+    return aPageRecord.scope + "|" + aPageRecord.originAttributes;
+  },
+
+  _lookupOrPutPendingRequest: function(aPageRecord) {
+    let key = this._makePendingKey(aPageRecord);
+    if (this._pendingRegisterRequest[key]) {
+      return this._pendingRegisterRequest[key];
+    }
+
+    return this._pendingRegisterRequest[key] = this._registerWithServer(aPageRecord);
+  },
+
+  _deletePendingRequest: function(aPageRecord) {
+    let key = this._makePendingKey(aPageRecord);
+    if (this._pendingRegisterRequest[key]) {
+      delete this._pendingRegisterRequest[key];
+    }
+  },
+
   _setState: function(aNewState) {
     debug("new state: " + aNewState + " old state: " + this._state);
 
     if (this._state == aNewState) {
       return;
     }
 
     if (this._state == PUSH_SERVICE_ACTIVATING) {
@@ -228,34 +248,51 @@ this.PushService = {
         let data = aSubject
                      .QueryInterface(Ci.mozIApplicationClearPrivateDataParams);
         if (!data) {
           debug("webapps-clear-data: Failed to get information about " +
                 "application");
           return;
         }
 
-        // TODO 1149274.  We should support site permissions as well as a way to
-        // go from manifest url to 'all scopes registered for push in this app'
-        let appsService = Cc["@mozilla.org/AppsService;1"]
-                            .getService(Ci.nsIAppsService);
-        let scope = appsService.getScopeByLocalId(data.appId);
-        if (!scope) {
-          debug("webapps-clear-data: No scope found for " + data.appId);
-          return;
-        }
+        var originAttributes =
+          ChromeUtils.originAttributesToSuffix({ appId: data.appId,
+                                                 inBrowser: data.browserOnly });
+        this._db.getAllByOriginAttributes(originAttributes)
+          .then(records => {
+            records.forEach(record => {
+              this._db.delete(this._service.getKeyFromRecord(record))
+                .then(_ => {
+                  // courtesy, but don't establish a connection
+                  // just for it
+                  if (this._ws) {
+                    debug("Had a connection, so telling the server");
+                    this._sendRequest("unregister", {channelID: records.channelID})
+                        .catch(function(e) {
+                          debug("Unregister errored " + e);
+                        });
+                  }
+                }, err => {
+                  debug("webapps-clear-data: " + scope +
+                        " Could not delete entry " + records.channelID);
 
-        this._db.getByScope(scope)
-          .then(record =>
-            Promise.all([
-              this._db.delete(this._service.getKeyFromRecord(record)),
-              this._sendRequest("unregister", record)
-            ])
-          ).catch(_ => {
-            debug("webapps-clear-data: Error in getByScope(" + scope + ")");
+                  // courtesy, but don't establish a connection
+                  // just for it
+                  if (this._ws) {
+                    debug("Had a connection, so telling the server");
+                    this._sendRequest("unregister", {channelID: records.channelID})
+                        .catch(function(e) {
+                          debug("Unregister errored " + e);
+                        });
+                  }
+                  throw "Database error";
+                });
+            });
+          }, _ => {
+            debug("webapps-clear-data: Error in getAllByOriginAttributes(" + originAttributes + ")");
           });
 
         break;
     }
   },
 
   // utility function used to add/remove observers in startObservers() and
   // stopObservers()
@@ -618,33 +655,29 @@ this.PushService = {
 
   // Fires a push-register system message to all applications that have
   // registration.
   _notifyAllAppsRegister: function() {
     debug("notifyAllAppsRegister()");
     // records are objects describing the registration as stored in IndexedDB.
     return this._db.getAllKeyIDs()
       .then(records => {
-        let scopes = new Set();
-        for (let record of records) {
-          scopes.add(record.scope);
-        }
         let globalMM = Cc['@mozilla.org/globalmessagemanager;1']
                          .getService(Ci.nsIMessageListenerManager);
-        for (let scope of scopes) {
+        for (let record of records) {
           // Notify XPCOM observers.
           Services.obs.notifyObservers(
             null,
             "push-subscription-change",
             scope
           );
 
           let data = {
-            originAttributes: {}, // TODO bug 1166350
-            scope: scope
+            originAttributes: record.originAttributes,
+            scope: record.scope
           };
 
           globalMM.broadcastAsyncMessage('pushsubscriptionchange', data);
         }
       });
   },
 
   dropRegistrationAndNotifyApp: function(aKeyId) {
@@ -654,17 +687,17 @@ this.PushService = {
                          .getService(Ci.nsIMessageListenerManager);
         Services.obs.notifyObservers(
           null,
           "push-subscription-change",
           record.scope
         );
 
         let data = {
-          originAttributes: {}, // TODO bug 1166350
+          originAttributes: record.originAttributes,
           scope: record.scope
         };
 
         globalMM.broadcastAsyncMessage('pushsubscriptionchange', data);
       })
       .then(_ => this._db.delete(aKeyId));
   },
 
@@ -676,31 +709,32 @@ this.PushService = {
                            .getService(Ci.nsIMessageListenerManager);
           Services.obs.notifyObservers(
             null,
             "push-subscription-change",
             record.scope
           );
 
           let data = {
-            originAttributes: {}, // TODO bug 1166350
+            originAttributes: record.originAttributes,
             scope: record.scope
           };
 
           globalMM.broadcastAsyncMessage('pushsubscriptionchange', data);
         }));
   },
 
   receivedPushMessage: function(aPushRecord, message) {
     this._db.put(aPushRecord)
       .then(_ => this._notifyApp(aPushRecord, message));
   },
 
   _notifyApp: function(aPushRecord, message) {
-    if (!aPushRecord || !aPushRecord.scope) {
+    if (!aPushRecord || !aPushRecord.scope ||
+        aPushRecord.originAttributes === undefined) {
       debug("notifyApp() something is undefined.  Dropping notification: " +
         JSON.stringify(aPushRecord) );
       return;
     }
 
     debug("notifyApp() " + aPushRecord.scope);
     let scopeURI = Services.io.newURI(aPushRecord.scope, null, null);
     // Notify XPCOM observers.
@@ -723,78 +757,76 @@ this.PushService = {
         Ci.nsIPermissionManager.ALLOW_ACTION) {
       debug("Does not have permission for push.");
       return;
     }
 
     // TODO data.
     let data = {
       payload: message,
-      originAttributes: {}, // TODO bug 1166350
+      originAttributes: aPushRecord.originAttributes,
       scope: aPushRecord.scope
     };
 
     let globalMM = Cc['@mozilla.org/globalmessagemanager;1']
                  .getService(Ci.nsIMessageListenerManager);
     globalMM.broadcastAsyncMessage('push', data);
   },
 
   getByKeyID: function(aKeyID) {
     return this._db.getByKeyID(aKeyID);
   },
 
   getAllKeyIDs: function() {
     return this._db.getAllKeyIDs();
   },
 
-  _sendRequest(action, aRecord) {
+  _sendRequest: function(action, aRecord) {
     if (this._state == PUSH_SERVICE_CONNECTION_DISABLE) {
       return Promise.reject({state: 0, error: "Service not active"});
     } else if (this._state == PUSH_SERVICE_ACTIVE_OFFLINE) {
       return Promise.reject({state: 0, error: "NetworkError"});
     }
     return this._service.request(action, aRecord);
   },
 
   /**
    * Called on message from the child process. aPageRecord is an object sent by
    * navigator.push, identifying the sending page and other fields.
    */
   _registerWithServer: function(aPageRecord) {
-    debug("registerWithServer()");
+    debug("registerWithServer()" + JSON.stringify(aPageRecord));
 
     return this._sendRequest("register", aPageRecord)
       .then(pushRecord => this._onRegisterSuccess(pushRecord),
             err => this._onRegisterError(err))
       .then(pushRecord => {
-        if (this._pendingRegisterRequest[aPageRecord.scope]) {
-          delete this._pendingRegisterRequest[aPageRecord.scope];
-        }
+        this._deletePendingRequest(aPageRecord);
         return pushRecord;
       }, err => {
-        if (this._pendingRegisterRequest[aPageRecord.scope]) {
-          delete this._pendingRegisterRequest[aPageRecord.scope];
-        }
+        this._deletePendingRequest(aPageRecord);
         throw err;
      });
   },
 
   _register: function(aPageRecord) {
+    debug("_register()");
+    if (!aPageRecord.scope || aPageRecord.originAttributes === undefined) {
+      return Promise.reject({state: 0, error: "NotFoundError"});
+    }
+
     return this._checkActivated()
-      .then(_ => this._db.getByScope(aPageRecord.scope))
+      .then(_ => this._db.getByIdentifiers(aPageRecord))
       .then(pushRecord => {
         if (pushRecord === undefined) {
-          if (this._pendingRegisterRequest[aPageRecord.scope]) {
-            return this._pendingRegisterRequest[aPageRecord.scope];
-          }
-          return this._pendingRegisterRequest[aPageRecord.scope] = this._registerWithServer(aPageRecord);
+          return this._lookupOrPutPendingRequest(aPageRecord);
         }
         return pushRecord;
       }, error => {
-        debug("getByScope failed");
+        debug("getByIdentifiers failed");
         throw error;
       });
   },
 
   /**
    * Exceptions thrown in _onRegisterSuccess are caught by the promise obtained
    * from _service.request, causing the promise to be rejected instead.
    */
@@ -828,20 +860,49 @@ this.PushService = {
   receiveMessage: function(aMessage) {
     debug("receiveMessage(): " + aMessage.name);
 
     if (kCHILD_PROCESS_MESSAGES.indexOf(aMessage.name) == -1) {
       debug("Invalid message from child " + aMessage.name);
       return;
     }
 
+    if (!aMessage.target.assertPermission("push")) {
+      debug("Got message from a child process that does not have 'push' permission.");
+      return null;
+    }
+
     let mm = aMessage.target.QueryInterface(Ci.nsIMessageSender);
-    let json = aMessage.data;
+    let pageRecord = aMessage.data;
+
+    let principal = aMessage.principal;
+    if (!principal) {
+      debug("No principal passed!");
+      let message = {
+        requestID: aPageRecord.requestID,
+        error: "SecurityError"
+      };
+      mm.sendAsyncMessage("PushService:Register:KO", message);
+      return;
+    }
 
-    this[aMessage.name.slice("Push:".length).toLowerCase()](json, mm);
+    pageRecord.originAttributes =
+      ChromeUtils.originAttributesToSuffix(principal.originAttributes);
+
+    if (!pageRecord.scope || pageRecord.originAttributes === undefined) {
+      debug("Incorrect identifier values set! " + JSON.stringify(pageRecord));
+      let message = {
+        requestID: aPageRecord.requestID,
+        error: "SecurityError"
+      };
+      mm.sendAsyncMessage("PushService:Register:KO", message);
+      return;
+    }
+
+    this[aMessage.name.slice("Push:".length).toLowerCase()](pageRecord, mm);
   },
 
   register: function(aPageRecord, aMessageManager) {
     debug("register(): " + JSON.stringify(aPageRecord));
 
     this._register(aPageRecord)
       .then(pushRecord => {
         let message = this._service.prepareRegister(pushRecord);
@@ -878,23 +939,22 @@ this.PushService = {
    * reason at which point the server will say it does not know of this
    * unregistration.  We'll have to make the registration/unregistration phases
    * have retries and attempts to resend messages from the server, and have the
    * client acknowledge. On a server, data is cheap, reliable notification is
    * not.
    */
   _unregister: function(aPageRecord) {
     debug("_unregister()");
-
-    if (!aPageRecord.scope) {
+    if (!aPageRecord.scope || aPageRecord.originAttributes === undefined) {
       return Promise.reject({state: 0, error: "NotFoundError"});
     }
 
     return this._checkActivated()
-      .then(_ => this._db.getByScope(aPageRecord.scope))
+      .then(_ => this._db.getByIdentifiers(aPageRecord))
       .then(record => {
         // If the endpoint didn't exist, let's just fail.
         if (record === undefined) {
           throw "NotFoundError";
         }
 
         return Promise.all([
           this._sendRequest("unregister", record),
@@ -927,22 +987,22 @@ this.PushService = {
       });
   },
 
   /**
    * Called on message from the child process
    */
   _registration: function(aPageRecord) {
     debug("_registration()");
-    if (!aPageRecord.scope) {
-      return Promise.reject({state: 0, error: "Database error"});
+    if (!aPageRecord.scope || aPageRecord.originAttributes === undefined) {
+      return Promise.reject({state: 0, error: "NotFoundError"});
     }
 
     return this._checkActivated()
-      .then(_ => this._db.getByScope(aPageRecord.scope))
+      .then(_ => this._db.getByIdentifiers(aPageRecord))
       .then(pushRecord => {
         if (!pushRecord) {
           return null;
         }
         return this._service.prepareRegistration(pushRecord);
       });
   },
 
--- a/dom/push/PushServiceHttp2.jsm
+++ b/dom/push/PushServiceHttp2.jsm
@@ -28,17 +28,17 @@ var gDebuggingEnabled = prefs.get("debug
 
 function debug(s) {
   if (gDebuggingEnabled) {
     dump("-*- PushServiceHttp2.jsm: " + s + "\n");
   }
 }
 
 const kPUSHHTTP2DB_DB_NAME = "pushHttp2";
-const kPUSHHTTP2DB_DB_VERSION = 1; // Change this if the IndexedDB format changes
+const kPUSHHTTP2DB_DB_VERSION = 3; // Change this if the IndexedDB format changes
 const kPUSHHTTP2DB_STORE_NAME = "pushHttp2";
 
 /**
  * A proxy between the PushService and connections listening for incoming push
  * messages. The PushService can silence messages from the connections by
  * setting PushSubscriptionListener._pushService to null. This is required
  * because it can happen that there is an outstanding push message that will
  * be send on OnStopRequest but the PushService may not be interested in these.
@@ -286,16 +286,17 @@ SubscriptionListener.prototype = {
     }
 
     var reply = {
       subscriptionUri: subscriptionUri,
       pushEndpoint: linkParserResult.pushEndpoint,
       pushReceiptEndpoint: linkParserResult.pushReceiptEndpoint,
       pageURL: this._subInfo.record.pageURL,
       scope: this._subInfo.record.scope,
+      originAttributes: this._subInfo.record.originAttributes,
       pushCount: 0,
       lastPush: 0
     };
     this._subInfo.resolve(reply);
   },
 };
 
 function retryAfterParser(aRequest) {
@@ -380,25 +381,43 @@ this.PushServiceHttp2 = {
 
   upgradeSchema: function(aTransaction,
                           aDb,
                           aOldVersion,
                           aNewVersion,
                           aDbInstance) {
     debug("upgradeSchemaHttp2()");
 
+    //XXXnsm We haven't shipped Push during this upgrade, so I'm just going to throw old
+    //registrations away without even informing the app.
+    if (aNewVersion != aOldVersion) {
+      try {
+        aDb.deleteObjectStore(aDbInstance._dbStoreName);
+      } catch (e) {
+        if (e.name === "NotFoundError") {
+          debug("No existing object store found");
+        } else {
+          throw e;
+        }
+      }
+    }
+
     let objectStore = aDb.createObjectStore(aDbInstance._dbStoreName,
                                             { keyPath: "subscriptionUri" });
 
     // index to fetch records based on endpoints. used by unregister
     objectStore.createIndex("pushEndpoint", "pushEndpoint", { unique: true });
 
-    // index to fetch records per scope, so we can identify endpoints
-    // associated with an app.
-    objectStore.createIndex("scope", "scope", { unique: true });
+    // index to fetch records by identifiers.
+    // In the current security model, the originAttributes distinguish between
+    // different 'apps' on the same origin. Since ServiceWorkers are
+    // same-origin to the scope they are registered for, the attributes and
+    // scope are enough to reconstruct a valid principal.
+    objectStore.createIndex("identifiers", ["scope", "originAttributes"], { unique: true });
+    objectStore.createIndex("originAttributes", "originAttributes", { unique: false });
   },
 
   getKeyFromRecord: function(aRecord) {
     return aRecord.subscriptionUri;
   },
 
   newPushDB: function() {
     return new PushDB(kPUSHHTTP2DB_DB_NAME,
--- a/dom/push/PushServiceWebSocket.jsm
+++ b/dom/push/PushServiceWebSocket.jsm
@@ -27,17 +27,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
                                    "@mozilla.org/power/powermanagerservice;1",
                                    "nsIPowerManagerService");
 #endif
 
 var threadManager = Cc["@mozilla.org/thread-manager;1"]
                       .getService(Ci.nsIThreadManager);
 
 const kPUSHWSDB_DB_NAME = "pushapi";
-const kPUSHWSDB_DB_VERSION = 1; // Change this if the IndexedDB format changes
+const kPUSHWSDB_DB_VERSION = 3; // Change this if the IndexedDB format changes
 const kPUSHWSDB_STORE_NAME = "pushapi";
 
 const kUDP_WAKEUP_WS_STATUS_CODE = 4774;  // WebSocket Close status code sent
                                           // by server to signal that it can
                                           // wake client up using UDP.
 
 const kWS_MAX_WENTDOWN = 2;
 
@@ -127,25 +127,43 @@ this.PushServiceWebSocket = {
 
   upgradeSchema: function(aTransaction,
                           aDb,
                           aOldVersion,
                           aNewVersion,
                           aDbInstance) {
     debug("upgradeSchemaWS()");
 
+    //XXXnsm We haven't shipped Push during this upgrade, so I'm just going to throw old
+    //registrations away without even informing the app.
+    if (aNewVersion != aOldVersion) {
+      try {
+        aDb.deleteObjectStore(aDbInstance._dbStoreName);
+      } catch (e) {
+        if (e.name === "NotFoundError") {
+          debug("No existing object store found");
+        } else {
+          throw e;
+        }
+      }
+    }
+
     let objectStore = aDb.createObjectStore(aDbInstance._dbStoreName,
                                             { keyPath: "channelID" });
 
     // index to fetch records based on endpoints. used by unregister
     objectStore.createIndex("pushEndpoint", "pushEndpoint", { unique: true });
 
-    // index to fetch records per scope, so we can identify endpoints
-    // associated with an app.
-    objectStore.createIndex("scope", "scope", { unique: true });
+    // index to fetch records by identifiers.
+    // In the current security model, the originAttributes distinguish between
+    // different 'apps' on the same origin. Since ServiceWorkers are
+    // same-origin to the scope they are registered for, the attributes and
+    // scope are enough to reconstruct a valid principal.
+    objectStore.createIndex("identifiers", ["scope", "originAttributes"], { unique: true });
+    objectStore.createIndex("originAttributes", "originAttributes", { unique: false });
   },
 
   getKeyFromRecord: function(aRecord) {
     return aRecord.channelID;
   },
 
   newPushDB: function() {
     return new PushDB(kPUSHWSDB_DB_NAME,
@@ -865,20 +883,22 @@ this.PushServiceWebSocket = {
         return;
       }
 
       let record = {
         channelID: reply.channelID,
         pushEndpoint: reply.pushEndpoint,
         pageURL: tmp.record.pageURL,
         scope: tmp.record.scope,
+        originAttributes: tmp.record.originAttributes,
         pushCount: 0,
         lastPush: 0,
         version: null
       };
+      dump("PushWebSocket " +  JSON.stringify(record));
       tmp.resolve(record);
     } else {
       tmp.reject(reply);
     }
   },
 
   /**
    * Protocol handler invoked by server message.
--- a/dom/push/test/mochitest.ini
+++ b/dom/push/test/mochitest.ini
@@ -10,12 +10,14 @@ skip-if = os == "android" || toolkit == 
 [test_permissions.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_register.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_multiple_register.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_multiple_register_during_service_activation.html]
 skip-if = os == "android" || toolkit == "gonk"
+[test_unregister.html]
+skip-if = os == "android" || toolkit == "gonk"
 [test_multiple_register_different_scope.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_try_registering_offline_disabled.html]
 skip-if = os == "android" || toolkit == "gonk"
--- a/dom/push/test/test_multiple_register.html
+++ b/dom/push/test/test_multiple_register.html
@@ -115,16 +115,17 @@ http://creativecommons.org/licenses/publ
     .then(unregister)
     .catch(function(e) {
       ok(false, "Some test failed with error " + e);
     }).then(SimpleTest.finish);
   }
 
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.push.enabled", true],
+    ["dom.push.debug", true],
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
     ]}, runTest);
   SpecialPowers.addPermission('push', true, document);
   SimpleTest.waitForExplicitFinish();
 </script>
 </body>
new file mode 100644
--- /dev/null
+++ b/dom/push/test/test_unregister.html
@@ -0,0 +1,93 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Bug 1170817: Push tests.
+
+Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/licenses/publicdomain/
+
+-->
+<head>
+  <title>Test for Bug 1170817</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+</head>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1170817">Mozilla Bug 1170817</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+
+<script class="testbody" type="text/javascript">
+
+  var registration;
+
+  function start() {
+    return navigator.serviceWorker.register("worker.js" + "?" + (Math.random()), {scope: "."})
+    .then(swr => { registration = swr; return swr; });
+  }
+
+  function unregisterSW() {
+    return registration.unregister().then(function(result) {
+      ok(result, "Unregister should return true.");
+    }, function(e) {
+      dump("Unregistering the SW failed with " + e + "\n");
+    });
+  }
+
+  function setupPushNotification(swr) {
+    return swr.pushManager.subscribe().then(
+      pushSubscription => {
+        ok(true, "successful registered for push notification");
+        return pushSubscription;
+      }, error => {
+        ok(false, "could not register for push notification");
+      });
+  }
+
+  function unregisterPushNotification(pushSubscription) {
+    return pushSubscription.unsubscribe().then(
+      result => {
+      ok(result, "unsubscribe() on existing subscription should return true.");
+      return pushSubscription;
+    }, error => {
+      ok(false, "unsubscribe() should never fail.");
+    });
+  }
+
+  function unregisterAgain(pushSubscription) {
+    return pushSubscription.unsubscribe().then(
+      result => {
+      ok(!result, "unsubscribe() on previously unsubscribed subscription should return false.");
+      return pushSubscription;
+    }, error => {
+      ok(false, "unsubscribe() should never fail.");
+    });
+  }
+
+  function runTest() {
+    start()
+    .then(setupPushNotification)
+    .then(unregisterPushNotification)
+    .then(unregisterAgain)
+    .then(unregisterSW)
+    .catch(function(e) {
+      ok(false, "Some test failed with error " + e);
+    }).then(SimpleTest.finish);
+  }
+
+  SpecialPowers.pushPrefEnv({"set": [
+    ["dom.push.enabled", true],
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true]
+    ]}, runTest);
+  SpecialPowers.addPermission('push', true, document);
+  SimpleTest.waitForExplicitFinish();
+</script>
+</body>
+</html>
+
--- a/dom/push/test/xpcshell/test_notification_ack.js
+++ b/dom/push/test/xpcshell/test_notification_ack.js
@@ -20,26 +20,29 @@ function run_test() {
 
 add_task(function* test_notification_ack() {
   let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
   let records = [{
     channelID: '21668e05-6da8-42c9-b8ab-9cc3f4d5630c',
     pushEndpoint: 'https://example.com/update/1',
     scope: 'https://example.org/1',
+    originAttributes: '',
     version: 1
   }, {
     channelID: '9a5ff87f-47c9-4215-b2b8-0bdd38b4b305',
     pushEndpoint: 'https://example.com/update/2',
     scope: 'https://example.org/2',
+    originAttributes: '',
     version: 2
   }, {
     channelID: '5477bfda-22db-45d4-9614-fee369630260',
     pushEndpoint: 'https://example.com/update/3',
     scope: 'https://example.org/3',
+    originAttributes: '',
     version: 3
   }];
   for (let record of records) {
     yield db.put(record);
   }
 
   let notifyPromise = Promise.all([
     promiseObserverNotification('push-notification'),
--- a/dom/push/test/xpcshell/test_notification_duplicate.js
+++ b/dom/push/test/xpcshell/test_notification_duplicate.js
@@ -18,21 +18,23 @@ function run_test() {
 // Should acknowledge duplicate notifications, but not notify apps.
 add_task(function* test_notification_duplicate() {
   let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
   let records = [{
     channelID: '8d2d9400-3597-4c5a-8a38-c546b0043bcc',
     pushEndpoint: 'https://example.org/update/1',
     scope: 'https://example.com/1',
+    originAttributes: "",
     version: 2
   }, {
     channelID: '27d1e393-03ef-4c72-a5e6-9e890dfccad0',
     pushEndpoint: 'https://example.org/update/2',
     scope: 'https://example.com/2',
+    originAttributes: "",
     version: 2
   }];
   for (let record of records) {
     yield db.put(record);
   }
 
   let notifyPromise = promiseObserverNotification('push-notification');
 
--- a/dom/push/test/xpcshell/test_notification_error.js
+++ b/dom/push/test/xpcshell/test_notification_error.js
@@ -14,30 +14,35 @@ function run_test() {
     'https://example.com/c'
   );
   run_next_test();
 }
 
 add_task(function* test_notification_error() {
   let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
+
+  let originAttributes = '';
   let records = [{
     channelID: 'f04f1e46-9139-4826-b2d1-9411b0821283',
     pushEndpoint: 'https://example.org/update/success-1',
     scope: 'https://example.com/a',
+    originAttributes: originAttributes,
     version: 1
   }, {
     channelID: '3c3930ba-44de-40dc-a7ca-8a133ec1a866',
     pushEndpoint: 'https://example.org/update/error',
     scope: 'https://example.com/b',
+    originAttributes: originAttributes,
     version: 2
   }, {
     channelID: 'b63f7bef-0a0d-4236-b41e-086a69dfd316',
     pushEndpoint: 'https://example.org/update/success-2',
     scope: 'https://example.com/c',
+    originAttributes: originAttributes,
     version: 3
   }];
   for (let record of records) {
     yield db.put(record);
   }
 
   let notifyPromise = Promise.all([
     promiseObserverNotification(
@@ -102,26 +107,29 @@ add_task(function* test_notification_err
   let cPush = c.subject.QueryInterface(Ci.nsIPushObserverNotification);
   equal(cPush.pushEndpoint, 'https://example.org/update/success-2',
     'Wrong endpoint for notification C');
   equal(cPush.version, 4, 'Wrong version for notification C');
 
   yield waitForPromise(ackDefer.promise, DEFAULT_TIMEOUT,
     'Timed out waiting for acknowledgements');
 
-  let aRecord = yield db.getByScope('https://example.com/a');
+  let aRecord = yield db.getByIdentifiers({scope: 'https://example.com/a',
+                                           originAttributes: originAttributes });
   equal(aRecord.channelID, 'f04f1e46-9139-4826-b2d1-9411b0821283',
     'Wrong channel ID for record A');
   strictEqual(aRecord.version, 2,
     'Should return the new version for record A');
 
-  let bRecord = yield db.getByScope('https://example.com/b');
+  let bRecord = yield db.getByIdentifiers({scope: 'https://example.com/b',
+                                           originAttributes: originAttributes });
   equal(bRecord.channelID, '3c3930ba-44de-40dc-a7ca-8a133ec1a866',
     'Wrong channel ID for record B');
   strictEqual(bRecord.version, 2,
     'Should return the previous version for record B');
 
-  let cRecord = yield db.getByScope('https://example.com/c');
+  let cRecord = yield db.getByIdentifiers({scope: 'https://example.com/c',
+                                           originAttributes: originAttributes });
   equal(cRecord.channelID, 'b63f7bef-0a0d-4236-b41e-086a69dfd316',
     'Wrong channel ID for record C');
   strictEqual(cRecord.version, 4,
     'Should return the new version for record C');
 });
--- a/dom/push/test/xpcshell/test_notification_http2.js
+++ b/dom/push/test/xpcshell/test_notification_http2.js
@@ -52,27 +52,30 @@ add_task(function* test_pushNotification
   });
 
   var serverURL = "https://localhost:" + serverPort;
 
   let records = [{
     subscriptionUri: serverURL + '/pushNotifications/subscription1',
     pushEndpoint: serverURL + '/pushEndpoint1',
     pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint1',
-    scope: 'https://example.com/page/1'
+    scope: 'https://example.com/page/1',
+    originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
   }, {
     subscriptionUri: serverURL + '/pushNotifications/subscription2',
     pushEndpoint: serverURL + '/pushEndpoint2',
     pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint2',
-    scope: 'https://example.com/page/2'
+    scope: 'https://example.com/page/2',
+    originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
   }, {
     subscriptionUri: serverURL + '/pushNotifications/subscription3',
     pushEndpoint: serverURL + '/pushEndpoint3',
     pushReceiptEndpoint: serverURL + '/pushReceiptEndpoint3',
-    scope: 'https://example.com/page/3'
+    scope: 'https://example.com/page/3',
+    originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
   }];
 
   for (let record of records) {
     yield db.put(record);
   }
 
   let notifyPromise = Promise.all([
     promiseObserverNotification('push-notification'),
--- a/dom/push/test/xpcshell/test_notification_version_string.js
+++ b/dom/push/test/xpcshell/test_notification_version_string.js
@@ -16,16 +16,17 @@ function run_test() {
 
 add_task(function* test_notification_version_string() {
   let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
   yield db.put({
     channelID: '6ff97d56-d0c0-43bc-8f5b-61b855e1d93b',
     pushEndpoint: 'https://example.org/updates/1',
     scope: 'https://example.com/page/1',
+    originAttributes: '',
     version: 2
   });
 
   let notifyPromise = promiseObserverNotification('push-notification');
 
   let ackDefer = Promise.defer();
   PushService.init({
     serverURI: "wss://push.example.org/",
--- a/dom/push/test/xpcshell/test_register_5xxCode_http2.js
+++ b/dom/push/test/xpcshell/test_register_5xxCode_http2.js
@@ -79,17 +79,18 @@ add_task(function* test1() {
 
   PushService.init({
     serverURI: serverURL + "/subscribe5xxCode",
     service: PushServiceHttp2,
     db
   });
 
   let newRecord = yield PushNotificationService.register(
-    'https://example.com/retry5xxCode'
+    'https://example.com/retry5xxCode',
+    { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }
   );
 
   var subscriptionUri = serverURL + '/subscription';
   var pushEndpoint = serverURL + '/pushEndpoint';
   var pushReceiptEndpoint = serverURL + '/receiptPushEndpoint';
   equal(newRecord.subscriptionUri, subscriptionUri,
     'Wrong subscription ID in registration record');
   equal(newRecord.pushEndpoint, pushEndpoint,
--- a/dom/push/test/xpcshell/test_register_case.js
+++ b/dom/push/test/xpcshell/test_register_case.js
@@ -42,17 +42,18 @@ add_task(function* test_register_case() 
             pushEndpoint: 'https://example.com/update/case'
           }));
         }
       });
     }
   });
 
   let newRecord = yield waitForPromise(
-    PushNotificationService.register('https://example.net/case'),
+    PushNotificationService.register('https://example.net/case',
+      { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
     DEFAULT_TIMEOUT,
     'Mixed-case register response timed out'
   );
   equal(newRecord.pushEndpoint, 'https://example.com/update/case',
     'Wrong push endpoint in registration record');
   equal(newRecord.scope, 'https://example.net/case',
     'Wrong scope in registration record');
 
--- a/dom/push/test/xpcshell/test_register_error_http2.js
+++ b/dom/push/test/xpcshell/test_register_error_http2.js
@@ -42,17 +42,18 @@ add_task(function* test_pushSubscription
 
   PushService.init({
     serverURI: serverURL + "/pushSubscriptionNoConnection/subscribe",
     db
   });
 
   yield rejects(
     PushNotificationService.register(
-      'https://example.net/page/invalid-response'),
+      'https://example.net/page/invalid-response',
+      ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
     function(error) {
       return error && error.includes("Error");
     },
     'Wrong error for not being able to establish connecion.'
   );
 
   let record = yield db.getAllKeyIDs();
   ok(record.length === 0, "Should not store records when connection couldn't be established.");
@@ -82,17 +83,18 @@ add_task(function* test_pushSubscription
 
   PushService.init({
     serverURI: serverURL + "/pushSubscriptionMissingLocation/subscribe",
     db
   });
 
   yield rejects(
     PushNotificationService.register(
-      'https://example.net/page/invalid-response'),
+      'https://example.net/page/invalid-response',
+      ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
     function(error) {
       return error && error.includes("Return code 201, but the answer is bogus");
     },
     'Wrong error for the missing location header.'
   );
 
   let record = yield db.getAllKeyIDs();
   ok(record.length === 0, 'Should not store records when the location header is missing.');
@@ -108,17 +110,18 @@ add_task(function* test_pushSubscription
 
   PushService.init({
     serverURI: serverURL + "/pushSubscriptionMissingLink/subscribe",
     db
   });
 
   yield rejects(
     PushNotificationService.register(
-      'https://example.net/page/invalid-response'),
+      'https://example.net/page/invalid-response',
+      ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
     function(error) {
       return error && error.includes("Return code 201, but the answer is bogus");
     },
     'Wrong error for the missing link header.'
   );
 
   let record = yield db.getAllKeyIDs();
   ok(record.length === 0, 'Should not store records when a link header is missing.');
@@ -134,17 +137,18 @@ add_task(function* test_pushSubscription
 
   PushService.init({
     serverURI: serverURL + "/pushSubscriptionMissingLink1/subscribe",
     db
   });
 
   yield rejects(
     PushNotificationService.register(
-      'https://example.net/page/invalid-response'),
+      'https://example.net/page/invalid-response',
+      ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
     function(error) {
       return error && error.includes("Return code 201, but the answer is bogus");
     },
     'Wrong error for the missing push endpoint.'
   );
 
   let record = yield db.getAllKeyIDs();
   ok(record.length === 0, 'Should not store records when the push endpoint is missing.');
@@ -160,17 +164,18 @@ add_task(function* test_pushSubscription
 
   PushService.init({
     serverURI: serverURL + "/pushSubscriptionLocationBogus/subscribe",
     db
   });
 
   yield rejects(
     PushNotificationService.register(
-      'https://example.net/page/invalid-response'),
+      'https://example.net/page/invalid-response',
+      ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
     function(error) {
       return error && error.includes("Return code 201, but URI is bogus.");
     },
     'Wrong error for the bogus location'
   );
 
   let record = yield db.getAllKeyIDs();
   ok(record.length === 0, 'Should not store records when location header is bogus.');
@@ -186,17 +191,18 @@ add_task(function* test_pushSubscription
 
   PushService.init({
     serverURI: serverURL + "/pushSubscriptionNot201Code/subscribe",
     db
   });
 
   yield rejects(
     PushNotificationService.register(
-      'https://example.net/page/invalid-response'),
+      'https://example.net/page/invalid-response',
+      ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
     function(error) {
       return error && error.includes("Error");
     },
     'Wrong error for not 201 responce code.'
   );
 
   let record = yield db.getAllKeyIDs();
   ok(record.length === 0, 'Should not store records when respons code is not 201.');
--- a/dom/push/test/xpcshell/test_register_flush.js
+++ b/dom/push/test/xpcshell/test_register_flush.js
@@ -24,16 +24,17 @@ function run_test() {
 
 add_task(function* test_register_flush() {
   let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
   let record = {
     channelID: '9bcc7efb-86c7-4457-93ea-e24e6eb59b74',
     pushEndpoint: 'https://example.org/update/1',
     scope: 'https://example.com/page/1',
+    originAttributes: '',
     version: 2
   };
   yield db.put(record);
 
   let notifyPromise = promiseObserverNotification('push-notification');
 
   let ackDefer = Promise.defer();
   let ackDone = after(2, ackDefer.resolve);
@@ -70,18 +71,17 @@ add_task(function* test_register_flush()
           }));
         },
         onACK: ackDone
       });
     }
   });
 
   let newRecord = yield PushNotificationService.register(
-    'https://example.com/page/2'
-  );
+    'https://example.com/page/2', '');
   equal(newRecord.pushEndpoint, 'https://example.org/update/2',
     'Wrong push endpoint in record');
   equal(newRecord.scope, 'https://example.com/page/2',
     'Wrong scope in record');
 
   let {data: scope} = yield waitForPromise(notifyPromise, DEFAULT_TIMEOUT,
     'Timed out waiting for notification');
   equal(scope, 'https://example.com/page/1', 'Wrong notification scope');
--- a/dom/push/test/xpcshell/test_register_invalid_channel.js
+++ b/dom/push/test/xpcshell/test_register_invalid_channel.js
@@ -43,17 +43,18 @@ add_task(function* test_register_invalid
             error: 'Invalid channel ID'
           }));
         }
       });
     }
   });
 
   yield rejects(
-    PushNotificationService.register('https://example.com/invalid-channel'),
+    PushNotificationService.register('https://example.com/invalid-channel',
+      { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
     function(error) {
       return error == 'Invalid channel ID';
     },
     'Wrong error for invalid channel ID'
   );
 
   let record = yield db.getByKeyID(channelID);
   ok(!record, 'Should not store records for error responses');
--- a/dom/push/test/xpcshell/test_register_invalid_endpoint.js
+++ b/dom/push/test/xpcshell/test_register_invalid_endpoint.js
@@ -45,17 +45,18 @@ add_task(function* test_register_invalid
           }));
         }
       });
     }
   });
 
   yield rejects(
     PushNotificationService.register(
-      'https://example.net/page/invalid-endpoint'),
+      'https://example.net/page/invalid-endpoint',
+      { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
     function(error) {
       return error && error.includes('Invalid pushEndpoint');
     },
     'Wrong error for invalid endpoint'
   );
 
   let record = yield db.getByKeyID(channelID);
   ok(!record, 'Should not store records with invalid endpoints');
--- a/dom/push/test/xpcshell/test_register_invalid_json.js
+++ b/dom/push/test/xpcshell/test_register_invalid_json.js
@@ -44,17 +44,18 @@ add_task(function* test_register_invalid
           this.serverSendMsg(');alert(1);(');
           registers++;
         }
       });
     }
   });
 
   yield rejects(
-    PushNotificationService.register('https://example.net/page/invalid-json'),
+    PushNotificationService.register('https://example.net/page/invalid-json',
+      { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
     function(error) {
       return error == 'TimeoutError';
     },
     'Wrong error for invalid JSON response'
   );
 
   yield waitForPromise(helloDefer.promise, DEFAULT_TIMEOUT,
     'Reconnect after invalid JSON response timed out');
--- a/dom/push/test/xpcshell/test_register_no_id.js
+++ b/dom/push/test/xpcshell/test_register_no_id.js
@@ -48,17 +48,18 @@ add_task(function* test_register_no_id()
             status: 200
           }));
         }
       });
     }
   });
 
   yield rejects(
-    PushNotificationService.register('https://example.com/incomplete'),
+    PushNotificationService.register('https://example.com/incomplete',
+      { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
     function(error) {
       return error == 'TimeoutError';
     },
     'Wrong error for incomplete register response'
   );
 
   yield waitForPromise(helloDefer.promise, DEFAULT_TIMEOUT,
     'Reconnect after incomplete register response timed out');
--- a/dom/push/test/xpcshell/test_register_request_queue.js
+++ b/dom/push/test/xpcshell/test_register_request_queue.js
@@ -40,20 +40,22 @@ add_task(function* test_register_request
         onRegister() {
           ok(false, 'Should cancel timed-out requests');
         }
       });
     }
   });
 
   let firstRegister = PushNotificationService.register(
-    'https://example.com/page/1'
+    'https://example.com/page/1',
+    { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }
   );
   let secondRegister = PushNotificationService.register(
-    'https://example.com/page/1'
+    'https://example.com/page/1',
+    { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }
   );
 
   yield waitForPromise(Promise.all([
     rejects(firstRegister, function(error) {
       return error == 'TimeoutError';
     }, 'Should time out the first request'),
     rejects(secondRegister, function(error) {
       return error == 'TimeoutError';
--- a/dom/push/test/xpcshell/test_register_rollback.js
+++ b/dom/push/test/xpcshell/test_register_rollback.js
@@ -69,17 +69,18 @@ add_task(function* test_register_rollbac
           unregisterDefer.resolve();
         }
       });
     }
   });
 
   // Should return a rejected promise if storage fails.
   yield rejects(
-    PushNotificationService.register('https://example.com/storage-error'),
+    PushNotificationService.register('https://example.com/storage-error',
+      { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
     function(error) {
       return error == 'universe has imploded';
     },
     'Wrong error for unregister database failure'
   );
 
   // Should send an out-of-band unregister request.
   yield waitForPromise(unregisterDefer.promise, DEFAULT_TIMEOUT,
--- a/dom/push/test/xpcshell/test_register_success.js
+++ b/dom/push/test/xpcshell/test_register_success.js
@@ -52,17 +52,18 @@ add_task(function* test_register_success
             pushEndpoint: 'https://example.com/update/1',
           }));
         }
       });
     }
   });
 
   let newRecord = yield PushNotificationService.register(
-    'https://example.org/1'
+    'https://example.org/1',
+    { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }
   );
   equal(newRecord.channelID, channelID,
     'Wrong channel ID in registration record');
   equal(newRecord.pushEndpoint, 'https://example.com/update/1',
     'Wrong push endpoint in registration record');
   equal(newRecord.scope, 'https://example.org/1',
     'Wrong scope in registration record');
 
--- a/dom/push/test/xpcshell/test_register_success_http2.js
+++ b/dom/push/test/xpcshell/test_register_success_http2.js
@@ -52,17 +52,18 @@ add_task(function* test_pushSubscription
   });
 
   PushService.init({
     serverURI: serverURL + "/pushSubscriptionSuccess/subscribe",
     db
   });
 
   let newRecord = yield PushNotificationService.register(
-    'https://example.org/1'
+    'https://example.org/1',
+    ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
   );
 
   var subscriptionUri = serverURL + '/pushSubscriptionSuccesss';
   var pushEndpoint = serverURL + '/pushEndpointSuccess';
   var pushReceiptEndpoint = serverURL + '/receiptPushEndpointSuccess';
   equal(newRecord.subscriptionUri, subscriptionUri,
     'Wrong subscription ID in registration record');
   equal(newRecord.pushEndpoint, pushEndpoint,
@@ -94,17 +95,18 @@ add_task(function* test_pushSubscription
   });
 
   PushService.init({
     serverURI: serverURL + "/pushSubscriptionMissingLink2/subscribe",
     db
   });
 
   let newRecord = yield PushNotificationService.register(
-    'https://example.org/no_receiptEndpoint'
+    'https://example.org/no_receiptEndpoint',
+    ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
   );
 
   var subscriptionUri = serverURL + '/subscriptionMissingLink2';
   var pushEndpoint = serverURL + '/pushEndpointMissingLink2';
   var pushReceiptEndpoint = '';
   equal(newRecord.subscriptionUri, subscriptionUri,
     'Wrong subscription ID in registration record');
   equal(newRecord.pushEndpoint, pushEndpoint,
--- a/dom/push/test/xpcshell/test_register_timeout.js
+++ b/dom/push/test/xpcshell/test_register_timeout.js
@@ -78,17 +78,18 @@ add_task(function* test_register_timeout
           }, 2000);
           registers++;
         }
       });
     }
   });
 
   yield rejects(
-    PushNotificationService.register('https://example.net/page/timeout'),
+    PushNotificationService.register('https://example.net/page/timeout',
+      { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
     function(error) {
       return error == 'TimeoutError';
     },
     'Wrong error for request timeout'
   );
 
   let record = yield db.getByKeyID(channelID);
   ok(!record, 'Should not store records for timed-out responses');
--- a/dom/push/test/xpcshell/test_register_wrong_id.js
+++ b/dom/push/test/xpcshell/test_register_wrong_id.js
@@ -54,17 +54,18 @@ add_task(function* test_register_wrong_i
             channelID: serverChannelID
           }));
         }
       });
     }
   });
 
   yield rejects(
-    PushNotificationService.register('https://example.com/mismatched'),
+    PushNotificationService.register('https://example.com/mismatched',
+      { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
     function(error) {
       return error == 'TimeoutError';
     },
     'Wrong error for mismatched register reply'
   );
 
   yield waitForPromise(helloDefer.promise, DEFAULT_TIMEOUT,
     'Reconnect after mismatched register reply timed out');
--- a/dom/push/test/xpcshell/test_register_wrong_type.js
+++ b/dom/push/test/xpcshell/test_register_wrong_type.js
@@ -50,17 +50,18 @@ add_task(function* test_register_wrong_t
         }
       });
     }
   });
 
   let promise =
 
   yield rejects(
-    PushNotificationService.register('https://example.com/mistyped'),
+    PushNotificationService.register('https://example.com/mistyped',
+      { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
     function(error) {
       return error == 'TimeoutError';
     },
     'Wrong error for non-string channel ID'
   );
 
   yield waitForPromise(helloDefer.promise, DEFAULT_TIMEOUT,
     'Reconnect after sending non-string channel ID timed out');
--- a/dom/push/test/xpcshell/test_registration_error.js
+++ b/dom/push/test/xpcshell/test_registration_error.js
@@ -16,25 +16,26 @@ function run_test() {
 add_task(function* test_registrations_error() {
   let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
 
   PushService.init({
     serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db: makeStub(db, {
-      getByScope(prev, scope) {
+      getByIdentifiers(prev, scope) {
         return Promise.reject('Database error');
       }
     }),
     makeWebSocket(uri) {
       return new MockWebSocket(uri);
     }
   });
 
   yield rejects(
-    PushNotificationService.registration('https://example.net/1'),
+    PushNotificationService.registration('https://example.net/1',
+      { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
     function(error) {
       return error == 'Database error';
     },
     'Wrong message'
   );
 });
--- a/dom/push/test/xpcshell/test_registration_error_http2.js
+++ b/dom/push/test/xpcshell/test_registration_error_http2.js
@@ -13,22 +13,23 @@ function run_test() {
 add_task(function* test_registrations_error() {
   let db = PushServiceHttp2.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
 
   PushService.init({
     serverURI: "https://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db: makeStub(db, {
-      getByScope(prev, scope) {
+      getByIdentifiers() {
         return Promise.reject('Database error');
       }
     }),
   });
 
   yield rejects(
-    PushNotificationService.registration('https://example.net/1'),
+    PushNotificationService.registration('https://example.net/1',
+      ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })),
     function(error) {
       return error == 'Database error';
     },
     'Wrong message'
   );
 });
--- a/dom/push/test/xpcshell/test_registration_missing_scope.js
+++ b/dom/push/test/xpcshell/test_registration_missing_scope.js
@@ -15,15 +15,15 @@ add_task(function* test_registration_mis
   PushService.init({
     serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     makeWebSocket(uri) {
       return new MockWebSocket(uri);
     }
   });
   yield rejects(
-    PushNotificationService.registration(''),
+    PushNotificationService.registration('', ''),
     function(error) {
-      return error.error == 'Database error';
+      return error.error == 'NotFoundError';
     },
     'Record missing page and manifest URLs'
   );
 });
--- a/dom/push/test/xpcshell/test_registration_none.js
+++ b/dom/push/test/xpcshell/test_registration_none.js
@@ -19,11 +19,12 @@ add_task(function* test_registration_non
     serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     makeWebSocket(uri) {
       return new MockWebSocket(uri);
     }
   });
 
   let registration = yield PushNotificationService.registration(
-    'https://example.net/1');
+    'https://example.net/1',
+    { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false });
   ok(!registration, 'Should not open a connection without registration');
 });
--- a/dom/push/test/xpcshell/test_registration_success.js
+++ b/dom/push/test/xpcshell/test_registration_success.js
@@ -15,26 +15,29 @@ function run_test() {
 
 add_task(function* test_registration_success() {
   let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
   let records = [{
     channelID: 'bf001fe0-2684-42f2-bc4d-a3e14b11dd5b',
     pushEndpoint: 'https://example.com/update/same-manifest/1',
     scope: 'https://example.net/a',
+    originAttributes: '',
     version: 5
   }, {
     channelID: 'f6edfbcd-79d6-49b8-9766-48b9dcfeff0f',
     pushEndpoint: 'https://example.com/update/same-manifest/2',
     scope: 'https://example.net/b',
+    originAttributes: ChromeUtils.originAttributesToSuffix({ appId: 42 }),
     version: 10
   }, {
     channelID: 'b1cf38c9-6836-4d29-8a30-a3e98d59b728',
     pushEndpoint: 'https://example.org/update/different-manifest',
     scope: 'https://example.org/c',
+    originAttributes: ChromeUtils.originAttributesToSuffix({ appId: 42, inBrowser: true }),
     version: 15
   }];
   for (let record of records) {
     yield db.put(record);
   }
 
   PushService.init({
     serverURI: "wss://push.example.org/",
@@ -54,16 +57,16 @@ add_task(function* test_registration_suc
             uaid: userAgentID
           }));
         }
       });
     }
   });
 
   let registration = yield PushNotificationService.registration(
-    'https://example.net/a');
+    'https://example.net/a', '');
   equal(
     registration.pushEndpoint,
     'https://example.com/update/same-manifest/1',
     'Wrong push endpoint for scope'
   );
   equal(registration.version, 5, 'Wrong version for scope');
 });
--- a/dom/push/test/xpcshell/test_registration_success_http2.js
+++ b/dom/push/test/xpcshell/test_registration_success_http2.js
@@ -36,38 +36,42 @@ add_task(function* test_pushNotification
   });
 
   var serverURL = "https://localhost:" + serverPort;
 
   let records = [{
     subscriptionUri: serverURL + '/subscriptionA',
     pushEndpoint: serverURL + '/pushEndpointA',
     pushReceiptEndpoint: serverURL + '/pushReceiptEndpointA',
-    scope: 'https://example.net/a'
+    scope: 'https://example.net/a',
+    originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
   }, {
     subscriptionUri: serverURL + '/subscriptionB',
     pushEndpoint: serverURL + '/pushEndpointB',
     pushReceiptEndpoint: serverURL + '/pushReceiptEndpointB',
-    scope: 'https://example.net/b'
+    scope: 'https://example.net/b',
+    originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
   }, {
     subscriptionUri: serverURL + '/subscriptionC',
     pushEndpoint: serverURL + '/pushEndpointC',
     pushReceiptEndpoint: serverURL + '/pushReceiptEndpointC',
-    scope: 'https://example.net/c'
+    scope: 'https://example.net/c',
+    originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false })
   }];
 
   for (let record of records) {
     yield db.put(record);
   }
 
   PushService.init({
     serverURI: serverURL,
     db
   });
 
   let registration = yield PushNotificationService.registration(
-    'https://example.net/a');
+    'https://example.net/a',
+    ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }));
   equal(
     registration.pushEndpoint,
     serverURL + '/pushEndpointA',
     'Wrong push endpoint for scope'
   );
 });
--- a/dom/push/test/xpcshell/test_unregister_empty_scope.js
+++ b/dom/push/test/xpcshell/test_unregister_empty_scope.js
@@ -24,15 +24,16 @@ add_task(function* test_unregister_empty
             uaid: '5619557c-86fe-4711-8078-d1fd6987aef7'
           }));
         }
       });
     }
   });
 
   yield rejects(
-    PushNotificationService.unregister(''),
+    PushNotificationService.unregister('',
+      { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
     function(error) {
       return error.error == 'NotFoundError';
     },
     'Wrong error for empty endpoint'
   );
 });
--- a/dom/push/test/xpcshell/test_unregister_error.js
+++ b/dom/push/test/xpcshell/test_unregister_error.js
@@ -15,16 +15,17 @@ function run_test() {
 
 add_task(function* test_unregister_error() {
   let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
   yield db.put({
     channelID: channelID,
     pushEndpoint: 'https://example.org/update/failure',
     scope: 'https://example.net/page/failure',
+    originAttributes: '',
     version: 1
   });
 
   let unregisterDefer = Promise.defer();
   PushService.init({
     serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db,
@@ -49,17 +50,17 @@ add_task(function* test_unregister_error
           }));
           unregisterDefer.resolve();
         }
       });
     }
   });
 
   yield PushNotificationService.unregister(
-    'https://example.net/page/failure');
+    'https://example.net/page/failure', '');
 
   let result = yield db.getByKeyID(channelID);
   ok(!result, 'Deleted push record exists');
 
   // Make sure we send a request to the server.
   yield waitForPromise(unregisterDefer.promise, DEFAULT_TIMEOUT,
     'Timed out waiting for unregister');
 });
--- a/dom/push/test/xpcshell/test_unregister_invalid_json.js
+++ b/dom/push/test/xpcshell/test_unregister_invalid_json.js
@@ -19,21 +19,23 @@ function run_test() {
 
 add_task(function* test_unregister_invalid_json() {
   let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
   let records = [{
     channelID: '87902e90-c57e-4d18-8354-013f4a556559',
     pushEndpoint: 'https://example.org/update/1',
     scope: 'https://example.edu/page/1',
+    originAttributes: '',
     version: 1
   }, {
     channelID: '057caa8f-9b99-47ff-891c-adad18ce603e',
     pushEndpoint: 'https://example.com/update/2',
     scope: 'https://example.net/page/1',
+    originAttributes: '',
     version: 1
   }];
   for (let record of records) {
     yield db.put(record);
   }
 
   let unregisterDefer = Promise.defer();
   let unregisterDone = after(2, unregisterDefer.resolve);
@@ -56,23 +58,23 @@ add_task(function* test_unregister_inval
         }
       });
     }
   });
 
   // "unregister" is fire-and-forget: it's sent via _send(), not
   // _sendRequest().
   yield PushNotificationService.unregister(
-    'https://example.edu/page/1');
+    'https://example.edu/page/1', '');
   let record = yield db.getByKeyID(
     '87902e90-c57e-4d18-8354-013f4a556559');
   ok(!record, 'Failed to delete unregistered record');
 
   yield PushNotificationService.unregister(
-    'https://example.net/page/1');
+    'https://example.net/page/1', '');
   record = yield db.getByKeyID(
     '057caa8f-9b99-47ff-891c-adad18ce603e');
   ok(!record,
     'Failed to delete unregistered record after receiving invalid JSON');
 
   yield waitForPromise(unregisterDefer.promise, DEFAULT_TIMEOUT,
     'Timed out waiting for unregister');
 });
--- a/dom/push/test/xpcshell/test_unregister_not_found.js
+++ b/dom/push/test/xpcshell/test_unregister_not_found.js
@@ -24,13 +24,14 @@ add_task(function* test_unregister_not_f
             uaid: 'f074ed80-d479-44fa-ba65-792104a79ea9'
           }));
         }
       });
     }
   });
 
   let promise = PushNotificationService.unregister(
-    'https://example.net/nonexistent');
+    'https://example.net/nonexistent',
+    { appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false });
   yield rejects(promise, function(error) {
     return error == 'NotFoundError';
   }, 'Wrong error for nonexistent scope');
 });
--- a/dom/push/test/xpcshell/test_unregister_success.js
+++ b/dom/push/test/xpcshell/test_unregister_success.js
@@ -15,16 +15,17 @@ function run_test() {
 
 add_task(function* test_unregister_success() {
   let db = PushServiceWebSocket.newPushDB();
   do_register_cleanup(() => {return db.drop().then(_ => db.close());});
   yield db.put({
     channelID,
     pushEndpoint: 'https://example.org/update/unregister-success',
     scope: 'https://example.com/page/unregister-success',
+    originAttributes: '',
     version: 1
   });
 
   let unregisterDefer = Promise.defer();
   PushService.init({
     serverURI: "wss://push.example.org/",
     networkInfo: new MockDesktopNetworkInfo(),
     db,
@@ -46,15 +47,15 @@ add_task(function* test_unregister_succe
           }));
           unregisterDefer.resolve();
         }
       });
     }
   });
 
   yield PushNotificationService.unregister(
-    'https://example.com/page/unregister-success');
+    'https://example.com/page/unregister-success', '');
   let record = yield db.getByKeyID(channelID);
   ok(!record, 'Unregister did not remove record');
 
   yield waitForPromise(unregisterDefer.promise, DEFAULT_TIMEOUT,
     'Timed out waiting for unregister');
 });
--- a/dom/push/test/xpcshell/test_unregister_success_http2.js
+++ b/dom/push/test/xpcshell/test_unregister_success_http2.js
@@ -48,26 +48,28 @@ add_task(function* test_pushUnsubscripti
   });
 
   var serverURL = "https://localhost:" + serverPort;
 
   yield db.put({
     subscriptionUri: serverURL + '/subscriptionUnsubscriptionSuccess',
     pushEndpoint: serverURL + '/pushEndpointUnsubscriptionSuccess',
     pushReceiptEndpoint: serverURL + '/receiptPushEndpointUnsubscriptionSuccess',
-    scope: 'https://example.com/page/unregister-success'
+    scope: 'https://example.com/page/unregister-success',
+    originAttributes: ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }),
   });
 
   PushService.init({
     serverURI: serverURL,
     db
   });
 
   yield PushNotificationService.unregister(
-    'https://example.com/page/unregister-success');
+    'https://example.com/page/unregister-success',
+    ChromeUtils.originAttributesToSuffix({ appId: Ci.nsIScriptSecurityManager.NO_APP_ID, inBrowser: false }));
   let record = yield db.getByKeyID(serverURL + '/subscriptionUnsubscriptionSuccess');
   ok(!record, 'Unregister did not remove record');
 
 });
 
 add_task(function* test_complete() {
   prefs.setBoolPref("network.http.spdy.enforce-tls-profile", tlsProfile);
 });
new file mode 100644
--- /dev/null
+++ b/dom/push/test/xpcshell/test_webapps_cleardata.js
@@ -0,0 +1,89 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+'use strict';
+
+const {PushDB, PushService, PushServiceWebSocket} = serviceExports;
+
+const userAgentID = 'bd744428-f125-436a-b6d0-dd0c9845837f';
+const channelIDs = ['0ef2ad4a-6c49-41ad-af6e-95d2425276bf', '4818b54a-97c5-4277-ad5d-0bfe630e4e50'];
+var channelIDCounter = 0;
+
+function run_test() {
+  do_get_profile();
+  setPrefs({
+    userAgentID,
+    requestTimeout: 1000,
+    retryBaseInterval: 150
+  });
+  disableServiceWorkerEvents(
+    'https://example.org/1'
+  );
+  run_next_test();
+}
+
+add_task(function* test_webapps_cleardata() {
+  let db = PushServiceWebSocket.newPushDB();
+  do_register_cleanup(() => {return db.drop().then(_ => db.close());});
+
+  PushService._generateID = () => channelID;
+  PushService.init({
+    serverURI: "wss://push.example.org",
+    networkInfo: new MockDesktopNetworkInfo(),
+    db,
+    makeWebSocket(uri) {
+      return new MockWebSocket(uri, {
+        onHello(data) {
+          equal(data.messageType, 'hello', 'Handshake: wrong message type');
+          equal(data.uaid, userAgentID, 'Handshake: wrong device ID');
+          this.serverSendMsg(JSON.stringify({
+            messageType: 'hello',
+            status: 200,
+            uaid: userAgentID
+          }));
+        },
+        onRegister(data) {
+          equal(data.messageType, 'register', 'Register: wrong message type');
+          this.serverSendMsg(JSON.stringify({
+            messageType: 'register',
+            status: 200,
+            channelID: data.channelID,
+            uaid: userAgentID,
+            pushEndpoint: 'https://example.com/update/' + Math.random(),
+          }));
+        }
+      });
+    }
+  });
+
+  let registers = yield Promise.all([
+    PushNotificationService.register(
+      'https://example.org/1',
+      ChromeUtils.originAttributesToSuffix({ appId: 1, inBrowser: false })),
+    PushNotificationService.register(
+      'https://example.org/1',
+      ChromeUtils.originAttributesToSuffix({ appId: 1, inBrowser: true })),
+  ]);
+
+  Services.obs.notifyObservers(
+      { appId: 1, browserOnly: false,
+        QueryInterface: XPCOMUtils.generateQI([Ci.mozIApplicationClearPrivateDataParams])},
+      "webapps-clear-data", "");
+
+  let waitAWhile = new Promise(function(res) {
+    setTimeout(res, 2000);
+  });
+  yield waitAWhile;
+
+  let registration;
+  registration = yield PushNotificationService.registration(
+    'https://example.org/1',
+    ChromeUtils.originAttributesToSuffix({ appId: 1, inBrowser: false }));
+  ok(!registration, 'Registration for { 1, false } should not exist.');
+
+  registration = yield PushNotificationService.registration(
+    'https://example.org/1',
+    ChromeUtils.originAttributesToSuffix({ appId: 1, inBrowser: true }));
+  ok(registration, 'Registration for { 1, true } should still exist.');
+});
+
--- a/dom/push/test/xpcshell/xpcshell.ini
+++ b/dom/push/test/xpcshell/xpcshell.ini
@@ -25,16 +25,17 @@ skip-if = toolkit == 'android'
 [test_registration_missing_scope.js]
 [test_registration_none.js]
 [test_registration_success.js]
 [test_unregister_empty_scope.js]
 [test_unregister_error.js]
 [test_unregister_invalid_json.js]
 [test_unregister_not_found.js]
 [test_unregister_success.js]
+[test_webapps_cleardata.js]
 #http2 test
 [test_resubscribe_4xxCode_http2.js]
 [test_resubscribe_5xxCode_http2.js]
 [test_resubscribe_listening_for_msg_error_http2.js]
 [test_register_5xxCode_http2.js]
 [test_register_success_http2.js]
 skip-if = !hasNode
 run-sequentially = node server exceptions dont replay well
--- a/dom/webidl/ChromeUtils.webidl
+++ b/dom/webidl/ChromeUtils.webidl
@@ -13,16 +13,24 @@ interface ChromeUtils : ThreadSafeChrome
   /**
    * A helper that converts OriginAttributesDictionary to cookie jar opaque
    * identfier.
    *
    * @param originAttrs       The originAttributes from the caller.
    */
   static ByteString
   originAttributesToCookieJar(optional OriginAttributesDictionary originAttrs);
+
+  /**
+   * A helper that converts OriginAttributesDictionary to a opaque suffix string.
+   *
+   * @param originAttrs       The originAttributes from the caller.
+   */
+  static ByteString
+  originAttributesToSuffix(optional OriginAttributesDictionary originAttrs);
 };
 
 /**
  * Used by principals and the script security manager to represent origin
  * attributes.
  *
  * IMPORTANT: If you add any members here, you need to update the
  * methods on mozilla::OriginAttributes, and bump the CIDs of all
--- a/dom/webidl/Location.webidl
+++ b/dom/webidl/Location.webidl
@@ -16,11 +16,10 @@ interface Location {
   [Throws, UnsafeInPrerendering]
   void assign(DOMString url);
   [Throws, CrossOriginCallable, UnsafeInPrerendering]
   void replace(DOMString url);
   // XXXbz there is no forceget argument in the spec!  See bug 1037721.
   [Throws, UnsafeInPrerendering]
   void reload(optional boolean forceget = false);
 };
-// No support for .searchParams on Location yet.  See bug 1082734.
 
 Location implements URLUtils;
--- a/dom/webidl/PushSubscription.webidl
+++ b/dom/webidl/PushSubscription.webidl
@@ -2,16 +2,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * The origin of this IDL file is
 * https://w3c.github.io/push-api/
 */
 
+interface Principal;
+
 [JSImplementation="@mozilla.org/push/PushSubscription;1",
- Constructor(DOMString pushEndpoint, DOMString scope, DOMString pageURL), ChromeOnly]
+ Constructor(DOMString pushEndpoint, DOMString scope, Principal principal), ChromeOnly]
 interface PushSubscription
 {
     readonly attribute USVString endpoint;
     Promise<boolean> unsubscribe();
     jsonifier;
 };
--- a/dom/webidl/URLUtils.webidl
+++ b/dom/webidl/URLUtils.webidl
@@ -46,10 +46,10 @@ interface URLUtils {
   // Bug 824857 should remove this.
   [Throws]
   stringifier;
 };
 
 [NoInterfaceObject,
  Exposed=(Window, Worker)]
 interface URLUtilsSearchParams {
-           attribute URLSearchParams searchParams;
+ readonly attribute URLSearchParams searchParams;
 };
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -279,17 +279,17 @@ public:
   }
 
 private:
   ~TeardownRunnable() {}
 
   nsRefPtr<ServiceWorkerManagerChild> mActor;
 };
 
-} // Anonymous namespace
+} // anonymous namespace
 
 NS_IMPL_ISUPPORTS0(ServiceWorkerJob)
 NS_IMPL_ISUPPORTS0(ServiceWorkerRegistrationInfo)
 
 void
 ServiceWorkerJob::Done(nsresult aStatus)
 {
   if (NS_WARN_IF(NS_FAILED(aStatus))) {
@@ -1582,16 +1582,84 @@ ServiceWorkerManager::AppendPendingOpera
   MOZ_ASSERT(aRunnable);
 
   if (!mShuttingDown) {
     PendingOperation* opt = mPendingOperations.AppendElement();
     opt->mRunnable = aRunnable;
   }
 }
 
+namespace {
+// Just holds a ref to a ServiceWorker until the Promise is fulfilled.
+class KeepAliveHandler final : public PromiseNativeHandler
+{
+  nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
+
+  virtual ~KeepAliveHandler()
+  {}
+
+public:
+  explicit KeepAliveHandler(const nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker)
+    : mServiceWorker(aServiceWorker)
+  {}
+
+  void
+  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+  {
+#ifdef DEBUG
+    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
+    workerPrivate->AssertIsOnWorkerThread();
+#endif
+  }
+
+  void
+  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
+  {
+#ifdef DEBUG
+    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
+    workerPrivate->AssertIsOnWorkerThread();
+#endif
+  }
+};
+
+// Returns a Promise if the event was successfully dispatched and no exceptions
+// were raised, otherwise returns null.
+already_AddRefed<Promise>
+DispatchExtendableEventOnWorkerScope(JSContext* aCx,
+                                     WorkerGlobalScope* aWorkerScope,
+                                     ExtendableEvent* aEvent)
+{
+  MOZ_ASSERT(aWorkerScope);
+  MOZ_ASSERT(aEvent);
+  nsCOMPtr<nsIGlobalObject> sgo = aWorkerScope;
+  WidgetEvent* internalEvent = aEvent->GetInternalNSEvent();
+
+  ErrorResult result;
+  result = aWorkerScope->DispatchDOMEvent(nullptr, aEvent, nullptr, nullptr);
+  if (result.Failed() || internalEvent->mFlags.mExceptionHasBeenRisen) {
+    return nullptr;
+  }
+
+  nsRefPtr<Promise> waitUntilPromise = aEvent->GetPromise();
+  if (!waitUntilPromise) {
+    ErrorResult result;
+    waitUntilPromise =
+      Promise::Resolve(sgo, aCx, JS::UndefinedHandleValue, result);
+    if (NS_WARN_IF(result.Failed())) {
+      return nullptr;
+    }
+  }
+
+  MOZ_ASSERT(waitUntilPromise);
+  return waitUntilPromise.forget();
+}
+}; // anonymous namespace
+
 /*
  * Used to handle ExtendableEvent::waitUntil() and proceed with
  * installation/activation.
  */
 class LifecycleEventPromiseHandler final : public PromiseNativeHandler
 {
   nsMainThreadPtrHandle<ContinueLifecycleTask> mTask;
   nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
@@ -1669,47 +1737,32 @@ LifecycleEventWorkerRunnable::DispatchLi
     init.mCancelable = true;
     event = ExtendableEvent::Constructor(target, mEventName, init);
   } else {
     MOZ_CRASH("Unexpected lifecycle event");
   }
 
   event->SetTrusted(true);
 
-  nsRefPtr<Promise> waitUntilPromise;
-
-  ErrorResult result;
-  result = target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
-
-  nsCOMPtr<nsIGlobalObject> sgo = aWorkerPrivate->GlobalScope();
-  WidgetEvent* internalEvent = event->GetInternalNSEvent();
-  if (!result.Failed() && !internalEvent->mFlags.mExceptionHasBeenRisen) {
-    waitUntilPromise = event->GetPromise();
-    if (!waitUntilPromise) {
-      ErrorResult result;
-      waitUntilPromise =
-        Promise::Resolve(sgo, aCx, JS::UndefinedHandleValue, result);
-      if (NS_WARN_IF(result.Failed())) {
-        return true;
-      }
-    }
+  nsRefPtr<Promise> waitUntilPromise =
+    DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(), event);
+  if (waitUntilPromise) {
+    nsRefPtr<LifecycleEventPromiseHandler> handler =
+      new LifecycleEventPromiseHandler(mTask, mServiceWorker, false /* activateImmediately */);
+    waitUntilPromise->AppendNativeHandler(handler);
   } else {
     // Continue with a canceled install.
     // Although the spec has different routines to deal with popping stuff
     // off it's internal queues, we can reuse the ContinueAfterInstallEvent()
     // logic.
     nsRefPtr<ContinueLifecycleRunnable> r =
       new ContinueLifecycleRunnable(mTask, false /* success */, false /* activate immediately */);
     MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
-    return true;
-  }
-
-  nsRefPtr<LifecycleEventPromiseHandler> handler =
-    new LifecycleEventPromiseHandler(mTask, mServiceWorker, false /* activateImmediately */);
-  waitUntilPromise->AppendNativeHandler(handler);
+  }
+
   return true;
 }
 
 void
 ServiceWorkerRegistrationInfo::TryToActivate()
 {
   if (!IsControllingDocuments() || mWaitingWorker->SkipWaitingFlag()) {
     Activate();
@@ -2086,18 +2139,23 @@ public:
       PushEvent::Constructor(globalObj, NS_LITERAL_STRING("push"), pei, result);
     if (NS_WARN_IF(result.Failed())) {
       return false;
     }
 
     event->SetTrusted(true);
     event->PostInit(mServiceWorker);
 
-    nsRefPtr<EventTarget> target = do_QueryObject(aWorkerPrivate->GlobalScope());
-    target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
+    nsRefPtr<Promise> waitUntilPromise =
+      DispatchExtendableEventOnWorkerScope(aCx, aWorkerPrivate->GlobalScope(), event);
+    if (waitUntilPromise) {
+      nsRefPtr<KeepAliveHandler> handler = new KeepAliveHandler(mServiceWorker);
+      waitUntilPromise->AppendNativeHandler(handler);
+    }
+
     return true;
   }
 };
 
 class SendPushSubscriptionChangeEventRunnable final : public WorkerRunnable
 {
   nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
 
@@ -2137,76 +2195,78 @@ public:
     globalScope->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
     return true;
   }
 };
 
 #endif /* ! MOZ_SIMPLEPUSH */
 
 NS_IMETHODIMP
-ServiceWorkerManager::SendPushEvent(JS::Handle<JS::Value> aOriginAttributes,
+ServiceWorkerManager::SendPushEvent(const nsACString& aOriginAttributes,
                                     const nsACString& aScope,
-                                    const nsAString& aData,
-                                    JSContext* aCx)
+                                    const nsAString& aData)
 {
 #ifdef MOZ_SIMPLEPUSH
   return NS_ERROR_NOT_AVAILABLE;
 #else
   OriginAttributes attrs;
-  if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
+  if (!attrs.PopulateFromSuffix(aOriginAttributes)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsRefPtr<ServiceWorker> serviceWorker =
     CreateServiceWorkerForScope(attrs, aScope);
   if (!serviceWorker) {
     return NS_ERROR_FAILURE;
   }
 
   nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
     new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker));
 
   nsRefPtr<SendPushEventRunnable> r =
     new SendPushEventRunnable(serviceWorker->GetWorkerPrivate(), aData,
                               serviceWorkerHandle);
 
-  if (NS_WARN_IF(!r->Dispatch(aCx))) {
+  AutoJSAPI jsapi;
+  jsapi.Init();
+  if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 #endif
 }
 
 NS_IMETHODIMP
-ServiceWorkerManager::SendPushSubscriptionChangeEvent(JS::Handle<JS::Value> aOriginAttributes,
-                                                      const nsACString& aScope,
-                                                      JSContext* aCx)
+ServiceWorkerManager::SendPushSubscriptionChangeEvent(const nsACString& aOriginAttributes,
+                                                      const nsACString& aScope)
 {
 #ifdef MOZ_SIMPLEPUSH
   return NS_ERROR_NOT_AVAILABLE;
 #else
   OriginAttributes attrs;
-  if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
+  if (!attrs.PopulateFromSuffix(aOriginAttributes)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsRefPtr<ServiceWorker> serviceWorker =
     CreateServiceWorkerForScope(attrs, aScope);
   if (!serviceWorker) {
     return NS_ERROR_FAILURE;
   }
   nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle(
     new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker));
 
   nsRefPtr<SendPushSubscriptionChangeEventRunnable> r =
     new SendPushSubscriptionChangeEventRunnable(
       serviceWorker->GetWorkerPrivate(), serviceWorkerHandle);
 
-  if (NS_WARN_IF(!r->Dispatch(aCx))) {
+  AutoJSAPI jsapi;
+  jsapi.Init();
+  if (NS_WARN_IF(!r->Dispatch(jsapi.cx()))) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 #endif
 }
 
 NS_IMETHODIMP
@@ -3291,17 +3351,17 @@ public:
     uint32_t loadFlags;
     rv = channel->GetLoadFlags(&loadFlags);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsILoadInfo> loadInfo;
     rv = channel->GetLoadInfo(getter_AddRefs(loadInfo));
     NS_ENSURE_SUCCESS(rv, rv);
 
-    mContentPolicyType = loadInfo->GetContentPolicyType();
+    mContentPolicyType = loadInfo->InternalContentPolicyType();
 
     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
     if (httpChannel) {
       rv = httpChannel->GetRequestMethod(mMethod);
       NS_ENSURE_SUCCESS(rv, rv);
 
       nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(httpChannel);
       NS_ENSURE_TRUE(internalChannel, NS_ERROR_NOT_AVAILABLE);
--- a/dom/workers/URL.cpp
+++ b/dom/workers/URL.cpp
@@ -834,31 +834,16 @@ URL::SetSearchInternal(const nsAString& 
 mozilla::dom::URLSearchParams*
 URL::SearchParams()
 {
   CreateSearchParamsIfNeeded();
   return mSearchParams;
 }
 
 void
-URL::SetSearchParams(URLSearchParams& aSearchParams)
-{
-  if (mSearchParams) {
-    mSearchParams->RemoveObserver(this);
-  }
-
-  mSearchParams = &aSearchParams;
-  mSearchParams->AddObserver(this);
-
-  nsAutoString search;
-  mSearchParams->Serialize(search);
-  SetSearchInternal(search);
-}
-
-void
 URL::GetHash(nsAString& aHash, ErrorResult& aRv) const
 {
   nsRefPtr<GetterRunnable> runnable =
     new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterHash, aHash,
                        mURLProxy);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
@@ -954,23 +939,22 @@ URL::URLSearchParamsUpdated(URLSearchPar
 
 void
 URL::UpdateURLSearchParams()
 {
   if (mSearchParams) {
     nsAutoString search;
     ErrorResult rv;
     GetSearch(search, rv);
-    mSearchParams->ParseInput(NS_ConvertUTF16toUTF8(Substring(search, 1)), this);
+    mSearchParams->ParseInput(NS_ConvertUTF16toUTF8(Substring(search, 1)));
   }
 }
 
 void
 URL::CreateSearchParamsIfNeeded()
 {
   if (!mSearchParams) {
-    mSearchParams = new URLSearchParams();
-    mSearchParams->AddObserver(this);
+    mSearchParams = new URLSearchParams(this);
     UpdateURLSearchParams();
   }
 }
 
 END_WORKERS_NAMESPACE
--- a/dom/workers/URL.h
+++ b/dom/workers/URL.h
@@ -103,18 +103,16 @@ public:
   void SetPathname(const nsAString& aPathname, ErrorResult& aRv);
 
   void GetSearch(nsAString& aSearch, ErrorResult& aRv) const;
 
   void SetSearch(const nsAString& aSearch, ErrorResult& aRv);
 
   URLSearchParams* SearchParams();
 
-  void SetSearchParams(URLSearchParams& aSearchParams);
-
   void GetHash(nsAString& aHost, ErrorResult& aRv) const;
 
   void SetHash(const nsAString& aHash, ErrorResult& aRv);
 
   void Stringify(nsAString& aRetval, ErrorResult& aRv) const
   {
     GetHref(aRetval, aRv);
   }
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -758,17 +758,24 @@ struct WorkerStructuredCloneCallbacks
 
     return false;
   }
 
   static void
   FreeTransfer(uint32_t aTag, JS::TransferableOwnership aOwnership,
                void *aContent, uint64_t aExtraData, void* aClosure)
   {
-    // Nothing to do.
+    if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
+      MOZ_ASSERT(aClosure);
+      MOZ_ASSERT(!aContent);
+      auto* closure = static_cast<WorkerStructuredCloneClosure*>(aClosure);
+
+      MOZ_ASSERT(aExtraData < closure->mMessagePortIdentifiers.Length());
+      dom::MessagePort::ForceClose(closure->mMessagePortIdentifiers[aExtraData]);
+    }
   }
 };
 
 const JSStructuredCloneCallbacks gWorkerStructuredCloneCallbacks = {
   WorkerStructuredCloneCallbacks::Read,
   WorkerStructuredCloneCallbacks::Write,
   WorkerStructuredCloneCallbacks::Error,
   WorkerStructuredCloneCallbacks::ReadTransfer,
--- a/dom/workers/test/urlSearchParams_worker.js
+++ b/dom/workers/test/urlSearchParams_worker.js
@@ -111,95 +111,43 @@ onmessage = function() {
     ok(url.searchParams.has('a'), "URL.searchParams.has('a')");
     is(url.searchParams.get('a'), 'b', "URL.searchParams.get('a')");
     ok(url.searchParams.has('c'), "URL.searchParams.has('c')");
     is(url.searchParams.get('c'), 'd', "URL.searchParams.get('c')");
 
     url.searchParams.set('e', 'f');
     ok(url.href.indexOf('e=f') != 1, 'URL right');
 
-    var u = new URLSearchParams();
-    u.append('foo', 'bar');
-    url.searchParams = u;
-    is(url.searchParams, u, "URL.searchParams is the same object");
-    is(url.searchParams.get('foo'), 'bar', "URL.searchParams.get('foo')");
-    is(url.href, 'http://www.example.net/?foo=bar', 'URL right');
-
-    try {
-      url.searchParams = null;
-      ok(false, "URLSearchParams is not nullable");
-    } catch(e) {
-      ok(true, "URLSearchParams is not nullable");
-    }
-
-    var url2 = new URL('http://www.example.net?e=f');
-    url.searchParams = url2.searchParams;
-    is(url.searchParams, url2.searchParams, "URL.searchParams is not the same object");
-    is(url.searchParams.get('e'), 'f', "URL.searchParams.get('e')");
-
-    url.href = "http://www.example.net?bar=foo";
-    is(url.searchParams.get('bar'), 'foo', "URL.searchParams.get('bar')");
-
     runTest();
   }
 
   function testEncoding() {
     var encoding = [ [ '1', '1' ],
                      [ 'a b', 'a+b' ],
                      [ '<>', '%3C%3E' ],
                      [ '\u0541', '%D5%81'] ];
 
     for (var i = 0; i < encoding.length; ++i) {
-      var a = new URLSearchParams();
-      a.set('a', encoding[i][0]);
-
       var url = new URL('http://www.example.net');
-      url.searchParams = a;
+      url.searchParams.set('a', encoding[i][0]);
       is(url.href, 'http://www.example.net/?a=' + encoding[i][1]);
 
       var url2 = new URL(url.href);
       is(url2.searchParams.get('a'), encoding[i][0], 'a is still there');
     }
 
     runTest();
   }
 
-  function testMultiURL() {
-    var a = new URL('http://www.example.net?a=b&c=d');
-    var b = new URL('http://www.example.net?e=f');
-    ok(a.searchParams.has('a'), "a.searchParams.has('a')");
-    ok(a.searchParams.has('c'), "a.searchParams.has('c')");
-    ok(b.searchParams.has('e'), "b.searchParams.has('e')");
-
-    var u = new URLSearchParams();
-    a.searchParams = b.searchParams = u;
-    is(a.searchParams, u, "a.searchParams === u");
-    is(b.searchParams, u, "b.searchParams === u");
-    ok(!a.searchParams.has('a'), "!a.searchParams.has('a')");
-    ok(!a.searchParams.has('c'), "!a.searchParams.has('c')");
-    ok(!b.searchParams.has('e'), "!b.searchParams.has('e')");
-
-    u.append('foo', 'bar');
-    is(a.searchParams.get('foo'), 'bar', "a has foo=bar");
-    is(b.searchParams.get('foo'), 'bar', "b has foo=bar");
-    is(a + "", b + "", "stringify a == b");
-
-    a.search = "?bar=foo";
-    is(a.searchParams.get('bar'), 'foo', "a has bar=foo");
-    is(b.searchParams.get('bar'), 'foo', "b has bar=foo");
-
-    runTest();
-  }
   var tests = [
     testSimpleURLSearchParams,
     testCopyURLSearchParams,
     testParserURLSearchParams,
     testURL,
     testEncoding,
-    testMultiURL
   ];
 
   function runTest() {
     if (!tests.length) {
       postMessage({type: 'finish' });
       return;
     }
 
--- a/dom/xbl/nsXBLMaybeCompiled.h
+++ b/dom/xbl/nsXBLMaybeCompiled.h
@@ -87,29 +87,29 @@ namespace js {
 
 template <class UncompiledT>
 struct GCMethods<nsXBLMaybeCompiled<UncompiledT> >
 {
   typedef struct GCMethods<JSObject *> Base;
 
   static nsXBLMaybeCompiled<UncompiledT> initial() { return nsXBLMaybeCompiled<UncompiledT>(); }
 
-  static void postBarrier(nsXBLMaybeCompiled<UncompiledT>* functionp,
-                          nsXBLMaybeCompiled<UncompiledT> prev,
-                          nsXBLMaybeCompiled<UncompiledT> next)
+  static bool needsPostBarrier(nsXBLMaybeCompiled<UncompiledT> function)
+  {
+    return function.IsCompiled() && Base::needsPostBarrier(function.GetJSFunction());
+  }
+
+  static void postBarrier(nsXBLMaybeCompiled<UncompiledT>* functionp)
   {
-    if (next.IsCompiled()) {
-      Base::postBarrier(&functionp->UnsafeGetJSFunction(),
-                        prev.IsCompiled() ? prev.UnsafeGetJSFunction() : nullptr,
-                        next.UnsafeGetJSFunction());
-    } else if (prev.IsCompiled()) {
-      Base::postBarrier(&prev.UnsafeGetJSFunction(),
-                        prev.UnsafeGetJSFunction(),
-                        nullptr);
-    }
+    Base::postBarrier(&functionp->UnsafeGetJSFunction());
+  }
+
+  static void relocate(nsXBLMaybeCompiled<UncompiledT>* functionp)
+  {
+    Base::relocate(&functionp->UnsafeGetJSFunction());
   }
 };
 
 template <class UncompiledT>
 class HeapBase<nsXBLMaybeCompiled<UncompiledT> >
 {
   const JS::Heap<nsXBLMaybeCompiled<UncompiledT> >& wrapper() const {
     return *static_cast<const JS::Heap<nsXBLMaybeCompiled<UncompiledT> >*>(this);
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -452,25 +452,38 @@ private:
     cairo_surface_set_device_offset(mSurface, 0, 0);
   }
 
   cairo_surface_t* mSurface;
   double mX;
   double mY;
 };
 
+static inline void
+CairoPatternAddGradientStop(cairo_pattern_t* aPattern,
+                            const GradientStop &aStop,
+                            Float aNudge = 0)
+{
+  cairo_pattern_add_color_stop_rgba(aPattern, aStop.offset + aNudge,
+                                    aStop.color.r, aStop.color.g, aStop.color.b,
+                                    aStop.color.a);
+
+}
+
 // Never returns nullptr. As such, you must always pass in Cairo-compatible
 // patterns, most notably gradients with a GradientStopCairo.
 // The pattern returned must have cairo_pattern_destroy() called on it by the
 // caller.
 // As the cairo_pattern_t returned may depend on the Pattern passed in, the
 // lifetime of the cairo_pattern_t returned must not exceed the lifetime of the
 // Pattern passed in.
 static cairo_pattern_t*
-GfxPatternToCairoPattern(const Pattern& aPattern, Float aAlpha)
+GfxPatternToCairoPattern(const Pattern& aPattern,
+                         Float aAlpha,
+                         const Matrix& aTransform)
 {
   cairo_pattern_t* pat;
   const Matrix* matrix = nullptr;
 
   switch (aPattern.GetType())
   {
     case PatternType::COLOR:
     {
@@ -507,21 +520,35 @@ GfxPatternToCairoPattern(const Pattern& 
 
       MOZ_ASSERT(pattern.mStops->GetBackendType() == BackendType::CAIRO);
       GradientStopsCairo* cairoStops = static_cast<GradientStopsCairo*>(pattern.mStops.get());
       cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(cairoStops->GetExtendMode()));
 
       matrix = &pattern.mMatrix;
 
       const std::vector<GradientStop>& stops = cairoStops->GetStops();
+      if (stops.size() >= 2 && stops.front().offset == stops.back().offset) {
+        // Certain Cairo backends that use pixman to implement gradients can have jagged
+        // edges occur with hard stops. Such hard stops are used for implementing certain
+        // types of CSS borders. Work around this by turning these hard-stops into half-pixel
+        // gradients to anti-alias them. See bug 1033375
+        Matrix patternToDevice = aTransform * pattern.mMatrix;
+        Float gradLength = (patternToDevice * pattern.mEnd - patternToDevice * pattern.mBegin).Length();
+        if (gradLength > 0) {
+          Float aaOffset = 0.25 / gradLength;
+          CairoPatternAddGradientStop(pat, stops.front(), -aaOffset);
+          for (size_t i = 1; i < stops.size()-1; ++i) {
+            CairoPatternAddGradientStop(pat, stops[i]);
+          }
+          CairoPatternAddGradientStop(pat, stops.back(), aaOffset);
+          break;
+        }
+      }
       for (size_t i = 0; i < stops.size(); ++i) {
-        const GradientStop& stop = stops[i];
-        cairo_pattern_add_color_stop_rgba(pat, stop.offset, stop.color.r,
-                                          stop.color.g, stop.color.b,
-                                          stop.color.a);
+        CairoPatternAddGradientStop(pat, stops[i]);
       }
 
       break;
     }
     case PatternType::RADIAL_GRADIENT:
     {
       const RadialGradientPattern& pattern = static_cast<const RadialGradientPattern&>(aPattern);
 
@@ -531,20 +558,17 @@ GfxPatternToCairoPattern(const Pattern& 
       MOZ_ASSERT(pattern.mStops->GetBackendType() == BackendType::CAIRO);
       GradientStopsCairo* cairoStops = static_cast<GradientStopsCairo*>(pattern.mStops.get());
       cairo_pattern_set_extend(pat, GfxExtendToCairoExtend(cairoStops->GetExtendMode()));
 
       matrix = &pattern.mMatrix;
 
       const std::vector<GradientStop>& stops = cairoStops->GetStops();
       for (size_t i = 0; i < stops.size(); ++i) {
-        const GradientStop& stop = stops[i];
-        cairo_pattern_add_color_stop_rgba(pat, stop.offset, stop.color.r,
-                                          stop.color.g, stop.color.b,
-                                          stop.color.a);
+        CairoPatternAddGradientStop(pat, stops[i]);
       }
 
       break;
     }
     default:
     {
       // We should support all pattern types!
       MOZ_ASSERT(false);
@@ -887,17 +911,17 @@ DrawTargetCairo::DrawPattern(const Patte
                              bool aPathBoundsClip)
 {
   if (!PatternIsCompatible(aPattern)) {
     return;
   }
 
   AutoClearDeviceOffset clear(aPattern);
 
-  cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha);
+  cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha, GetTransform());
   if (!pat) {
     return;
   }
   if (cairo_pattern_status(pat)) {
     cairo_pattern_destroy(pat);
     gfxWarning() << "Invalid pattern";
     return;
   }
@@ -1169,17 +1193,17 @@ DrawTargetCairo::FillGlyphs(ScaledFont *
                             const GlyphRenderingOptions*)
 {
   AutoPrepareForDrawing prep(this, mContext);
   AutoClearDeviceOffset clear(aPattern);
 
   ScaledFontBase* scaledFont = static_cast<ScaledFontBase*>(aFont);
   cairo_set_scaled_font(mContext, scaledFont->GetCairoScaledFont());
 
-  cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha);
+  cairo_pattern_t* pat = GfxPatternToCairoPattern(aPattern, aOptions.mAlpha, GetTransform());
   if (!pat)
     return;
 
   cairo_set_source(mContext, pat);
   cairo_pattern_destroy(pat);
 
   cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
 
@@ -1208,22 +1232,22 @@ DrawTargetCairo::Mask(const Pattern &aSo
                       const DrawOptions &aOptions /* = DrawOptions() */)
 {
   AutoPrepareForDrawing prep(this, mContext);
   AutoClearDeviceOffset clearSource(aSource);
   AutoClearDeviceOffset clearMask(aMask);
 
   cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
 
-  cairo_pattern_t* source = GfxPatternToCairoPattern(aSource, aOptions.mAlpha);
+  cairo_pattern_t* source = GfxPatternToCairoPattern(aSource, aOptions.mAlpha, GetTransform());
   if (!source) {
     return;
   }
 
-  cairo_pattern_t* mask = GfxPatternToCairoPattern(aMask, aOptions.mAlpha);
+  cairo_pattern_t* mask = GfxPatternToCairoPattern(aMask, aOptions.mAlpha, GetTransform());
   if (!mask) {
     cairo_pattern_destroy(source);
     return;
   }
 
   if (cairo_pattern_status(source) || cairo_pattern_status(mask)) {
     cairo_pattern_destroy(source);
     cairo_pattern_destroy(mask);
@@ -1249,17 +1273,17 @@ DrawTargetCairo::MaskSurface(const Patte
   AutoClearDeviceOffset clearMask(aMask);
 
   if (!PatternIsCompatible(aSource)) {
     return;
   }
 
   cairo_set_antialias(mContext, GfxAntialiasToCairoAntialias(aOptions.mAntialiasMode));
 
-  cairo_pattern_t* pat = GfxPatternToCairoPattern(aSource, aOptions.mAlpha);
+  cairo_pattern_t* pat = GfxPatternToCairoPattern(aSource, aOptions.mAlpha, GetTransform());
   if (!pat) {
     return;
   }
 
   if (cairo_pattern_status(pat)) {
     cairo_pattern_destroy(pat);
     gfxWarning() << "Invalid pattern";
     return;
--- a/gfx/gl/GLContextEGL.h
+++ b/gfx/gl/GLContextEGL.h
@@ -5,20 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GLCONTEXTEGL_H_
 #define GLCONTEXTEGL_H_
 
 #include "GLContext.h"
 #include "GLLibraryEGL.h"
 
-#ifdef MOZ_WIDGET_GONK
-#include "HwcComposer2D.h"
-#endif
-
 class nsIWidget;
 
 namespace mozilla {
 namespace gl {
 
 class GLContextEGL : public GLContext
 {
     friend class TextureImageEGL;
@@ -123,19 +119,16 @@ protected:
     EGLContext mContext;
     nsRefPtr<gfxASurface> mThebesSurface;
     bool mBound;
 
     bool mIsPBuffer;
     bool mIsDoubleBuffered;
     bool mCanBindToTexture;
     bool mShareWithEGLImage;
-#ifdef MOZ_WIDGET_GONK
-    nsRefPtr<HwcComposer2D> mHwc;
-#endif
     bool mOwnsContext;
 
     static EGLSurface CreatePBufferSurfaceTryingPowerOfTwo(EGLConfig config,
                                                            EGLenum bindToTextureFormat,
                                                            gfx::IntSize& pbsize);
 };
 
 }
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -12,17 +12,16 @@
 #ifdef MOZ_WIDGET_GTK
 #include <gdk/gdkx.h>
 // we're using default display for now
 #define GET_NATIVE_WINDOW(aWidget) (EGLNativeWindowType)GDK_WINDOW_XID((GdkWindow *) aWidget->GetNativeData(NS_NATIVE_WINDOW))
 #elif defined(MOZ_WIDGET_QT)
 #define GET_NATIVE_WINDOW(aWidget) (EGLNativeWindowType)(aWidget->GetNativeData(NS_NATIVE_SHAREABLE_WINDOW))
 #elif defined(MOZ_WIDGET_GONK)
 #define GET_NATIVE_WINDOW(aWidget) ((EGLNativeWindowType)aWidget->GetNativeData(NS_NATIVE_WINDOW))
-#include "HwcComposer2D.h"
 #include "libdisplay/GonkDisplay.h"
 #include "nsWindow.h"
 #include "nsScreenManagerGonk.h"
 #endif
 
 #if defined(ANDROID)
 /* from widget */
 #if defined(MOZ_WIDGET_ANDROID)
--- a/gfx/layers/LayersLogging.cpp
+++ b/gfx/layers/LayersLogging.cpp
@@ -178,16 +178,19 @@ AppendToString(std::stringstream& aStrea
   AppendToString(aStream, m.GetDisplayPort(), "] [dp=");
   AppendToString(aStream, m.GetCriticalDisplayPort(), "] [cdp=");
   AppendToString(aStream, m.GetBackgroundColor(), "] [color=");
   if (!detailed) {
     AppendToString(aStream, m.GetScrollId(), "] [scrollId=");
     if (m.GetScrollParentId() != FrameMetrics::NULL_SCROLL_ID) {
       AppendToString(aStream, m.GetScrollParentId(), "] [scrollParent=");
     }
+    if (m.IsRootContent()) {
+      aStream << "] [rcd";
+    }
     if (m.HasClipRect()) {
       AppendToString(aStream, m.ClipRect(), "] [clip=");
     }
     AppendToString(aStream, m.GetZoom(), "] [z=", "] }");
   } else {
     AppendToString(aStream, m.GetDisplayPortMargins(), " [dpm=");
     aStream << nsPrintfCString("] um=%d", m.GetUseDisplayPortMargins()).get();
     AppendToString(aStream, m.GetRootCompositionSize(), "] [rcs=");
@@ -197,18 +200,18 @@ AppendToString(std::stringstream& aStrea
             m.GetPresShellResolution()).get();
     AppendToString(aStream, m.GetCumulativeResolution(), " cr=");
     AppendToString(aStream, m.GetZoom(), " z=");
     AppendToString(aStream, m.GetExtraResolution(), " er=");
     aStream << nsPrintfCString(")] [u=(%d %d %lu)",
             m.GetScrollOffsetUpdated(), m.GetDoSmoothScroll(),
             m.GetScrollGeneration()).get();
     AppendToString(aStream, m.GetScrollParentId(), "] [p=");
-    aStream << nsPrintfCString("] [i=(%ld %lld)] }",
-            m.GetPresShellId(), m.GetScrollId()).get();
+    aStream << nsPrintfCString("] [i=(%ld %lld %d)] }",
+            m.GetPresShellId(), m.GetScrollId(), m.IsRootContent()).get();
   }
   aStream << sfx;
 }
 
 void
 AppendToString(std::stringstream& aStream, const ScrollableLayerGuid& s,
                const char* pfx, const char* sfx)
 {
--- a/image/DecodePool.cpp
+++ b/image/DecodePool.cpp
@@ -212,21 +212,16 @@ public:
   }
 
   /// Pops a new work item, blocking if necessary.
   Work PopWork()
   {
     MonitorAutoLock lock(mMonitor);
 
     do {
-      // XXX(seth): The queue popping code below is NOT efficient, obviously,
-      // since we're removing an element from the front of the array. However,
-      // it's not worth implementing something better right now, because we are
-      // replacing this FIFO behavior with LIFO behavior very soon.
-
       // Prioritize size decodes over full decodes.
       if (!mSizeDecodeQueue.IsEmpty()) {
         return PopWorkFromQueue(mSizeDecodeQueue);
       }
 
       if (!mFullDecodeQueue.IsEmpty()) {
         return PopWorkFromQueue(mFullDecodeQueue);
       }
@@ -244,18 +239,18 @@ public:
 
 private:
   ~DecodePoolImpl() { }
 
   Work PopWorkFromQueue(nsTArray<nsRefPtr<Decoder>>& aQueue)
   {
     Work work;
     work.mType = Work::Type::DECODE;
-    work.mDecoder = aQueue.ElementAt(0);
-    aQueue.RemoveElementAt(0);
+    work.mDecoder = aQueue.LastElement();
+    aQueue.RemoveElementAt(aQueue.Length() - 1);
 
     return work;
   }
 
   nsThreadPoolNaming mThreadNaming;
 
   // mMonitor guards mQueue and mShuttingDown.
   Monitor mMonitor;
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -583,16 +583,27 @@ BackgroundParentImpl::DeallocPMessagePor
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aActor);
 
   delete static_cast<MessagePortParent*>(aActor);
   return true;
 }
 
+bool
+BackgroundParentImpl::RecvMessagePortForceClose(const nsID& aUUID,
+                                                const nsID& aDestinationUUID,
+                                                const uint32_t& aSequenceID)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  return MessagePortParent::ForceClose(aUUID, aDestinationUUID, aSequenceID);
+}
+
 } // namespace ipc
 } // namespace mozilla
 
 void
 TestParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   AssertIsInMainProcess();
   AssertIsOnBackgroundThread();
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -132,14 +132,19 @@ protected:
   virtual bool
   RecvPMessagePortConstructor(PMessagePortParent* aActor,
                               const nsID& aUUID,
                               const nsID& aDestinationUUID,
                               const uint32_t& aSequenceID) override;
 
   virtual bool
   DeallocPMessagePortParent(PMessagePortParent* aActor) override;
+
+  virtual bool
+  RecvMessagePortForceClose(const nsID& aUUID,
+                            const nsID& aDestinationUUID,
+                            const uint32_t& aSequenceID) override;
 };
 
 } // namespace ipc
 } // namespace mozilla
 
 #endif // mozilla_ipc_backgroundparentimpl_h__
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -55,16 +55,18 @@ parent:
   PServiceWorkerManager();
 
   ShutdownServiceWorkerRegistrar();
 
   PCacheStorage(Namespace aNamespace, PrincipalInfo aPrincipalInfo);
 
   PMessagePort(nsID uuid, nsID destinationUuid, uint32_t sequenceId);
 
+  MessagePortForceClose(nsID uuid, nsID destinationUuid, uint32_t sequenceId);
+
 child:
   PCache();
   PCacheStreamControl();
 
 both:
   PBlob(BlobConstructorParams params);
 
   PFileDescriptorSet(FileDescriptor fd);
--- a/js/ipc/WrapperAnswer.cpp
+++ b/js/ipc/WrapperAnswer.cpp
@@ -184,17 +184,17 @@ WrapperAnswer::RecvDefineProperty(const 
     if (!fromJSIDVariant(cx, idVar, &id))
         return fail(jsapi, rs);
 
     Rooted<JSPropertyDescriptor> desc(cx);
     if (!toDescriptor(cx, descriptor, &desc))
         return fail(jsapi, rs);
 
     ObjectOpResult success;
-    if (!js::DefineOwnProperty(cx, obj, id, desc, success))
+    if (!JS_DefinePropertyById(cx, obj, id, desc, success))
         return fail(jsapi, rs);
     return ok(rs, success);
 }
 
 bool
 WrapperAnswer::RecvDelete(const ObjectId& objId, const JSIDVariant& idVar, ReturnStatus* rs)
 {
     AutoJSAPI jsapi;
--- a/js/public/Id.h
+++ b/js/public/Id.h
@@ -166,17 +166,19 @@ extern JS_PUBLIC_DATA(const jsid) JSID_E
 extern JS_PUBLIC_DATA(const JS::HandleId) JSID_VOIDHANDLE;
 extern JS_PUBLIC_DATA(const JS::HandleId) JSID_EMPTYHANDLE;
 
 namespace js {
 
 template <> struct GCMethods<jsid>
 {
     static jsid initial() { return JSID_VOID; }
-    static void postBarrier(jsid* idp, jsid prev, jsid next) {}
+    static bool needsPostBarrier(jsid id) { return false; }
+    static void postBarrier(jsid* idp) {}
+    static void relocate(jsid* idp) {}
 };
 
 // If the jsid is a GC pointer type, convert to that type and call |f| with
 // the pointer. If the jsid is not a GC type, calls F::defaultValue.
 template <typename F, typename... Args>
 auto
 DispatchIdTyped(F f, jsid& id, Args&&... args)
   -> decltype(f(static_cast<JSString*>(nullptr), mozilla::Forward<Args>(args)...))
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -169,17 +169,18 @@ struct PersistentRootedMarker;
 namespace JS {
 
 template <typename T> class Rooted;
 template <typename T> class PersistentRooted;
 
 /* This is exposing internal state of the GC for inlining purposes. */
 JS_FRIEND_API(bool) isGCEnabled();
 
-JS_FRIEND_API(void) HeapObjectPostBarrier(JSObject** objp, JSObject* prev, JSObject* next);
+JS_FRIEND_API(void) HeapObjectPostBarrier(JSObject** objp);
+JS_FRIEND_API(void) HeapObjectRelocate(JSObject** objp);
 
 #ifdef JS_DEBUG
 /*
  * For generational GC, assert that an object is in the tenured generation as
  * opposed to being in the nursery.
  */
 extern JS_FRIEND_API(void)
 AssertGCThingMustBeTenured(JSObject* obj);
@@ -226,17 +227,18 @@ class Heap : public js::HeapBase<T>
      * For Heap, move semantics are equivalent to copy semantics. In C++, a
      * copy constructor taking const-ref is the way to get a single function
      * that will be used for both lvalue and rvalue copies, so we can simply
      * omit the rvalue variant.
      */
     explicit Heap(const Heap<T>& p) { init(p.ptr); }
 
     ~Heap() {
-        post(ptr, js::GCMethods<T>::initial());
+        if (js::GCMethods<T>::needsPostBarrier(ptr))
+            relocate();
     }
 
     DECLARE_POINTER_CONSTREF_OPS(T);
     DECLARE_POINTER_ASSIGN_OPS(Heap, T);
     DECLARE_NONPOINTER_ACCESSOR_METHODS(ptr);
 
     T* unsafeGet() { return &ptr; }
 
@@ -250,27 +252,39 @@ class Heap : public js::HeapBase<T>
 
     bool isSetToCrashOnTouch() {
         return ptr == crashOnTouchPointer;
     }
 
   private:
     void init(T newPtr) {
         ptr = newPtr;
-        post(js::GCMethods<T>::initial(), ptr);
+        if (js::GCMethods<T>::needsPostBarrier(ptr))
+            post();
     }
 
     void set(T newPtr) {
-        T tmp = ptr;
-        ptr = newPtr;
-        post(tmp, ptr);
+        if (js::GCMethods<T>::needsPostBarrier(newPtr)) {
+            ptr = newPtr;
+            post();
+        } else if (js::GCMethods<T>::needsPostBarrier(ptr)) {
+            relocate();  /* Called before overwriting ptr. */
+            ptr = newPtr;
+        } else {
+            ptr = newPtr;
+        }
     }
 
-    void post(const T& prev, const T& next) {
-        js::GCMethods<T>::postBarrier(&ptr, prev, next);
+    void post() {
+        MOZ_ASSERT(js::GCMethods<T>::needsPostBarrier(ptr));
+        js::GCMethods<T>::postBarrier(&ptr);
+    }
+
+    void relocate() {
+        js::GCMethods<T>::relocate(&ptr);
     }
 
     enum {
         crashOnTouchPointer = 1
     };
 
     T ptr;
 };
@@ -585,46 +599,57 @@ struct RootKind<T*>
 {
     static ThingRootKind rootKind() { return T::rootKind(); }
 };
 
 template <typename T>
 struct GCMethods<T*>
 {
     static T* initial() { return nullptr; }
-    static void postBarrier(T** vp, T* prev, T* next) {
-        if (next)
-            JS::AssertGCThingIsNotAnObjectSubclass(reinterpret_cast<js::gc::Cell*>(next));
+    static bool needsPostBarrier(T* v) { return false; }
+    static void postBarrier(T** vp) {
+        if (vp)
+            JS::AssertGCThingIsNotAnObjectSubclass(reinterpret_cast<js::gc::Cell*>(vp));
     }
     static void relocate(T** vp) {}
 };
 
 template <>
 struct GCMethods<JSObject*>
 {
     static JSObject* initial() { return nullptr; }
     static gc::Cell* asGCThingOrNull(JSObject* v) {
         if (!v)
             return nullptr;
         MOZ_ASSERT(uintptr_t(v) > 32);
         return reinterpret_cast<gc::Cell*>(v);
     }
-    static void postBarrier(JSObject** vp, JSObject* prev, JSObject* next) {
-        JS::HeapObjectPostBarrier(vp, prev, next);
+    static bool needsPostBarrier(JSObject* v) {
+        return v != nullptr && gc::IsInsideNursery(reinterpret_cast<gc::Cell*>(v));
+    }
+    static void postBarrier(JSObject** vp) {
+        JS::HeapObjectPostBarrier(vp);
+    }
+    static void relocate(JSObject** vp) {
+        JS::HeapObjectRelocate(vp);
     }
 };
 
 template <>
 struct GCMethods<JSFunction*>
 {
     static JSFunction* initial() { return nullptr; }
-    static void postBarrier(JSFunction** vp, JSFunction* prev, JSFunction* next) {
-        JS::HeapObjectPostBarrier(reinterpret_cast<JSObject**>(vp),
-                                  reinterpret_cast<JSObject*>(prev),
-                                  reinterpret_cast<JSObject*>(next));
+    static bool needsPostBarrier(JSFunction* v) {
+        return v != nullptr && gc::IsInsideNursery(reinterpret_cast<gc::Cell*>(v));
+    }
+    static void postBarrier(JSFunction** vp) {
+        JS::HeapObjectPostBarrier(reinterpret_cast<JSObject**>(vp));
+    }
+    static void relocate(JSFunction** vp) {
+        JS::HeapObjectRelocate(reinterpret_cast<JSObject**>(vp));
     }
 };
 
 } /* namespace js */
 
 namespace JS {
 
 /*
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -1624,35 +1624,38 @@ SameType(const Value& lhs, const Value& 
     return JSVAL_SAME_TYPE_IMPL(lhs.data, rhs.data);
 }
 
 } // namespace JS
 
 /************************************************************************/
 
 namespace JS {
-JS_PUBLIC_API(void) HeapValuePostBarrier(Value* valuep, const Value& prev, const Value& next);
+JS_PUBLIC_API(void) HeapValuePostBarrier(Value* valuep);
+JS_PUBLIC_API(void) HeapValueRelocate(Value* valuep);
 }
 
 namespace js {
 
 template <> struct GCMethods<const JS::Value>
 {
     static JS::Value initial() { return JS::UndefinedValue(); }
 };
 
 template <> struct GCMethods<JS::Value>
 {
     static JS::Value initial() { return JS::UndefinedValue(); }
     static gc::Cell* asGCThingOrNull(const JS::Value& v) {
         return v.isMarkable() ? v.toGCThing() : nullptr;
     }
-    static void postBarrier(JS::Value* v, const JS::Value& prev, const JS::Value& next) {
-        JS::HeapValuePostBarrier(v, prev, next);
+    static bool needsPostBarrier(const JS::Value& v) {
+        return v.isObject() && gc::IsInsideNursery(reinterpret_cast<gc::Cell*>(&v.toObject()));
     }
+    static void postBarrier(JS::Value* v) { JS::HeapValuePostBarrier(v); }
+    static void relocate(JS::Value* v) { JS::HeapValueRelocate(v); }
 };
 
 template <class Outer> class MutableValueOperations;
 
 /*
  * A class designed for CRTP use in implementing the non-mutating parts of the
  * Value interface in Value-like classes.  Outer must be a class inheriting
  * ValueOperations<Outer> with a visible extract() method returning the
--- a/js/src/asmjs/AsmJSModule.h
+++ b/js/src/asmjs/AsmJSModule.h
@@ -846,17 +846,17 @@ class AsmJSModule
     ScriptSource *                        scriptSource_;
     PropertyName *                        globalArgumentName_;
     PropertyName *                        importArgumentName_;
     PropertyName *                        bufferArgumentName_;
     uint8_t *                             code_;
     uint8_t *                             interruptExit_;
     uint8_t *                             outOfBoundsExit_;
     StaticLinkData                        staticLinkData_;
-    HeapPtrArrayBufferObjectMaybeShared   maybeHeap_;
+    RelocatablePtrArrayBufferObjectMaybeShared maybeHeap_;
     AsmJSModule **                        prevLinked_;
     AsmJSModule *                         nextLinked_;
     bool                                  dynamicallyLinked_;
     bool                                  loadedFromCache_;
     bool                                  profilingEnabled_;
     bool                                  interrupted_;
 
     void restoreHeapToInitialState(ArrayBufferObjectMaybeShared* maybePrevBuffer);
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -1194,17 +1194,17 @@ MapObject::set(JSContext* cx, HandleObje
     ValueMap* map = obj->as<MapObject>().getData();
     if (!map)
         return false;
 
     AutoHashableValueRooter key(cx);
     if (!key.setValue(cx, k))
         return false;
 
-    HeapValue rval(v);
+    RelocatableValue rval(v);
     if (!map->put(key, rval)) {
         ReportOutOfMemory(cx);
         return false;
     }
     WriteBarrierPost(cx->runtime(), map, key.get());
     return true;
 }
 
@@ -1289,17 +1289,17 @@ MapObject::construct(JSContext* cx, unsi
             RootedValue val(cx);
             if (!GetElement(cx, pairObj, pairObj, 1, &val))
                 return false;
 
             if (isOriginalAdder) {
                 if (!hkey.setValue(cx, key))
                     return false;
 
-                HeapValue rval(val);
+                RelocatableValue rval(val);
                 if (!map->put(hkey, rval)) {
                     ReportOutOfMemory(cx);
                     return false;
                 }
                 WriteBarrierPost(cx->runtime(), map, key);
             } else {
                 if (!args2.init(2))
                     return false;
@@ -1446,17 +1446,17 @@ MapObject::has(JSContext* cx, unsigned a
 
 bool
 MapObject::set_impl(JSContext* cx, CallArgs args)
 {
     MOZ_ASSERT(MapObject::is(args.thisv()));
 
     ValueMap& map = extract(args);
     ARG0_KEY(cx, args, key);
-    HeapValue rval(args.get(1));
+    RelocatableValue rval(args.get(1));
     if (!map.put(key, rval)) {
         ReportOutOfMemory(cx);
         return false;
     }
     WriteBarrierPost(cx->runtime(), &map, key.get());
     args.rval().set(args.thisv());
     return true;
 }
@@ -1467,24 +1467,24 @@ MapObject::set(JSContext* cx, unsigned a
     CallArgs args = CallArgsFromVp(argc, vp);
     return CallNonGenericMethod<MapObject::is, MapObject::set_impl>(cx, args);
 }
 
 bool
 MapObject::delete_impl(JSContext* cx, CallArgs args)
 {
     // MapObject::mark does not mark deleted entries. Incremental GC therefore
-    // requires that no HeapValue objects pointing to heap values be left alive
-    // in the ValueMap.
+    // requires that no RelocatableValue objects pointing to heap values be
+    // left alive in the ValueMap.
     //
     // OrderedHashMap::remove() doesn't destroy the removed entry. It merely
     // calls OrderedHashMap::MapOps::makeEmpty. But that is sufficient, because
     // makeEmpty clears the value by doing e->value = Value(), and in the case
-    // of a ValueMap, Value() means HeapValue(), which is the same as
-    // HeapValue(UndefinedValue()).
+    // of a ValueMap, Value() means RelocatableValue(), which is the same as
+    // RelocatableValue(UndefinedValue()).
     MOZ_ASSERT(MapObject::is(args.thisv()));
 
     ValueMap& map = extract(args);
     ARG0_KEY(cx, args, key);
     bool found;
     if (!map.remove(key, &found)) {
         ReportOutOfMemory(cx);
         return false;
--- a/js/src/builtin/MapObject.h
+++ b/js/src/builtin/MapObject.h
@@ -72,17 +72,17 @@ class AutoHashableValueRooter : private 
 
 template <class Key, class Value, class OrderedHashPolicy, class AllocPolicy>
 class OrderedHashMap;
 
 template <class T, class OrderedHashPolicy, class AllocPolicy>
 class OrderedHashSet;
 
 typedef OrderedHashMap<HashableValue,
-                       HeapValue,
+                       RelocatableValue,
                        HashableValue::Hasher,
                        RuntimeAllocPolicy> ValueMap;
 
 typedef OrderedHashSet<HashableValue,
                        HashableValue::Hasher,
                        RuntimeAllocPolicy> ValueSet;
 
 class MapObject : public NativeObject {
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -827,17 +827,17 @@ js::obj_defineProperty(JSContext* cx, un
         return false;
 
     // Steps 4-5.
     Rooted<PropertyDescriptor> desc(cx);
     if (!ToPropertyDescriptor(cx, args.get(2), true, &desc))
         return false;
 
     // Steps 6-8.
-    if (!StandardDefineProperty(cx, obj, id, desc))
+    if (!DefineProperty(cx, obj, id, desc))
         return false;
     args.rval().setObject(*obj);
     return true;
 }
 
 /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
 static bool
 obj_defineProperties(JSContext* cx, unsigned argc, Value* vp)
--- a/js/src/gc/Barrier.cpp
+++ b/js/src/gc/Barrier.cpp
@@ -85,20 +85,36 @@ js::PreBarrierFunctor<S>::operator()(T* 
 }
 template void js::PreBarrierFunctor<JS::Value>::operator()<JS::Symbol>(JS::Symbol*);
 template void js::PreBarrierFunctor<JS::Value>::operator()<JSObject>(JSObject*);
 template void js::PreBarrierFunctor<JS::Value>::operator()<JSString>(JSString*);
 template void js::PreBarrierFunctor<jsid>::operator()<JS::Symbol>(JS::Symbol*);
 template void js::PreBarrierFunctor<jsid>::operator()<JSString>(JSString*);
 
 JS_PUBLIC_API(void)
-JS::HeapObjectPostBarrier(JSObject** objp, JSObject* prev, JSObject* next)
+JS::HeapObjectPostBarrier(JSObject** objp)
 {
     MOZ_ASSERT(objp);
-    js::InternalGCMethods<JSObject*>::postBarrier(objp, prev, next);
+    MOZ_ASSERT(*objp);
+    js::InternalGCMethods<JSObject*>::postBarrierRelocate(objp);
 }
 
 JS_PUBLIC_API(void)
-JS::HeapValuePostBarrier(JS::Value* valuep, const Value& prev, const Value& next)
+JS::HeapObjectRelocate(JSObject** objp)
+{
+    MOZ_ASSERT(objp);
+    MOZ_ASSERT(*objp);
+    js::InternalGCMethods<JSObject*>::postBarrierRemove(objp);
+}
+
+JS_PUBLIC_API(void)
+JS::HeapValuePostBarrier(JS::Value* valuep)
 {
     MOZ_ASSERT(valuep);
-    js::InternalGCMethods<JS::Value>::postBarrier(valuep, prev, next);
+    js::InternalGCMethods<JS::Value>::postBarrierRelocate(valuep);
 }
+
+JS_PUBLIC_API(void)
+JS::HeapValueRelocate(JS::Value* valuep)
+{
+    MOZ_ASSERT(valuep);
+    js::InternalGCMethods<JS::Value>::postBarrierRemove(valuep);
+}
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -128,36 +128,38 @@
  * and jsid, respectively.
  *
  * One additional note: not all object writes need to be pre-barriered. Writes
  * to newly allocated objects do not need a pre-barrier. In these cases, we use
  * the "obj->field.init(value)" method instead of "obj->field = value". We use
  * the init naming idiom in many places to signify that a field is being
  * assigned for the first time.
  *
- * This file implements three classes, illustrated here:
+ * This file implements four classes, illustrated here:
  *
- * BarrieredBase         abstract base class which provides common operations
- *    |  |
- *    | PreBarriered     provides pre-barriers only
- *    |
- *   HeapPtr             provides pre- and post-barriers
+ * BarrieredBase          abstract base class which provides common operations
+ *  |  |  |
+ *  |  | PreBarriered     provides pre-barriers only
+ *  |  |
+ *  | HeapPtr             provides pre- and post-barriers
+ *  |
+ * RelocatablePtr         provides pre- and post-barriers and is relocatable
  *
  * The implementation of the barrier logic is implemented on T::writeBarrier.*,
  * via:
  *
  * BarrieredBase<T>::pre
  *  -> InternalGCMethods<T*>::preBarrier
  *      -> T::writeBarrierPre
  *  -> InternalGCMethods<Value>::preBarrier
  *  -> InternalGCMethods<jsid>::preBarrier
  *      -> InternalGCMethods<T*>::preBarrier
  *          -> T::writeBarrierPre
  *
- * HeapPtr<T>::post
+ * HeapPtr<T>::post and RelocatablePtr<T>::post
  *  -> InternalGCMethods<T*>::postBarrier
  *      -> T::writeBarrierPost
  *  -> InternalGCMethods<Value>::postBarrier
  *      -> StoreBuffer::put
  *
  * These classes are designed to be used by the internals of the JS engine.
  * Barriers designed to be used externally are provided in js/RootingAPI.h.
  * These external barriers call into the same post-barrier implementations at
@@ -232,19 +234,19 @@ struct InternalGCMethods {};
 
 template <typename T>
 struct InternalGCMethods<T*>
 {
     static bool isMarkable(T* v) { return v != nullptr; }
 
     static void preBarrier(T* v) { T::writeBarrierPre(v); }
 
-    static void postBarrier(T** vp, T* prior, T* next) {
-        return T::writeBarrierPost(vp, prior, next);
-    }
+    static void postBarrier(T** vp) { T::writeBarrierPost(*vp, vp); }
+    static void postBarrierRelocate(T** vp) { T::writeBarrierPostRelocate(*vp, vp); }
+    static void postBarrierRemove(T** vp) { T::writeBarrierPostRemove(*vp, vp); }
 
     static void readBarrier(T* v) { T::readBarrier(v); }
 };
 
 template <typename S> struct PreBarrierFunctor : VoidDefaultAdaptor<S> {
     template <typename T> void operator()(T* t);
 };
 
@@ -256,49 +258,57 @@ template <>
 struct InternalGCMethods<Value>
 {
     static bool isMarkable(Value v) { return v.isMarkable(); }
 
     static void preBarrier(Value v) {
         DispatchValueTyped(PreBarrierFunctor<Value>(), v);
     }
 
-    static void postBarrier(Value* vp, const Value& prev, const Value& next) {
+    static void postBarrier(Value* vp) {
         MOZ_ASSERT(!CurrentThreadIsIonCompiling());
-        MOZ_ASSERT(vp);
+        if (vp->isObject()) {
+            gc::StoreBuffer* sb = reinterpret_cast<gc::Cell*>(&vp->toObject())->storeBuffer();
+            if (sb)
+                sb->putValueFromAnyThread(vp);
+        }
+    }
 
-        // If the target needs an entry, add it.
-        js::gc::StoreBuffer* sb;
-        if (next.isObject() && (sb = reinterpret_cast<gc::Cell*>(&next.toObject())->storeBuffer())) {
-            // If we know that the prev has already inserted an entry, we can skip
-            // doing the lookup to add the new entry.
-            if (prev.isObject() && reinterpret_cast<gc::Cell*>(&prev.toObject())->storeBuffer()) {
-                sb->assertHasValueEdge(vp);
-                return;
-            }
-            sb->putValueFromAnyThread(vp);
-            return;
+    static void postBarrierRelocate(Value* vp) {
+        MOZ_ASSERT(!CurrentThreadIsIonCompiling());
+        if (vp->isObject()) {
+            gc::StoreBuffer* sb = reinterpret_cast<gc::Cell*>(&vp->toObject())->storeBuffer();
+            if (sb)
+                sb->putRelocatableValueFromAnyThread(vp);
         }
-        // Remove the prev entry if the new value does not need it.
-        if (prev.isObject() && (sb = reinterpret_cast<gc::Cell*>(&prev.toObject())->storeBuffer()))
-            sb->unputValueFromAnyThread(vp);
+    }
+
+    static void postBarrierRemove(Value* vp) {
+        MOZ_ASSERT(vp);
+        MOZ_ASSERT(vp->isMarkable());
+        MOZ_ASSERT(!CurrentThreadIsIonCompiling());
+        JSRuntime* rt = static_cast<js::gc::Cell*>(vp->toGCThing())->runtimeFromAnyThread();
+        JS::shadow::Runtime* shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt);
+        shadowRuntime->gcStoreBufferPtr()->removeRelocatableValueFromAnyThread(vp);
     }
 
     static void readBarrier(const Value& v) {
         DispatchValueTyped(ReadBarrierFunctor<Value>(), v);
     }
 };
 
 template <>
 struct InternalGCMethods<jsid>
 {
     static bool isMarkable(jsid id) { return JSID_IS_STRING(id) || JSID_IS_SYMBOL(id); }
 
     static void preBarrier(jsid id) { DispatchIdTyped(PreBarrierFunctor<jsid>(), id); }
-    static void postBarrier(jsid* idp, jsid prev, jsid next) {}
+    static void postBarrier(jsid* idp) {}
+    static void postBarrierRelocate(jsid* idp) {}
+    static void postBarrierRemove(jsid* idp) {}
 };
 
 template <typename T>
 class BarrieredBaseMixins {};
 
 /*
  * Base class for barriered pointer types.
  */
@@ -326,35 +336,36 @@ class BarrieredBase : public BarrieredBa
      * Obviously this is dangerous unless you know the barrier is not needed.
      */
     T* unsafeGet() { return &value; }
     const T* unsafeGet() const { return &value; }
     void unsafeSet(T v) { value = v; }
 
     /* For users who need to manually barrier the raw types. */
     static void writeBarrierPre(const T& v) { InternalGCMethods<T>::preBarrier(v); }
+    static void writeBarrierPost(const T& v, T* vp) { InternalGCMethods<T>::postBarrier(vp); }
 
   protected:
     void pre() { InternalGCMethods<T>::preBarrier(value); }
 };
 
 template <>
 class BarrieredBaseMixins<JS::Value> : public ValueOperations<BarrieredBase<JS::Value> >
 {
     friend class ValueOperations<BarrieredBase<JS::Value> >;
     const JS::Value * extract() const {
         return static_cast<const BarrieredBase<JS::Value>*>(this)->unsafeGet();
     }
 };
 
 /*
- * PreBarriered only automatically handles pre-barriers. Post-barriers must be
- * manually implemented when using this class. HeapPtr should be used in all
- * cases that do not require explicit low-level control of moving behavior,
- * e.g. for HashMap keys.
+ * PreBarriered only automatically handles pre-barriers. Post-barriers must
+ * be manually implemented when using this class. HeapPtr and RelocatablePtr
+ * should be used in all cases that do not require explicit low-level control
+ * of moving behavior, e.g. for HashMap keys.
  */
 template <class T>
 class PreBarriered : public BarrieredBase<T>
 {
   public:
     PreBarriered() : BarrieredBase<T>(GCMethods<T>::initial()) {}
     /*
      * Allow implicit construction for use in generic contexts, such as DebuggerWeakMap::markKeys.
@@ -377,90 +388,185 @@ class PreBarriered : public BarrieredBas
         this->pre();
         this->value = v;
     }
 };
 
 /*
  * A pre- and post-barriered heap pointer, for use inside the JS engine.
  *
- * Use of this class makes a heap-stored pointer safe for both incremental and
- * generational GC. Note, however, that this class does *not* create a new
- * root: HeapPtr references must still be traced like any other pointer in the
- * system.
+ * It must only be stored in memory that has GC lifetime. HeapPtr must not be
+ * used in contexts where it may be implicitly moved or deleted, e.g. most
+ * containers.
  *
- * This class should not to be confused with JS::Heap<T>. This is a different
- * class from the external interface and implements substantially different
- * semantics. Specifically, it implements pre-barriers, which the external
- * interface does not.
+ * Not to be confused with JS::Heap<T>. This is a different class from the
+ * external interface and implements substantially different semantics.
+ *
+ * The post-barriers implemented by this class are faster than those
+ * implemented by RelocatablePtr<T> or JS::Heap<T> at the cost of not
+ * automatically handling deletion or movement.
  */
 template <class T>
 class HeapPtr : public BarrieredBase<T>
 {
   public:
     HeapPtr() : BarrieredBase<T>(GCMethods<T>::initial()) {}
-    explicit HeapPtr(T v) : BarrieredBase<T>(v) {
-        post(GCMethods<T>::initial(), this->value);
+    explicit HeapPtr(T v) : BarrieredBase<T>(v) { post(); }
+    explicit HeapPtr(const HeapPtr<T>& v) : BarrieredBase<T>(v) { post(); }
+#ifdef DEBUG
+    ~HeapPtr() {
+        // No prebarrier necessary as this only happens when we are sweeping or
+        // before the containing obect becomes part of the GC graph.
+        MOZ_ASSERT(CurrentThreadIsGCSweeping() || CurrentThreadIsHandlingInitFailure());
+    }
+#endif
+
+    void init(T v) {
+        this->value = v;
+        post();
+    }
+
+    DECLARE_POINTER_ASSIGN_OPS(HeapPtr, T);
+
+  protected:
+    void post() { InternalGCMethods<T>::postBarrier(&this->value); }
+
+  private:
+    void set(const T& v) {
+        this->pre();
+        this->value = v;
+        post();
     }
 
     /*
-     * For HeapPtr, move semantics are equivalent to copy semantics. In
+     * Unlike RelocatablePtr<T>, HeapPtr<T> must be managed with GC lifetimes.
+     * Specifically, the memory used by the pointer itself must be live until
+     * at least the next minor GC. For that reason, move semantics are invalid
+     * and are deleted here. Please note that not all containers support move
+     * semantics, so this does not completely prevent invalid uses.
+     */
+    HeapPtr(HeapPtr<T>&&) = delete;
+    HeapPtr<T>& operator=(HeapPtr<T>&&) = delete;
+};
+
+/*
+ * ImmutableTenuredPtr is designed for one very narrow case: replacing
+ * immutable raw pointers to GC-managed things, implicitly converting to a
+ * handle type for ease of use. Pointers encapsulated by this type must:
+ *
+ *   be immutable (no incremental write barriers),
+ *   never point into the nursery (no generational write barriers), and
+ *   be traced via MarkRuntime (we use fromMarkedLocation).
+ *
+ * In short: you *really* need to know what you're doing before you use this
+ * class!
+ */
+template <typename T>
+class ImmutableTenuredPtr
+{
+    T value;
+
+  public:
+    operator T() const { return value; }
+    T operator->() const { return value; }
+
+    operator Handle<T>() const {
+        return Handle<T>::fromMarkedLocation(&value);
+    }
+
+    void init(T ptr) {
+        MOZ_ASSERT(ptr->isTenured());
+        value = ptr;
+    }
+
+    T get() const { return value; }
+    const T* address() { return &value; }
+};
+
+/*
+ * A pre- and post-barriered heap pointer, for use inside the JS engine.
+ *
+ * Unlike HeapPtr<T>, it can be used in memory that is not managed by the GC,
+ * i.e. in C++ containers.  It is, however, somewhat slower, so should only be
+ * used in contexts where this ability is necessary.
+ */
+template <class T>
+class RelocatablePtr : public BarrieredBase<T>
+{
+  public:
+    RelocatablePtr() : BarrieredBase<T>(GCMethods<T>::initial()) {}
+    explicit RelocatablePtr(T v) : BarrieredBase<T>(v) {
+        if (GCMethods<T>::needsPostBarrier(v))
+            post();
+    }
+
+    /*
+     * For RelocatablePtr, move semantics are equivalent to copy semantics. In
      * C++, a copy constructor taking const-ref is the way to get a single
      * function that will be used for both lvalue and rvalue copies, so we can
      * simply omit the rvalue variant.
      */
-    HeapPtr(const HeapPtr<T>& v) : BarrieredBase<T>(v) {
-        post(GCMethods<T>::initial(), this->value);
+    RelocatablePtr(const RelocatablePtr<T>& v) : BarrieredBase<T>(v) {
+        if (GCMethods<T>::needsPostBarrier(this->value))
+            post();
     }
 
-    ~HeapPtr() {
+    ~RelocatablePtr() {
         this->pre();
-        post(this->value, GCMethods<T>::initial());
+        if (GCMethods<T>::needsPostBarrier(this->value))
+            relocate();
     }
 
-    void init(T v) {
-        this->value = v;
-        post(GCMethods<T>::initial(), this->value);
-    }
-
-    DECLARE_POINTER_ASSIGN_OPS(HeapPtr, T);
+    DECLARE_POINTER_ASSIGN_OPS(RelocatablePtr, T);
 
     /* Make this friend so it can access pre() and post(). */
     template <class T1, class T2>
     friend inline void
     BarrieredSetPair(Zone* zone,
-                     HeapPtr<T1*>& v1, T1* val1,
-                     HeapPtr<T2*>& v2, T2* val2);
+                     RelocatablePtr<T1*>& v1, T1* val1,
+                     RelocatablePtr<T2*>& v2, T2* val2);
 
   protected:
     void set(const T& v) {
         this->pre();
         postBarrieredSet(v);
     }
 
     void postBarrieredSet(const T& v) {
-        T tmp = this->value;
-        this->value = v;
-        post(tmp, this->value);
+        if (GCMethods<T>::needsPostBarrier(v)) {
+            this->value = v;
+            post();
+        } else if (GCMethods<T>::needsPostBarrier(this->value)) {
+            relocate();
+            this->value = v;
+        } else {
+            this->value = v;
+        }
     }
 
-    void post(T prev, T next) {
-        InternalGCMethods<T>::postBarrier(&this->value, prev, next);
+    void post() {
+        MOZ_ASSERT(GCMethods<T>::needsPostBarrier(this->value));
+        InternalGCMethods<T>::postBarrierRelocate(&this->value);
+    }
+
+    void relocate() {
+        MOZ_ASSERT(GCMethods<T>::needsPostBarrier(this->value));
+        InternalGCMethods<T>::postBarrierRemove(&this->value);
     }
 };
 
 /*
  * This is a hack for RegExpStatics::updateFromMatch. It allows us to do two
  * barriers with only one branch to check if we're in an incremental GC.
  */
 template <class T1, class T2>
 static inline void
 BarrieredSetPair(Zone* zone,
-                 HeapPtr<T1*>& v1, T1* val1,
-                 HeapPtr<T2*>& v2, T2* val2)
+                 RelocatablePtr<T1*>& v1, T1* val1,
+                 RelocatablePtr<T2*>& v2, T2* val2)
 {
     if (T1::needWriteBarrierPre(zone)) {
         v1.pre();
         v2.pre();
     }
     v1.postBarrieredSet(val1);
     v2.postBarrieredSet(val2);
 }
@@ -548,50 +654,16 @@ struct ReadBarrieredHasher
     static bool match(const Key& k, Lookup l) { return k.get() == l; }
     static void rekey(Key& k, const Key& newKey) { k.set(newKey); }
 };
 
 /* Specialized hashing policy for ReadBarriereds. */
 template <class T>
 struct DefaultHasher<ReadBarriered<T>> : ReadBarrieredHasher<T> { };
 
-/*
- * ImmutableTenuredPtr is designed for one very narrow case: replacing
- * immutable raw pointers to GC-managed things, implicitly converting to a
- * handle type for ease of use. Pointers encapsulated by this type must:
- *
- *   be immutable (no incremental write barriers),
- *   never point into the nursery (no generational write barriers), and
- *   be traced via MarkRuntime (we use fromMarkedLocation).
- *
- * In short: you *really* need to know what you're doing before you use this
- * class!
- */
-template <typename T>
-class ImmutableTenuredPtr
-{
-    T value;
-
-  public:
-    operator T() const { return value; }
-    T operator->() const { return value; }
-
-    operator Handle<T>() const {
-        return Handle<T>::fromMarkedLocation(&value);
-    }
-
-    void init(T ptr) {
-        MOZ_ASSERT(ptr->isTenured());
-        value = ptr;
-    }
-
-    T get() const { return value; }
-    const T* address() { return &value; }
-};
-
 class ArrayObject;
 class ArrayBufferObject;
 class NestedScopeObject;
 class DebugScopeObject;
 class GlobalObject;
 class ScriptSourceObject;
 class Shape;
 class BaseShape;
@@ -601,16 +673,30 @@ class JitCode;
 }
 
 typedef PreBarriered<JSObject*> PreBarrieredObject;
 typedef PreBarriered<JSScript*> PreBarrieredScript;
 typedef PreBarriered<jit::JitCode*> PreBarrieredJitCode;
 typedef PreBarriered<JSString*> PreBarrieredString;
 typedef PreBarriered<JSAtom*> PreBarrieredAtom;
 
+typedef RelocatablePtr<JSObject*> RelocatablePtrObject;
+typedef RelocatablePtr<JSFunction*> RelocatablePtrFunction;
+typedef RelocatablePtr<PlainObject*> RelocatablePtrPlainObject;
+typedef RelocatablePtr<JSScript*> RelocatablePtrScript;
+typedef RelocatablePtr<NativeObject*> RelocatablePtrNativeObject;
+typedef RelocatablePtr<NestedScopeObject*> RelocatablePtrNestedScopeObject;
+typedef RelocatablePtr<Shape*> RelocatablePtrShape;
+typedef RelocatablePtr<ObjectGroup*> RelocatablePtrObjectGroup;
+typedef RelocatablePtr<jit::JitCode*> RelocatablePtrJitCode;
+typedef RelocatablePtr<JSLinearString*> RelocatablePtrLinearString;
+typedef RelocatablePtr<JSString*> RelocatablePtrString;
+typedef RelocatablePtr<JSAtom*> RelocatablePtrAtom;
+typedef RelocatablePtr<ArrayBufferObjectMaybeShared*> RelocatablePtrArrayBufferObjectMaybeShared;
+
 typedef HeapPtr<NativeObject*> HeapPtrNativeObject;
 typedef HeapPtr<ArrayObject*> HeapPtrArrayObject;
 typedef HeapPtr<ArrayBufferObjectMaybeShared*> HeapPtrArrayBufferObjectMaybeShared;
 typedef HeapPtr<ArrayBufferObject*> HeapPtrArrayBufferObject;
 typedef HeapPtr<BaseShape*> HeapPtrBaseShape;
 typedef HeapPtr<JSAtom*> HeapPtrAtom;
 typedef HeapPtr<JSFlatString*> HeapPtrFlatString;
 typedef HeapPtr<JSFunction*> HeapPtrFunction;
@@ -621,19 +707,21 @@ typedef HeapPtr<JSString*> HeapPtrString
 typedef HeapPtr<PlainObject*> HeapPtrPlainObject;
 typedef HeapPtr<PropertyName*> HeapPtrPropertyName;
 typedef HeapPtr<Shape*> HeapPtrShape;
 typedef HeapPtr<UnownedBaseShape*> HeapPtrUnownedBaseShape;
 typedef HeapPtr<jit::JitCode*> HeapPtrJitCode;
 typedef HeapPtr<ObjectGroup*> HeapPtrObjectGroup;
 
 typedef PreBarriered<Value> PreBarrieredValue;
+typedef RelocatablePtr<Value> RelocatableValue;
 typedef HeapPtr<Value> HeapValue;
 
 typedef PreBarriered<jsid> PreBarrieredId;
+typedef RelocatablePtr<jsid> RelocatableId;
 typedef HeapPtr<jsid> HeapId;
 
 typedef ImmutableTenuredPtr<PropertyName*> ImmutablePropertyNamePtr;
 typedef ImmutableTenuredPtr<JS::Symbol*> ImmutableSymbolPtr;
 
 typedef ReadBarriered<DebugScopeObject*> ReadBarrieredDebugScopeObject;
 typedef ReadBarriered<GlobalObject*> ReadBarrieredGlobalObject;
 typedef ReadBarriered<JSFunction*> ReadBarrieredFunction;
@@ -646,17 +734,17 @@ typedef ReadBarriered<jit::JitCode*> Rea
 typedef ReadBarriered<ObjectGroup*> ReadBarrieredObjectGroup;
 typedef ReadBarriered<JSAtom*> ReadBarrieredAtom;
 typedef ReadBarriered<JS::Symbol*> ReadBarrieredSymbol;
 
 typedef ReadBarriered<Value> ReadBarrieredValue;
 
 // A pre- and post-barriered Value that is specialized to be aware that it
 // resides in a slots or elements vector. This allows it to be relocated in
-// memory, but with substantially less overhead than a HeapPtr.
+// memory, but with substantially less overhead than a RelocatablePtr.
 class HeapSlot : public BarrieredBase<Value>
 {
   public:
     enum Kind {
         Slot = 0,
         Element = 1
     };
 
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -280,18 +280,19 @@ class TenuredCell : public Cell
     }
     MOZ_ALWAYS_INLINE JS::shadow::Zone* shadowZoneFromAnyThread() const {
         return JS::shadow::Zone::asShadowZone(zoneFromAnyThread());
     }
 
     static MOZ_ALWAYS_INLINE void readBarrier(TenuredCell* thing);
     static MOZ_ALWAYS_INLINE void writeBarrierPre(TenuredCell* thing);
 
-    static MOZ_ALWAYS_INLINE void writeBarrierPost(void* cellp, TenuredCell* prior,
-                                                   TenuredCell* next);
+    static MOZ_ALWAYS_INLINE void writeBarrierPost(TenuredCell* thing, void* cellp);
+    static MOZ_ALWAYS_INLINE void writeBarrierPostRelocate(TenuredCell* thing, void* cellp);
+    static MOZ_ALWAYS_INLINE void writeBarrierPostRemove(TenuredCell* thing, void* cellp);
 
 #ifdef DEBUG
     inline bool isAligned() const;
 #endif
 };
 
 /*
  * The mark bitmap has one bit per each GC cell. For multi-cell GC things this
@@ -1464,19 +1465,31 @@ TenuredCell::writeBarrierPre(TenuredCell
 static MOZ_ALWAYS_INLINE void
 AssertValidToSkipBarrier(TenuredCell* thing)
 {
     MOZ_ASSERT(!IsInsideNursery(thing));
     MOZ_ASSERT_IF(thing, MapAllocToTraceKind(thing->getAllocKind()) != JS::TraceKind::Object);
 }
 
 /* static */ MOZ_ALWAYS_INLINE void
-TenuredCell::writeBarrierPost(void* cellp, TenuredCell* prior, TenuredCell* next)
+TenuredCell::writeBarrierPost(TenuredCell* thing, void* cellp)
+{
+    AssertValidToSkipBarrier(thing);
+}
+
+/* static */ MOZ_ALWAYS_INLINE void
+TenuredCell::writeBarrierPostRelocate(TenuredCell* thing, void* cellp)
 {
-    AssertValidToSkipBarrier(next);
+    AssertValidToSkipBarrier(thing);
+}
+
+/* static */ MOZ_ALWAYS_INLINE void
+TenuredCell::writeBarrierPostRemove(TenuredCell* thing, void* cellp)
+{
+    AssertValidToSkipBarrier(thing);
 }
 
 #ifdef DEBUG
 bool
 Cell::isAligned() const
 {
     if (!isTenured())
         return true;
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1744,16 +1744,17 @@ template <> void js::TenuringTracer::tra
 
 template <typename T>
 void
 js::gc::StoreBuffer::MonoTypeBuffer<T>::trace(StoreBuffer* owner, TenuringTracer& mover)
 {
     mozilla::ReentrancyGuard g(*owner);
     MOZ_ASSERT(owner->isEnabled());
     MOZ_ASSERT(stores_.initialized());
+    sinkStores(owner);
     for (typename StoreSet::Range r = stores_.all(); !r.empty(); r.popFront())
         r.front().trace(mover);
 }
 
 namespace js {
 namespace gc {
 template void
 StoreBuffer::MonoTypeBuffer<StoreBuffer::WholeCellEdges>::trace(StoreBuffer*, TenuringTracer&);
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -440,16 +440,24 @@ js::Nursery::collect(JSRuntime* rt, JS::
     TIME_START(traceSlots);
     sb.traceSlots(mover);
     TIME_END(traceSlots);
 
     TIME_START(traceWholeCells);
     sb.traceWholeCells(mover);
     TIME_END(traceWholeCells);
 
+    TIME_START(traceRelocatableValues);
+    sb.traceRelocatableValues(mover);
+    TIME_END(traceRelocatableValues);
+
+    TIME_START(traceRelocatableCells);
+    sb.traceRelocatableCells(mover);
+    TIME_END(traceRelocatableCells);
+
     TIME_START(traceGenericEntries);
     sb.traceGenericEntries(&mover);
     TIME_END(traceGenericEntries);
 
     TIME_START(markRuntime);
     rt->gc.markRuntime(&mover);
     TIME_END(markRuntime);
 
@@ -544,32 +552,34 @@ js::Nursery::collect(JSRuntime* rt, JS::
         rt->addTelemetry(JS_TELEMETRY_GC_MINOR_REASON_LONG, reason);
 
     TraceMinorGCEnd();
 
     if (enableProfiling_ && totalTime >= profileThreshold_) {
         static bool printedHeader = false;
         if (!printedHeader) {
             fprintf(stderr,
-                    "MinorGC: Reason               PRate  Size Time   mkVals mkClls mkSlts mkWCll mkGnrc ckTbls mkRntm mkDbgr clrNOC collct swpABO updtIn runFin frSlts clrSB  sweep resize pretnr\n");
+                    "MinorGC: Reason               PRate  Size Time   mkVals mkClls mkSlts mkWCll mkRVal mkRCll mkGnrc ckTbls mkRntm mkDbgr clrNOC collct swpABO updtIn runFin frSlts clrSB  sweep resize pretnr\n");
             printedHeader = true;
         }
 
 #define FMT " %6" PRIu64
         fprintf(stderr,
-                "MinorGC: %20s %5.1f%% %4d" FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT "\n",
+                "MinorGC: %20s %5.1f%% %4d" FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT "\n",
                 js::gcstats::ExplainReason(reason),
                 promotionRate * 100,
                 numActiveChunks_,
                 totalTime,
                 TIME_TOTAL(cancelIonCompilations),
                 TIME_TOTAL(traceValues),
                 TIME_TOTAL(traceCells),
                 TIME_TOTAL(traceSlots),
                 TIME_TOTAL(traceWholeCells),
+                TIME_TOTAL(traceRelocatableValues),
+                TIME_TOTAL(traceRelocatableCells),
                 TIME_TOTAL(traceGenericEntries),
                 TIME_TOTAL(checkHashTables),
                 TIME_TOTAL(markRuntime),
                 TIME_TOTAL(markDebugger),
                 TIME_TOTAL(clearNewObjectCache),
                 TIME_TOTAL(collectToFP),
                 TIME_TOTAL(sweepArrayBufferViewList),
                 TIME_TOTAL(updateJitActivations),
--- a/js/src/gc/StoreBuffer.cpp
+++ b/js/src/gc/StoreBuffer.cpp
@@ -39,16 +39,18 @@ StoreBuffer::enable()
 {
     if (enabled_)
         return true;
 
     if (!bufferVal.init() ||
         !bufferCell.init() ||
         !bufferSlot.init() ||
         !bufferWholeCell.init() ||
+        !bufferRelocVal.init() ||
+        !bufferRelocCell.init() ||
         !bufferGeneric.init())
     {
         return false;
     }
 
     enabled_ = true;
     return true;
 }
@@ -72,16 +74,18 @@ StoreBuffer::clear()
 
     aboutToOverflow_ = false;
     cancelIonCompilations_ = false;
 
     bufferVal.clear();
     bufferCell.clear();
     bufferSlot.clear();
     bufferWholeCell.clear();
+    bufferRelocVal.clear();
+    bufferRelocCell.clear();
     bufferGeneric.clear();
 
     return true;
 }
 
 void
 StoreBuffer::setAboutToOverflow()
 {
@@ -95,15 +99,17 @@ StoreBuffer::setAboutToOverflow()
 void
 StoreBuffer::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::GCSizes
 *sizes)
 {
     sizes->storeBufferVals       += bufferVal.sizeOfExcludingThis(mallocSizeOf);
     sizes->storeBufferCells      += bufferCell.sizeOfExcludingThis(mallocSizeOf);
     sizes->storeBufferSlots      += bufferSlot.sizeOfExcludingThis(mallocSizeOf);
     sizes->storeBufferWholeCells += bufferWholeCell.sizeOfExcludingThis(mallocSizeOf);
+    sizes->storeBufferRelocVals  += bufferRelocVal.sizeOfExcludingThis(mallocSizeOf);
+    sizes->storeBufferRelocCells += bufferRelocCell.sizeOfExcludingThis(mallocSizeOf);
     sizes->storeBufferGenerics   += bufferGeneric.sizeOfExcludingThis(mallocSizeOf);
 }
 
 template struct StoreBuffer::MonoTypeBuffer<StoreBuffer::ValueEdge>;
 template struct StoreBuffer::MonoTypeBuffer<StoreBuffer::CellPtrEdge>;
 template struct StoreBuffer::MonoTypeBuffer<StoreBuffer::SlotsEdge>;
 template struct StoreBuffer::MonoTypeBuffer<StoreBuffer::WholeCellEdges>;
--- a/js/src/gc/StoreBuffer.h
+++ b/js/src/gc/StoreBuffer.h
@@ -28,16 +28,18 @@ namespace gc {
  */
 class BufferableRef
 {
   public:
     virtual void trace(JSTracer* trc) = 0;
     bool maybeInRememberedSet(const Nursery&) const { return true; }
 };
 
+typedef HashSet<void*, PointerHasher<void*, 3>, SystemAllocPolicy> EdgeSet;
+
 /* The size of a single block of store buffer storage space. */
 static const size_t LifoAllocBlockSize = 1 << 16; /* 64KiB */
 
 /*
  * The StoreBuffer observes all writes that occur in the system and performs
  * efficient filtering of them to derive a remembered set for nursery GC.
  */
 class StoreBuffer
@@ -50,56 +52,80 @@ class StoreBuffer
     /*
      * This buffer holds only a single type of edge. Using this buffer is more
      * efficient than the generic buffer when many writes will be to the same
      * type of edge: e.g. Value or Cell*.
      */
     template<typename T>
     struct MonoTypeBuffer
     {
-        /* The set of stores. */
+        /* The canonical set of stores. */
         typedef HashSet<T, typename T::Hasher, SystemAllocPolicy> StoreSet;
         StoreSet stores_;
 
+        /*
+         * A small, fixed-size buffer in front of the canonical set to simplify
+         * insertion via jit code.
+         */
+        const static size_t NumBufferEntries = 4096 / sizeof(T);
+        T buffer_[NumBufferEntries];
+        T* insert_;
+
         /* Maximum number of entries before we request a minor GC. */
         const static size_t MaxEntries = 48 * 1024 / sizeof(T);
 
-        MonoTypeBuffer() {}
+        explicit MonoTypeBuffer() { clearBuffer(); }
         ~MonoTypeBuffer() { stores_.finish(); }
 
         bool init() {
             if (!stores_.initialized() && !stores_.init())
                 return false;
             clear();
             return true;
         }
 
+        void clearBuffer() {
+            JS_POISON(buffer_, JS_EMPTY_STOREBUFFER_PATTERN, NumBufferEntries * sizeof(T));
+            insert_ = buffer_;
+        }
+
         void clear() {
+            clearBuffer();
             if (stores_.initialized())
                 stores_.clear();
         }
 
         /* Add one item to the buffer. */
         void put(StoreBuffer* owner, const T& t) {
             MOZ_ASSERT(stores_.initialized());
-            if (MOZ_UNLIKELY(!stores_.put(t)))
-                CrashAtUnhandlableOOM("Failed to allocate for MonoTypeBuffer.");
+            *insert_++ = t;
+            if (MOZ_UNLIKELY(insert_ == buffer_ + NumBufferEntries))
+                sinkStores(owner);
+        }
+
+        /* Move any buffered stores to the canonical store set. */
+        void sinkStores(StoreBuffer* owner) {
+            MOZ_ASSERT(stores_.initialized());
+
+            for (T* p = buffer_; p < insert_; ++p) {
+                if (!stores_.put(*p))
+                    CrashAtUnhandlableOOM("Failed to allocate for MonoTypeBuffer::sinkStores.");
+            }
+            clearBuffer();
+
             if (MOZ_UNLIKELY(stores_.count() > MaxEntries))
                 owner->setAboutToOverflow();
         }
 
         /* Remove an item from the store buffer. */
         void unput(StoreBuffer* owner, const T& v) {
+            sinkStores(owner);
             stores_.remove(v);
         }
 
-        bool has(const T& v) const {
-            return stores_.has(v);
-        }
-
         /* Trace the source of all edges in the store buffer. */
         void trace(StoreBuffer* owner, TenuringTracer& mover);
 
         size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) {
             return stores_.sizeOfExcludingThis(mallocSizeOf);
         }
 
       private:
@@ -350,84 +376,105 @@ class StoreBuffer
         if (edge.maybeInRememberedSet(nursery_))
             buffer.put(this, edge);
     }
 
     MonoTypeBuffer<ValueEdge> bufferVal;
     MonoTypeBuffer<CellPtrEdge> bufferCell;
     MonoTypeBuffer<SlotsEdge> bufferSlot;
     MonoTypeBuffer<WholeCellEdges> bufferWholeCell;
+    MonoTypeBuffer<ValueEdge> bufferRelocVal;
+    MonoTypeBuffer<CellPtrEdge> bufferRelocCell;
     GenericBuffer bufferGeneric;
     bool cancelIonCompilations_;
 
     JSRuntime* runtime_;
     const Nursery& nursery_;
 
     bool aboutToOverflow_;
     bool enabled_;
     mozilla::DebugOnly<bool> mEntered; /* For ReentrancyGuard. */
 
   public:
     explicit StoreBuffer(JSRuntime* rt, const Nursery& nursery)
-      : bufferVal(), bufferCell(), bufferSlot(), bufferWholeCell(), bufferGeneric(),
-        cancelIonCompilations_(false), runtime_(rt), nursery_(nursery), aboutToOverflow_(false),
-        enabled_(false), mEntered(false)
+      : bufferVal(), bufferCell(), bufferSlot(), bufferWholeCell(),
+        bufferRelocVal(), bufferRelocCell(), bufferGeneric(), cancelIonCompilations_(false),
+        runtime_(rt), nursery_(nursery), aboutToOverflow_(false), enabled_(false),
+        mEntered(false)
     {
     }
 
     bool enable();
     void disable();
     bool isEnabled() const { return enabled_; }
 
     bool clear();
 
     /* Get the overflowed status. */
     bool isAboutToOverflow() const { return aboutToOverflow_; }
 
     bool cancelIonCompilations() const { return cancelIonCompilations_; }
 
     /* Insert a single edge into the buffer/remembered set. */
-    void putValueFromAnyThread(JS::Value* vp) { putFromAnyThread(bufferVal, ValueEdge(vp)); }
-    void unputValueFromAnyThread(JS::Value* vp) { unputFromAnyThread(bufferVal, ValueEdge(vp)); }
+    void putValueFromAnyThread(JS::Value* valuep) { putFromAnyThread(bufferVal, ValueEdge(valuep)); }
     void putCellFromAnyThread(Cell** cellp) { putFromAnyThread(bufferCell, CellPtrEdge(cellp)); }
-    void unputCellFromAnyThread(Cell** cellp) { unputFromAnyThread(bufferCell, CellPtrEdge(cellp)); }
     void putSlotFromAnyThread(NativeObject* obj, int kind, int32_t start, int32_t count) {
         putFromAnyThread(bufferSlot, SlotsEdge(obj, kind, start, count));
     }
     void putWholeCellFromMainThread(Cell* cell) {
         MOZ_ASSERT(cell->isTenured());
         putFromMainThread(bufferWholeCell, WholeCellEdges(cell));
     }
 
+    /* Insert or update a single edge in the Relocatable buffer. */
+    void putRelocatableValueFromAnyThread(JS::Value* valuep) {
+        putFromAnyThread(bufferRelocVal, ValueEdge(valuep));
+    }
+    void removeRelocatableValueFromAnyThread(JS::Value* valuep) {
+        unputFromAnyThread(bufferRelocVal, ValueEdge(valuep));
+    }
+    void putRelocatableCellFromAnyThread(Cell** cellp) {
+        putFromAnyThread(bufferRelocCell, CellPtrEdge(cellp));
+    }
+    void removeRelocatableCellFromAnyThread(Cell** cellp) {
+        unputFromAnyThread(bufferRelocCell, CellPtrEdge(cellp));
+    }
+
     /* Insert an entry into the generic buffer. */
     template <typename T>
     void putGeneric(const T& t) { putFromAnyThread(bufferGeneric, t);}
 
     /* Insert or update a callback entry. */
     template <typename Key>
     void putCallback(void (*callback)(JSTracer* trc, Key* key, void* data), Key* key, void* data) {
         putFromAnyThread(bufferGeneric, CallbackRef<Key>(callback, key, data));
     }
 
-    void assertHasCellEdge(Cell** cellp) const { MOZ_ASSERT(bufferCell.has(CellPtrEdge(cellp))); }
-    void assertHasValueEdge(Value* vp) const { MOZ_ASSERT(bufferVal.has(ValueEdge(vp))); }
-
     void setShouldCancelIonCompilations() {
         cancelIonCompilations_ = true;
     }
 
     /* Methods to trace the source of all edges in the store buffer. */
     void traceValues(TenuringTracer& mover)            { bufferVal.trace(this, mover); }
     void traceCells(TenuringTracer& mover)             { bufferCell.trace(this, mover); }
     void traceSlots(TenuringTracer& mover)             { bufferSlot.trace(this, mover); }
     void traceWholeCells(TenuringTracer& mover)        { bufferWholeCell.trace(this, mover); }
+    void traceRelocatableValues(TenuringTracer& mover) { bufferRelocVal.trace(this, mover); }
+    void traceRelocatableCells(TenuringTracer& mover)  { bufferRelocCell.trace(this, mover); }
     void traceGenericEntries(JSTracer *trc)            { bufferGeneric.trace(this, trc); }
 
     /* For use by our owned buffers and for testing. */
     void setAboutToOverflow();
 
+    /* For jit access to the raw buffer. */
+    void oolSinkStoresForWholeCellBuffer() { bufferWholeCell.sinkStores(this); }
+    void* addressOfWholeCellBufferPointer() const { return (void*)&bufferWholeCell.insert_; }
+    void* addressOfWholeCellBufferEnd() const {
+        return (void*)(bufferWholeCell.buffer_ + bufferWholeCell.NumBufferEntries);
+    }
+
     void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::GCSizes* sizes);
 };
 
 } /* namespace gc */
 } /* namespace js */
 
 #endif /* gc_StoreBuffer_h */
--- a/js/src/gdb/tests/test-Root.cpp
+++ b/js/src/gdb/tests/test-Root.cpp
@@ -36,22 +36,26 @@ FRAGMENT(Root, HeapSlot) {
   (void) plinth;
   (void) array;
 }
 
 FRAGMENT(Root, barriers) {
   JSObject* obj = JS_NewPlainObject(cx);
   js::PreBarriered<JSObject*> prebarriered(obj);
   js::HeapPtr<JSObject*> heapptr(obj);
+  js::RelocatablePtr<JSObject*> relocatable(obj);
 
   JS::Value val = JS::ObjectValue(*obj);
   js::PreBarrieredValue prebarrieredValue(JS::ObjectValue(*obj));
   js::HeapValue heapValue(JS::ObjectValue(*obj));
+  js::RelocatableValue relocatableValue(JS::ObjectValue(*obj));
 
   breakpoint();
 
   (void) prebarriered;
   (void) heapptr;
+  (void) relocatable;
   (void) val;
   (void) prebarrieredValue;
   (void) heapValue;
+  (void) relocatableValue;
 }
 
--- a/js/src/jit/BacktrackingAllocator.cpp
+++ b/js/src/jit/BacktrackingAllocator.cpp
@@ -1396,23 +1396,23 @@ BacktrackingAllocator::tryAllocateRegist
     if (!aliasedConflicting.empty()) {
         // One or more aliased registers is allocated to another bundle
         // overlapping this one. Keep track of the conflicting set, and in the
         // case of multiple conflicting sets keep track of the set with the
         // lowest maximum spill weight.
 
         if (JitSpewEnabled(JitSpew_RegAlloc)) {
             if (aliasedConflicting.length() == 1) {
-                LiveBundle* existing = aliasedConflicting[0];
+                mozilla::DebugOnly<LiveBundle*> existing = aliasedConflicting[0];
                 JitSpew(JitSpew_RegAlloc, "  %s collides with %s [weight %lu]",
                         r.reg.name(), existing->toString(), computeSpillWeight(existing));
             } else {
                 JitSpew(JitSpew_RegAlloc, "  %s collides with the following", r.reg.name());
                 for (size_t i = 0; i < aliasedConflicting.length(); i++) {
-                    LiveBundle* existing = aliasedConflicting[i];
+                    mozilla::DebugOnly<LiveBundle*> existing = aliasedConflicting[i];
                     JitSpew(JitSpew_RegAlloc, "      %s [weight %lu]",
                             existing->toString(), computeSpillWeight(existing));
                 }
             }
         }
 
         if (conflicting.empty()) {
             if (!conflicting.appendAll(aliasedConflicting))
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1407,17 +1407,17 @@ jit::BailoutIonToBaseline(JSContext* cx,
     TraceLogStartEvent(logger, TraceLogger_Baseline);
 
     // The caller of the top frame must be one of the following:
     //      IonJS - Ion calling into Ion.
     //      BaselineStub - Baseline calling into Ion.
     //      Entry - Interpreter or other calling into Ion.
     //      Rectifier - Arguments rectifier calling into Ion.
     MOZ_ASSERT(iter.isBailoutJS());
-    FrameType prevFrameType = iter.prevType();
+    mozilla::DebugOnly<FrameType> prevFrameType = iter.prevType();
     MOZ_ASSERT(prevFrameType == JitFrame_IonJS ||
                prevFrameType == JitFrame_BaselineStub ||
                prevFrameType == JitFrame_Entry ||
                prevFrameType == JitFrame_Rectifier ||
                prevFrameType == JitFrame_IonAccessorIC);
 
     // All incoming frames are going to look like this:
     //
--- a/js/src/jit/BaselineDebugModeOSR.cpp
+++ b/js/src/jit/BaselineDebugModeOSR.cpp
@@ -280,16 +280,17 @@ CollectInterpreterStackScripts(JSContext
         if (obs.shouldRecompileOrInvalidate(script)) {
             if (!entries.append(DebugModeOSREntry(iter.frame()->script())))
                 return false;
         }
     }
     return true;
 }
 
+#ifdef DEBUG
 static const char*
 ICEntryKindToString(ICEntry::Kind kind)
 {
     switch (kind) {
       case ICEntry::Kind_Op:
         return "IC";
       case ICEntry::Kind_NonOp:
         return "non-op IC";
@@ -304,16 +305,17 @@ ICEntryKindToString(ICEntry::Kind kind)
       case ICEntry::Kind_DebugPrologue:
         return "debug prologue";
       case ICEntry::Kind_DebugEpilogue:
         return "debug epilogue";
       default:
         MOZ_CRASH("bad ICEntry kind");
     }
 }
+#endif // DEBUG
 
 static void
 SpewPatchBaselineFrame(uint8_t* oldReturnAddress, uint8_t* newReturnAddress,
                        JSScript* script, ICEntry::Kind frameKind, jsbytecode* pc)
 {
     JitSpew(JitSpew_BaselineDebugModeOSR,
             "Patch return %p -> %p on BaselineJS frame (%s:%d) from %s at %s",
             oldReturnAddress, newReturnAddress, script->filename(), script->lineno(),
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -114,21 +114,21 @@ struct BaselineScript
 
     // Limit the locals on a given script so that stack check on baseline frames
     // doesn't overflow a uint32_t value.
     // (MAX_JSSCRIPT_SLOTS * sizeof(Value)) must fit within a uint32_t.
     static const uint32_t MAX_JSSCRIPT_SLOTS = 0xffffu;
 
   private:
     // Code pointer containing the actual method.
-    HeapPtrJitCode method_;
+    RelocatablePtrJitCode method_;
 
     // For heavyweight scripts, template objects to use for the call object and
     // decl env object (linked via the call object's enclosing scope).
-    HeapPtrObject templateScope_;
+    RelocatablePtrObject templateScope_;
 
     // Allocated space for fallback stubs.
     FallbackICStubSpace fallbackStubSpace_;
 
     // If non-null, the list of AsmJSModules that contain an optimized call
     // directly to this script.
     Vector<DependentAsmJSModuleExit>* dependentAsmJSModules_;
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -5469,17 +5469,17 @@ IonBuilder::inlineCalls(CallInfo& callIn
         if (!inlineBlock)
             return false;
 
         // Create a function MConstant to use in the entry ResumePoint. If we
         // can't use a constant, add a no-op MPolyInlineGuard, to prevent
         // hoisting scope chain gets above the dispatch instruction.
         MInstruction* funcDef;
         if (target->isSingleton())
-            funcDef = MConstant::New(alloc(), ObjectValue(*target), constraints(), this);
+            funcDef = MConstant::New(alloc(), ObjectValue(*target), constraints());
         else
             funcDef = MPolyInlineGuard::New(alloc(), callInfo.fun());
 
         funcDef->setImplicitlyUsedUnchecked();
         dispatchBlock->add(funcDef);
 
         // Use the inlined callee in the inline resume point and on stack.
         int funIndex = inlineBlock->entryResumePoint()->stackDepth() - callInfo.numFormals();
@@ -5784,17 +5784,17 @@ IonBuilder::createThisScriptedSingleton(
         return nullptr;
 
     StackTypeSet* thisTypes = TypeScript::ThisTypes(target->nonLazyScript());
     if (!thisTypes || !thisTypes->hasType(TypeSet::ObjectType(templateObject)))
         return nullptr;
 
     // Generate an inline path to create a new |this| object with
     // the given singleton prototype.
-    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this);
+    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
     MCreateThisWithTemplate* createThis =
         MCreateThisWithTemplate::New(alloc(), constraints(), templateConst,
                                      templateObject->group()->initialHeap(constraints()));
     current->add(templateConst);
     current->add(createThis);
 
     return createThis;
 }
@@ -5843,17 +5843,17 @@ IonBuilder::createThisScriptedBaseline(M
     current->add(prototype);
     MDefinition* protoConst = constant(ObjectValue(*proto));
     MGuardObjectIdentity* guard = MGuardObjectIdentity::New(alloc(), prototype, protoConst,
                                                             /* bailOnEquality = */ false);
     current->add(guard);
 
     // Generate an inline path to create a new |this| object with
     // the given prototype.
-    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this);
+    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
     MCreateThisWithTemplate* createThis =
         MCreateThisWithTemplate::New(alloc(), constraints(), templateConst,
                                      templateObject->group()->initialHeap(constraints()));
     current->add(templateConst);
     current->add(createThis);
 
     return createThis;
 }
@@ -6145,19 +6145,16 @@ IonBuilder::jsop_call(uint32_t argc, boo
     }
 
     return makeCall(target, callInfo);
 }
 
 bool
 IonBuilder::testShouldDOMCall(TypeSet* inTypes, JSFunction* func, JSJitInfo::OpType opType)
 {
-    if (IsInsideNursery(func))
-        return false;
-
     if (!func->isNative() || !func->jitInfo())
         return false;
 
     // If all the DOM objects flowing through are legal with this
     // property, we can bake in a call to the bottom half of the DOM
     // accessor
     DOMInstanceClassHasProtoAtDepth instanceChecker =
         compartment->runtime()->DOMcallbacks()->instanceClassMatchesProto;
@@ -6463,17 +6460,17 @@ bool
 IonBuilder::jsop_newarray(uint32_t count)
 {
     JSObject* templateObject = inspector->getTemplateObject(pc);
     gc::InitialHeap heap;
     MConstant* templateConst;
 
     if (templateObject) {
         heap = templateObject->group()->initialHeap(constraints());
-        templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this);
+        templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
     } else {
         heap = gc::DefaultHeap;
         templateConst = MConstant::New(alloc(), NullValue());
     }
     current->add(templateConst);
 
     MNewArray* ins = MNewArray::New(alloc(), constraints(), count, templateConst, heap, pc);
     current->add(ins);
@@ -6514,17 +6511,17 @@ bool
 IonBuilder::jsop_newobject()
 {
     JSObject* templateObject = inspector->getTemplateObject(pc);
     gc::InitialHeap heap;
     MConstant* templateConst;
 
     if (templateObject) {
         heap = templateObject->group()->initialHeap(constraints());
-        templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this);
+        templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
     } else {
         heap = gc::DefaultHeap;
         templateConst = MConstant::New(alloc(), NullValue());
     }
 
     current->add(templateConst);
     MNewObject* ins = MNewObject::New(alloc(), constraints(), templateConst, heap,
                                       MNewObject::ObjectLiteral);
@@ -9009,17 +9006,17 @@ IonBuilder::setElemTryDense(bool* emitte
     JSValueType unboxedType = UnboxedArrayElementType(constraints(), object, index);
     if (unboxedType == JSVAL_TYPE_MAGIC) {
         if (!ElementAccessIsDenseNative(constraints(), object, index)) {
             trackOptimizationOutcome(TrackedOutcome::AccessNotDense);
             return true;
         }
     }
 
-    if (PropertyWriteNeedsTypeBarrier(this, constraints(), current,
+    if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
                                       &object, nullptr, &value, /* canModify = */ true))
     {
         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
         return true;
     }
 
     if (!object->resultTypeSet()) {
         trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
@@ -9089,17 +9086,17 @@ IonBuilder::setElemTryCache(bool* emitte
     // Temporary disable the cache if non dense native,
     // until the cache supports more ics
     SetElemICInspector icInspect(inspector->setElemICInspector(pc));
     if (!icInspect.sawDenseWrite() && !icInspect.sawTypedArrayWrite()) {
         trackOptimizationOutcome(TrackedOutcome::SetElemNonDenseNonTANotCached);
         return true;
     }
 
-    if (PropertyWriteNeedsTypeBarrier(this, constraints(), current,
+    if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
                                       &object, nullptr, &value, /* canModify = */ true))
     {
         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
         return true;
     }
 
     // We can avoid worrying about holes in the IC if we know a priori we are safe
     // from them. If TI can guard that there are no indexed properties on the prototype
@@ -9427,17 +9424,17 @@ IonBuilder::jsop_rest()
         return true;
     }
 
     // We know the exact number of arguments the callee pushed.
     unsigned numActuals = inlineCallInfo_->argc();
     unsigned numFormals = info().nargs() - 1;
     unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0;
 
-    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this);
+    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
     current->add(templateConst);
 
     MNewArray* array = MNewArray::New(alloc(), constraints(), numRest, templateConst,
                                       templateObject->group()->initialHeap(constraints()), pc);
     current->add(array);
 
     if (numRest == 0) {
         // No more updating to do. (Note that in this one case the length from
@@ -10807,18 +10804,17 @@ IonBuilder::getPropTryCommonGetter(bool*
           case InliningDecision_Inline:
             if (!inlineScriptedCall(callInfo, commonGetter))
                 return false;
             *emitted = true;
             return true;
         }
     }
 
-    JSFunction* tenuredCommonGetter = IsInsideNursery(commonGetter) ? nullptr : commonGetter;
-    if (!makeCall(tenuredCommonGetter, callInfo))
+    if (!makeCall(commonGetter, callInfo))
         return false;
 
     // If the getter could have been inlined, don't track success. The call to
     // makeInliningDecision above would have tracked a specific reason why we
     // couldn't inline.
     if (!commonGetter->isInterpreted())
         trackOptimizationSuccess();
 
@@ -11178,17 +11174,17 @@ IonBuilder::jsop_setprop(PropertyName* n
 
         // Try to emit stores to known binary data blocks
         trackOptimizationAttempt(TrackedStrategy::SetProp_TypedObject);
         if (!setPropTryTypedObject(&emitted, obj, name, value) || emitted)
             return emitted;
     }
 
     TemporaryTypeSet* objTypes = obj->resultTypeSet();
-    bool barrier = PropertyWriteNeedsTypeBarrier(this, constraints(), current, &obj, name, &value,
+    bool barrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, name, &value,
                                                  /* canModify = */ true);
 
     if (!forceInlineCaches()) {
         // Try to emit stores to unboxed objects.
         trackOptimizationAttempt(TrackedStrategy::SetProp_Unboxed);
         if (!setPropTryUnboxed(&emitted, obj, name, value, barrier, objTypes) || emitted)
             return emitted;
     }
@@ -11302,18 +11298,17 @@ IonBuilder::setPropTryCommonSetter(bool*
           case InliningDecision_Inline:
             if (!inlineScriptedCall(callInfo, commonSetter))
                 return false;
             *emitted = true;
             return true;
         }
     }
 
-    JSFunction* tenuredCommonSetter = IsInsideNursery(commonSetter) ? nullptr : commonSetter;
-    MCall* call = makeCallHelper(tenuredCommonSetter, callInfo);
+    MCall* call = makeCallHelper(commonSetter, callInfo);
     if (!call)
         return false;
 
     current->push(value);
     if (!resumeAfter(call))
         return false;
 
     // If the setter could have been inlined, don't track success. The call to
@@ -11820,17 +11815,17 @@ bool
 IonBuilder::jsop_lambda(JSFunction* fun)
 {
     MOZ_ASSERT(analysis().usesScopeChain());
     MOZ_ASSERT(!fun->isArrow());
 
     if (fun->isNative() && IsAsmJSModuleNative(fun->native()))
         return abort("asm.js module function");
 
-    MConstant* cst = MConstant::NewConstraintlessObject(alloc(), fun, this);
+    MConstant* cst = MConstant::NewConstraintlessObject(alloc(), fun);
     current->add(cst);
     MLambda* ins = MLambda::New(alloc(), constraints(), current->scopeChain(), cst);
     current->add(ins);
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
@@ -12370,27 +12365,26 @@ IonBuilder::jsop_in_dense(JSValueType un
                                   unboxedType);
 
     current->add(ins);
     current->push(ins);
 
     return true;
 }
 
-static bool
-HasOnProtoChain(CompilerConstraintList* constraints, TypeSet::ObjectKey* key,
-                JSObject* protoObject, bool* hasOnProto)
+bool
+IonBuilder::hasOnProtoChain(TypeSet::ObjectKey* key, JSObject* protoObject, bool* hasOnProto)
 {
     MOZ_ASSERT(protoObject);
 
     while (true) {
-        if (!key->hasStableClassAndProto(constraints) || !key->clasp()->isNative())
+        if (!key->hasStableClassAndProto(constraints()) || !key->clasp()->isNative())
             return false;
 
-        JSObject* proto = key->proto().toObjectOrNull();
+        JSObject* proto = checkNurseryObject(key->proto().toObjectOrNull());
         if (!proto) {
             *hasOnProto = false;
             return true;
         }
 
         if (proto == protoObject) {
             *hasOnProto = true;
             return true;
@@ -12424,17 +12418,17 @@ IonBuilder::tryFoldInstanceOf(MDefinitio
     bool knownIsInstance = false;
 
     for (unsigned i = 0; i < lhsTypes->getObjectCount(); i++) {
         TypeSet::ObjectKey* key = lhsTypes->getObject(i);
         if (!key)
             continue;
 
         bool isInstance;
-        if (!HasOnProtoChain(constraints(), key, protoObject, &isInstance))
+        if (!hasOnProtoChain(key, protoObject, &isInstance))
             return false;
 
         if (isFirst) {
             knownIsInstance = isInstance;
             isFirst = false;
         } else if (knownIsInstance != isInstance) {
             // Some of the objects have protoObject on their proto chain and
             // others don't, so we can't optimize this.
@@ -12506,17 +12500,17 @@ IonBuilder::jsop_instanceof()
         rhs = addShapeGuard(rhs, shape, Bailout_ShapeGuard);
 
         // Guard .prototype == protoObject.
         MOZ_ASSERT(shape->numFixedSlots() == 0, "Must be a dynamic slot");
         MSlots* slots = MSlots::New(alloc(), rhs);
         current->add(slots);
         MLoadSlot* prototype = MLoadSlot::New(alloc(), slots, slot);
         current->add(prototype);
-        MConstant* protoConst = MConstant::NewConstraintlessObject(alloc(), protoObject, this);
+        MConstant* protoConst = MConstant::NewConstraintlessObject(alloc(), protoObject);
         current->add(protoConst);
         MGuardObjectIdentity* guard = MGuardObjectIdentity::New(alloc(), prototype, protoConst,
                                                                 /* bailOnEquality = */ false);
         current->add(guard);
 
         if (tryFoldInstanceOf(obj, protoObject))
             return true;
 
@@ -12917,17 +12911,17 @@ IonBuilder::storeReferenceTypedObjectVal
     // Make sure we aren't adding new type information for writes of object and value
     // references.
     if (type != ReferenceTypeDescr::TYPE_STRING) {
         MOZ_ASSERT(type == ReferenceTypeDescr::TYPE_ANY ||
                    type == ReferenceTypeDescr::TYPE_OBJECT);
         MIRType implicitType =
             (type == ReferenceTypeDescr::TYPE_ANY) ? MIRType_Undefined : MIRType_Null;
 
-        if (PropertyWriteNeedsTypeBarrier(this, constraints(), current, &typedObj, name, &value,
+        if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &typedObj, name, &value,
                                           /* canModify = */ true, implicitType))
         {
             trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
             return false;
         }
     }
 
     // Find location within the owner object.
@@ -12986,17 +12980,17 @@ MConstant*
 IonBuilder::constant(const Value& v)
 {
     MOZ_ASSERT(!v.isString() || v.toString()->isAtom(),
                "Handle non-atomized strings outside IonBuilder.");
 
     if (v.isObject())
         checkNurseryObject(&v.toObject());
 
-    MConstant* c = MConstant::New(alloc(), v, constraints(), this);
+    MConstant* c = MConstant::New(alloc(), v, constraints());
     current->add(c);
     return c;
 }
 
 MConstant*
 IonBuilder::constantInt(int32_t i)
 {
     return constant(Int32Value(i));
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -611,16 +611,17 @@ class IonBuilder
 
     bool improveThisTypesForCall();
 
     MDefinition* getCallee();
     MDefinition* getAliasedVar(ScopeCoordinate sc);
     MDefinition* addLexicalCheck(MDefinition* input);
 
     bool tryFoldInstanceOf(MDefinition* lhs, JSObject* protoObject);
+    bool hasOnProtoChain(TypeSet::ObjectKey* key, JSObject* protoObject, bool* hasOnProto);
 
     bool jsop_add(MDefinition* left, MDefinition* right);
     bool jsop_bitnot();
     bool jsop_bitop(JSOp op);
     bool jsop_binary(JSOp op);
     bool jsop_binary(JSOp op, MDefinition* left, MDefinition* right);
     bool jsop_pos();
     bool jsop_neg();
--- a/js/src/jit/JitFrameIterator.h
+++ b/js/src/jit/JitFrameIterator.h
@@ -311,17 +311,17 @@ class JitProfilingFrameIterator
     void* stackAddress() const { return fp(); }
     FrameType frameType() const { MOZ_ASSERT(!done()); return type_; }
     void* returnAddressToFp() const { MOZ_ASSERT(!done()); return returnAddressToFp_; }
 };
 
 class RInstructionResults
 {
     // Vector of results of recover instructions.
-    typedef mozilla::Vector<HeapValue, 1, SystemAllocPolicy> Values;
+    typedef mozilla::Vector<RelocatableValue, 1, SystemAllocPolicy> Values;
     mozilla::UniquePtr<Values, JS::DeletePolicy<Values> > results_;
 
     // The frame pointer is used as a key to check if the current frame already
     // bailed out.
     JitFrameLayout* fp_;
 
     // Record if we tried and succeed at allocating and filling the vector of
     // recover instruction results, if needed.  This flag is needed in order to
@@ -336,17 +336,17 @@ class RInstructionResults
 
     ~RInstructionResults();
 
     bool init(JSContext* cx, uint32_t numResults);
     bool isInitialized() const;
 
     JitFrameLayout* frame() const;
 
-    HeapValue& operator[](size_t index);
+    RelocatableValue& operator[](size_t index);
 
     void trace(JSTracer* trc);
 };
 
 struct MaybeReadFallback
 {
     enum NoGCValue {
         NoGC_UndefinedValue,
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -1740,17 +1740,17 @@ RInstructionResults::isInitialized() con
 
 JitFrameLayout*
 RInstructionResults::frame() const
 {
     MOZ_ASSERT(fp_);
     return fp_;
 }
 
-HeapValue&
+RelocatableValue&
 RInstructionResults::operator [](size_t index)
 {
     return (*results_)[index];
 }
 
 void
 RInstructionResults::trace(JSTracer* trc)
 {
--- a/js/src/jit/JitSpewer.h
+++ b/js/src/jit/JitSpewer.h
@@ -213,22 +213,28 @@ static inline Fprinter& JitSpewPrinter()
 
 class JitSpewIndent
 {
   public:
     explicit JitSpewIndent(JitSpewChannel channel) {}
     ~JitSpewIndent() {}
 };
 
-static inline void JitSpew(JitSpewChannel, const char* fmt, ...)
+// The computation of some of the argument of the spewing functions might be
+// costly, thus we use variaidic macros to ignore any argument of these
+// functions.
+static inline void JitSpewCheckArguments(JitSpewChannel channel, const char* fmt)
 { }
-static inline void JitSpewStart(JitSpewChannel channel, const char* fmt, ...)
-{ }
-static inline void JitSpewCont(JitSpewChannel channel, const char* fmt, ...)
-{ }
+
+#define JitSpewCheckExpandedArgs(channel, fmt, ...) JitSpewCheckArguments(channel, fmt)
+#define JitSpewCheckExpandedArgs_(ArgList) JitSpewCheckExpandedArgs ArgList /* Fix MSVC issue */
+#define JitSpew(...) JitSpewCheckExpandedArgs_((__VA_ARGS__))
+#define JitSpewStart(...) JitSpewCheckExpandedArgs_((__VA_ARGS__))
+#define JitSpewCont(...) JitSpewCheckExpandedArgs_((__VA_ARGS__))
+
 static inline void JitSpewFin(JitSpewChannel channel)
 { }
 
 static inline void JitSpewHeader(JitSpewChannel channel)
 { }
 static inline bool JitSpewEnabled(JitSpewChannel channel)
 { return false; }
 static inline void JitSpewVA(JitSpewChannel channel, const char* fmt, va_list ap)
--- a/js/src/jit/JitcodeMap.cpp
+++ b/js/src/jit/JitcodeMap.cpp
@@ -1290,17 +1290,17 @@ JitcodeRegionEntry::WriteRun(CompactBuff
         WriteDelta(writer, nativeDelta, bytecodeDelta);
 
         // Spew the bytecode in these ranges.
         if (curBytecodeOffset < nextBytecodeOffset) {
             JitSpewStart(JitSpew_Profiling, "      OPS: ");
             uint32_t curBc = curBytecodeOffset;
             while (curBc < nextBytecodeOffset) {
                 jsbytecode* pc = entry[i].tree->script()->offsetToPC(curBc);
-                JSOp op = JSOp(*pc);
+                mozilla::DebugOnly<JSOp> op = JSOp(*pc);
                 JitSpewCont(JitSpew_Profiling, "%s ", js_CodeName[op]);
                 curBc += GetBytecodeLength(pc);
             }
             JitSpewFin(JitSpew_Profiling);
         }
         spewer.spewAndAdvance("      ");
 
         curNativeOffset = nextNativeOffset;
--- a/js/src/jit/LIR.cpp
+++ b/js/src/jit/LIR.cpp
@@ -335,17 +335,16 @@ LNode::printName(GenericPrinter& out)
 bool
 LAllocation::aliases(const LAllocation& other) const
 {
     if (isFloatReg() && other.isFloatReg())
         return toFloatReg()->reg().aliases(other.toFloatReg()->reg());
     return *this == other;
 }
 
-#ifdef DEBUG
 static const char * const TypeChars[] =
 {
     "g",            // GENERAL
     "i",            // INT32
     "o",            // OBJECT
     "s",            // SLOTS
     "f",            // FLOAT32
     "d",            // DOUBLE
@@ -439,17 +438,16 @@ LAllocation::toString() const
         return buf;
       case LAllocation::USE:
         PrintUse(buf, sizeof(buf), toUse());
         return buf;
       default:
         MOZ_CRASH("what?");
     }
 }
-#endif // DEBUG
 
 void
 LAllocation::dump() const
 {
     fprintf(stderr, "%s\n", toString());
 }
 
 void
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -178,21 +178,17 @@ class LAllocation : public TempObject
     bool operator !=(const LAllocation& other) const {
         return bits_ != other.bits_;
     }
 
     HashNumber hash() const {
         return bits_;
     }
 
-#ifdef DEBUG
     const char* toString() const;
-#else
-    const char* toString() const { return "???"; }
-#endif
     bool aliases(const LAllocation& other) const;
     void dump() const;
 
 };
 
 class LUse : public LAllocation
 {
     static const uint32_t POLICY_BITS = 3;
@@ -575,21 +571,17 @@ class LDefinition
             return LDefinition::INT32X4;
           case MIRType_Float32x4:
             return LDefinition::FLOAT32X4;
           default:
             MOZ_CRASH("unexpected type");
         }
     }
 
-#ifdef DEBUG
     const char* toString() const;
-#else
-    const char* toString() const { return "???"; }
-#endif
 
     void dump() const;
 };
 
 // Forward declarations of LIR types.
 #define LIROP(op) class L##op;
     LIR_OPCODE_LIST(LIROP)
 #undef LIROP
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -586,17 +586,17 @@ IonBuilder::inlineArray(CallInfo& callIn
 
         // Don't inline large allocations.
         if (initLength > ArrayObject::EagerAllocationMaxLength)
             return InliningStatus_NotInlined;
     }
 
     callInfo.setImplicitlyUsedUnchecked();
 
-    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this);
+    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
     current->add(templateConst);
 
     MNewArray* ins = MNewArray::New(alloc(), constraints(), initLength, templateConst,
                                     templateObject->group()->initialHeap(constraints()), pc);
     current->add(ins);
     current->push(ins);
 
     if (callInfo.argc() >= 2) {
@@ -760,17 +760,17 @@ IonBuilder::inlineArrayPush(CallInfo& ca
 {
     if (callInfo.argc() != 1 || callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
     MDefinition* obj = callInfo.thisArg();
     MDefinition* value = callInfo.getArg(0);
-    if (PropertyWriteNeedsTypeBarrier(this, constraints(), current,
+    if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
                                       &obj, nullptr, &value, /* canModify = */ false))
     {
         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
         return InliningStatus_NotInlined;
     }
     MOZ_ASSERT(obj == callInfo.thisArg() && value == callInfo.getArg(0));
 
     if (getInlineReturnType() != MIRType_Int32)
@@ -1696,17 +1696,17 @@ IonBuilder::inlineConstantStringSplit(Ca
     }
     callInfo.setImplicitlyUsedUnchecked();
 
     TemporaryTypeSet::DoubleConversion conversion =
             getInlineReturnTypeSet()->convertDoubleElements(constraints());
     if (conversion == TemporaryTypeSet::AlwaysConvertToDoubles)
         return InliningStatus_NotInlined;
 
-    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this);
+    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
     current->add(templateConst);
 
     MNewArray* ins = MNewArray::New(alloc(), constraints(), initLength, templateConst,
                                     templateObject->group()->initialHeap(constraints()), pc);
 
     current->add(ins);
     current->push(ins);
 
@@ -1767,17 +1767,17 @@ IonBuilder::inlineStringSplit(CallInfo& 
 
     if (!key.maybeTypes()->hasType(TypeSet::StringType())) {
         key.freeze(constraints());
         return InliningStatus_NotInlined;
     }
 
     callInfo.setImplicitlyUsedUnchecked();
     MConstant* templateObjectDef = MConstant::New(alloc(), ObjectValue(*templateObject),
-                                                  constraints(), this);
+                                                  constraints());
     current->add(templateObjectDef);
 
     MStringSplit* ins = MStringSplit::New(alloc(), constraints(), callInfo.thisArg(),
                                           callInfo.getArg(0), templateObjectDef);
     current->add(ins);
     current->push(ins);
 
     return InliningStatus_Inlined;
@@ -2092,17 +2092,17 @@ IonBuilder::inlineObjectCreate(CallInfo&
         MOZ_ASSERT(types->getKnownMIRType() == MIRType_Object);
     } else {
         if (arg->type() != MIRType_Null)
             return InliningStatus_NotInlined;
     }
 
     callInfo.setImplicitlyUsedUnchecked();
 
-    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this);
+    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
     current->add(templateConst);
     MNewObject* ins = MNewObject::New(alloc(), constraints(), templateConst,
                                       templateObject->group()->initialHeap(constraints()),
                                       MNewObject::ObjectCreate);
     current->add(ins);
     current->push(ins);
     if (!resumeAfter(ins))
         return InliningStatus_Error;
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -636,46 +636,45 @@ MDefinition::replaceAllLiveUsesWith(MDef
 
 bool
 MDefinition::emptyResultTypeSet() const
 {
     return resultTypeSet() && resultTypeSet()->empty();
 }
 
 MConstant*
-MConstant::New(TempAllocator& alloc, const Value& v,
-               CompilerConstraintList* constraints, MIRGenerator* gen)
-{
-    return new(alloc) MConstant(v, constraints, gen);
+MConstant::New(TempAllocator& alloc, const Value& v, CompilerConstraintList* constraints)
+{
+    return new(alloc) MConstant(v, constraints);
 }
 
 MConstant*
 MConstant::NewTypedValue(TempAllocator& alloc, const Value& v, MIRType type,
-                         CompilerConstraintList* constraints, MIRGenerator* gen)
+                         CompilerConstraintList* constraints)
 {
     MOZ_ASSERT(!IsSimdType(type));
     MOZ_ASSERT_IF(type == MIRType_Float32,
                   IsNaN(v.toDouble()) || v.toDouble() == double(float(v.toDouble())));
-    MConstant* constant = new(alloc) MConstant(v, constraints, gen);
+    MConstant* constant = new(alloc) MConstant(v, constraints);
     constant->setResultType(type);
     return constant;
 }
 
 MConstant*
 MConstant::NewAsmJS(TempAllocator& alloc, const Value& v, MIRType type)
 {
     if (type == MIRType_Float32)
         return NewTypedValue(alloc, Float32Value(v.toNumber()), type);
     return NewTypedValue(alloc, v, type);
 }
 
 MConstant*
-MConstant::NewConstraintlessObject(TempAllocator& alloc, JSObject* v, MIRGenerator* gen)
-{
-    return new(alloc) MConstant(v, gen);
+MConstant::NewConstraintlessObject(TempAllocator& alloc, JSObject* v)
+{
+    return new(alloc) MConstant(v);
 }
 
 static TemporaryTypeSet*
 MakeSingletonTypeSetFromKey(CompilerConstraintList* constraints, TypeSet::ObjectKey* key)
 {
     // Invalidate when this object's ObjectGroup gets unknown properties. This
     // happens for instance when we mutate an object's __proto__, in this case
     // we want to invalidate and mark this TypeSet as containing AnyObject
@@ -701,25 +700,44 @@ jit::MakeSingletonTypeSet(CompilerConstr
 
 static TemporaryTypeSet*
 MakeUnknownTypeSet()
 {
     LifoAlloc* alloc = GetJitContext()->temp->lifoAlloc();
     return alloc->new_<TemporaryTypeSet>(alloc, TypeSet::UnknownType());
 }
 
-MConstant::MConstant(const js::Value& vp, CompilerConstraintList* constraints, MIRGenerator* gen)
+#ifdef DEBUG
+
+bool
+jit::IonCompilationCanUseNurseryPointers()
+{
+    // If we are doing backend compilation, which could occur on a helper
+    // thread but might actually be on the main thread, check the flag set on
+    // the PerThreadData by AutoEnterIonCompilation.
+    if (CurrentThreadIsIonCompiling())
+        return !CurrentThreadIsIonCompilingSafeForMinorGC();
+
+    // Otherwise, we must be on the main thread during MIR construction. The
+    // store buffer must have been notified that minor GCs must cancel pending
+    // or in progress Ion compilations.
+    JSRuntime* rt = TlsPerThreadData.get()->runtimeFromMainThread();
+    return rt->gc.storeBuffer.cancelIonCompilations();
+}
+
+#endif // DEBUG
+
+MConstant::MConstant(const js::Value& vp, CompilerConstraintList* constraints)
   : value_(vp)
 {
     setResultType(MIRTypeFromValue(vp));
     if (vp.isObject()) {
         // Create a singleton type set for the object. This isn't necessary for
         // other types as the result type encodes all needed information.
-        MOZ_ASSERT(gen);
-        MOZ_ASSERT_IF(IsInsideNursery(&vp.toObject()), !gen->safeForMinorGC());
+        MOZ_ASSERT_IF(IsInsideNursery(&vp.toObject()), IonCompilationCanUseNurseryPointers());
         setResultTypeSet(MakeSingletonTypeSet(constraints, &vp.toObject()));
     }
     if (vp.isMagic() && vp.whyMagic() == JS_UNINITIALIZED_LEXICAL) {
         // JS_UNINITIALIZED_LEXICAL does not escape to script and is not
         // observed in type sets. However, it may flow around freely during
         // Ion compilation. Give it an unknown typeset to poison any type sets
         // it merges with.
         //
@@ -728,21 +746,20 @@ MConstant::MConstant(const js::Value& vp
         setResultTypeSet(MakeUnknownTypeSet());
     }
 
     MOZ_ASSERT_IF(vp.isString(), vp.toString()->isAtom());
 
     setMovable();
 }
 
-MConstant::MConstant(JSObject* obj, MIRGenerator* gen)
+MConstant::MConstant(JSObject* obj)
   : value_(ObjectValue(*obj))
 {
-    MOZ_ASSERT(gen);
-    MOZ_ASSERT_IF(IsInsideNursery(obj), !gen->safeForMinorGC());
+    MOZ_ASSERT_IF(IsInsideNursery(obj), IonCompilationCanUseNurseryPointers());
     setResultType(MIRType_Object);
     setMovable();
 }
 
 HashNumber
 MConstant::valueHash() const
 {
     // Fold all 64 bits into the 32-bit result. It's tempting to just discard
@@ -5203,28 +5220,28 @@ TryAddTypeBarrierForWrite(TempAllocator&
         kind = BarrierKind::TypeTagOnly;
 
     MInstruction* ins = MMonitorTypes::New(alloc, *pvalue, types, kind);
     current->add(ins);
     return true;
 }
 
 static MInstruction*
-AddGroupGuard(MIRGenerator* gen, MBasicBlock* current, MDefinition* obj,
+AddGroupGuard(TempAllocator& alloc, MBasicBlock* current, MDefinition* obj,
               TypeSet::ObjectKey* key, bool bailOnEquality)
 {
     MInstruction* guard;
 
     if (key->isGroup()) {
-        guard = MGuardObjectGroup::New(gen->alloc(), obj, key->group(), bailOnEquality,
+        guard = MGuardObjectGroup::New(alloc, obj, key->group(), bailOnEquality,
                                        Bailout_ObjectIdentityOrTypeGuard);
     } else {
-        MConstant* singletonConst = MConstant::NewConstraintlessObject(gen->alloc(), key->singleton(), gen);
+        MConstant* singletonConst = MConstant::NewConstraintlessObject(alloc, key->singleton());
         current->add(singletonConst);
-        guard = MGuardObjectIdentity::New(gen->alloc(), obj, singletonConst, bailOnEquality);
+        guard = MGuardObjectIdentity::New(alloc, obj, singletonConst, bailOnEquality);
     }
 
     current->add(guard);
 
     // For now, never move object group / identity guards.
     guard->setNotMovable();
 
     return guard;
@@ -5237,17 +5254,17 @@ jit::CanWriteProperty(TempAllocator& all
                       MIRType implicitType /* = MIRType_None */)
 {
     if (property.couldBeConstant(constraints))
         return false;
     return PropertyTypeIncludes(alloc, property, value, implicitType);
 }
 
 bool
-jit::PropertyWriteNeedsTypeBarrier(MIRGenerator* gen, CompilerConstraintList* constraints,
+jit::PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList* constraints,
                                    MBasicBlock* current, MDefinition** pobj,
                                    PropertyName* name, MDefinition** pvalue,
                                    bool canModify, MIRType implicitType)
 {
     // If any value being written is not reflected in the type information for
     // objects which obj could represent, a type barrier is needed when writing
     // the value. As for propertyReadNeedsTypeBarrier, this only applies for
     // properties that are accounted for by type information, i.e. normal data
@@ -5270,24 +5287,24 @@ jit::PropertyWriteNeedsTypeBarrier(MIRGe
 
         // TI doesn't track TypedArray indexes and should never insert a type
         // barrier for them.
         if (!name && IsAnyTypedArrayClass(key->clasp()))
             continue;
 
         jsid id = name ? NameToId(name) : JSID_VOID;
         HeapTypeSetKey property = key->property(id);
-        if (!CanWriteProperty(gen->alloc(), constraints, property, *pvalue, implicitType)) {
+        if (!CanWriteProperty(alloc, constraints, property, *pvalue, implicitType)) {
             // Either pobj or pvalue needs to be modified to filter out the
             // types which the value could have but are not in the property,
             // or a VM call is required. A VM call is always required if pobj
             // and pvalue cannot be modified.
             if (!canModify)
                 return true;
-            success = TryAddTypeBarrierForWrite(gen->alloc(), constraints, current, types, name, pvalue,
+            success = TryAddTypeBarrierForWrite(alloc, constraints, current, types, name, pvalue,
                                                 implicitType);
             break;
         }
     }
 
     if (success)
         return false;
 
@@ -5303,17 +5320,17 @@ jit::PropertyWriteNeedsTypeBarrier(MIRGe
         TypeSet::ObjectKey* key = types->getObject(i);
         if (!key || key->unknownProperties())
             continue;
         if (!name && IsAnyTypedArrayClass(key->clasp()))
             continue;
 
         jsid id = name ? NameToId(name) : JSID_VOID;
         HeapTypeSetKey property = key->property(id);
-        if (CanWriteProperty(gen->alloc(), constraints, property, *pvalue, implicitType))
+        if (CanWriteProperty(alloc, constraints, property, *pvalue, implicitType))
             continue;
 
         if ((property.maybeTypes() && !property.maybeTypes()->empty()) || excluded)
             return true;
         excluded = key;
     }
 
     MOZ_ASSERT(excluded);
@@ -5324,11 +5341,11 @@ jit::PropertyWriteNeedsTypeBarrier(MIRGe
     if (excluded->isGroup()) {
         if (UnboxedLayout* layout = excluded->group()->maybeUnboxedLayout()) {
             if (layout->nativeGroup())
                 return true;
             excluded->watchStateChangeForUnboxedConvertedToNative(constraints);
         }
     }
 
-    *pobj = AddGroupGuard(gen, current, *pobj, excluded, /* bailOnEquality = */ true);
+    *pobj = AddGroupGuard(alloc, current, *pobj, excluded, /* bailOnEquality = */ true);
     return false;
 }
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -1304,30 +1304,27 @@ class MLimitedTruncate
 };
 
 // A constant js::Value.
 class MConstant : public MNullaryInstruction
 {
     Value value_;
 
   protected:
-    MConstant(const Value& v, CompilerConstraintList* constraints, MIRGenerator* gen);
-    explicit MConstant(JSObject* obj, MIRGenerator* gen);
+    MConstant(const Value& v, CompilerConstraintList* constraints);
+    explicit MConstant(JSObject* obj);
 
   public:
     INSTRUCTION_HEADER(Constant)
     static MConstant* New(TempAllocator& alloc, const Value& v,
-                          CompilerConstraintList* constraints = nullptr,
-                          MIRGenerator* gen = nullptr);
+                          CompilerConstraintList* constraints = nullptr);
     static MConstant* NewTypedValue(TempAllocator& alloc, const Value& v, MIRType type,
-                                    CompilerConstraintList* constraints = nullptr,
-                                    MIRGenerator* gen = nullptr);
+                                    CompilerConstraintList* constraints = nullptr);
     static MConstant* NewAsmJS(TempAllocator& alloc, const Value& v, MIRType type);
-    static MConstant* NewConstraintlessObject(TempAllocator& alloc, JSObject* v,
-                                              MIRGenerator* gen);
+    static MConstant* NewConstraintlessObject(TempAllocator& alloc, JSObject* v);
 
     const js::Value& value() const {
         return value_;
     }
     const js::Value* vp() const {
         return &value_;
     }
     bool valueToBoolean() const {
@@ -2839,52 +2836,59 @@ MergeTypes(MIRType* ptype, TemporaryType
 
 bool
 TypeSetIncludes(TypeSet* types, MIRType input, TypeSet* inputTypes);
 
 bool
 EqualTypes(MIRType type1, TemporaryTypeSet* typeset1,
            MIRType type2, TemporaryTypeSet* typeset2);
 
-// Helper class to assert all GC pointers embedded in MIR instructions are
-// tenured. Off-thread Ion compilation and nursery GCs can happen in parallel,
-// so it's invalid to store pointers to nursery things. There's no need to root
-// these pointers, as GC is suppressed during compilation and off-thread
-// compilations are canceled on every major GC.
+#ifdef DEBUG
+bool
+IonCompilationCanUseNurseryPointers();
+#endif
+
+// Helper class to check that GC pointers embedded in MIR instructions are in
+// in the nursery only when the store buffer has been marked as needing to
+// cancel all ion compilations. Otherwise, off-thread Ion compilation and
+// nursery GCs can happen in parallel, so it's invalid to store pointers to
+// nursery things. There's no need to root these pointers, as GC is suppressed
+// during compilation and off-thread compilations are canceled on major GCs.
 template <typename T>
-class AlwaysTenured
+class CompilerGCPointer
 {
     js::gc::Cell* ptr_;
 
   public:
-    explicit AlwaysTenured(T ptr)
+    explicit CompilerGCPointer(T ptr)
       : ptr_(ptr)
     {
+        MOZ_ASSERT_IF(IsInsideNursery(ptr), IonCompilationCanUseNurseryPointers());
 #ifdef DEBUG
-        MOZ_ASSERT(!IsInsideNursery(ptr_));
         PerThreadData* pt = TlsPerThreadData.get();
         MOZ_ASSERT_IF(pt->runtimeIfOnOwnerThread(), pt->suppressGC);
 #endif
     }
 
     operator T() const { return static_cast<T>(ptr_); }
     T operator->() const { return static_cast<T>(ptr_); }
 
   private:
-    AlwaysTenured() = delete;
-    AlwaysTenured(const AlwaysTenured<T>&) = delete;
-    AlwaysTenured<T>& operator=(const AlwaysTenured<T>&) = delete;
-};
-
-typedef AlwaysTenured<JSObject*> AlwaysTenuredObject;
-typedef AlwaysTenured<NativeObject*> AlwaysTenuredNativeObject;
-typedef AlwaysTenured<JSFunction*> AlwaysTenuredFunction;
-typedef AlwaysTenured<JSScript*> AlwaysTenuredScript;
-typedef AlwaysTenured<PropertyName*> AlwaysTenuredPropertyName;
-typedef AlwaysTenured<Shape*> AlwaysTenuredShape;
+    CompilerGCPointer() = delete;
+    CompilerGCPointer(const CompilerGCPointer<T>&) = delete;
+    CompilerGCPointer<T>& operator=(const CompilerGCPointer<T>&) = delete;
+};
+
+typedef CompilerGCPointer<JSObject*> CompilerObject;
+typedef CompilerGCPointer<NativeObject*> CompilerNativeObject;
+typedef CompilerGCPointer<JSFunction*> CompilerFunction;
+typedef CompilerGCPointer<JSScript*> CompilerScript;
+typedef CompilerGCPointer<PropertyName*> CompilerPropertyName;
+typedef CompilerGCPointer<Shape*> CompilerShape;
+typedef CompilerGCPointer<ObjectGroup*> CompilerObjectGroup;
 
 class MNewArray
   : public MUnaryInstruction,
     public NoTypePolicy::Data
 {
   private:
     // Number of space to allocate for the array.
     uint32_t count_;
@@ -2949,17 +2953,17 @@ class MNewArray
         // The template object can safely be used in the recover instruction
         // because it can never be mutated by any other function execution.
         return templateObject() != nullptr;
     }
 };
 
 class MNewArrayCopyOnWrite : public MNullaryInstruction
 {
-    AlwaysTenured<ArrayObject*> templateObject_;
+    CompilerGCPointer<ArrayObject*> templateObject_;
     gc::InitialHeap initialHeap_;
 
     MNewArrayCopyOnWrite(CompilerConstraintList* constraints, ArrayObject* templateObject,
               gc::InitialHeap initialHeap)
       : templateObject_(templateObject),
         initialHeap_(initialHeap)
     {
         MOZ_ASSERT(!templateObject->isSingleton());
@@ -2990,17 +2994,17 @@ class MNewArrayCopyOnWrite : public MNul
         return AliasSet::None();
     }
 };
 
 class MNewArrayDynamicLength
   : public MUnaryInstruction,
     public IntPolicy<0>::Data
 {
-    AlwaysTenuredObject templateObject_;
+    CompilerObject templateObject_;
     gc::InitialHeap initialHeap_;
 
     MNewArrayDynamicLength(CompilerConstraintList* constraints, JSObject* templateObject,
                            gc::InitialHeap initialHeap, MDefinition* length)
       : MUnaryInstruction(length),
         templateObject_(templateObject),
         initialHeap_(initialHeap)
     {
@@ -3098,17 +3102,17 @@ class MNewObject
         // The template object can safely be used in the recover instruction
         // because it can never be mutated by any other function execution.
         return templateObject() != nullptr;
     }
 };
 
 class MNewTypedObject : public MNullaryInstruction
 {
-    AlwaysTenured<InlineTypedObject*> templateObject_;
+    CompilerGCPointer<InlineTypedObject*> templateObject_;
     gc::InitialHeap initialHeap_;
 
     MNewTypedObject(CompilerConstraintList* constraints,
                     InlineTypedObject* templateObject,
                     gc::InitialHeap initialHeap)
       : templateObject_(templateObject),
         initialHeap_(initialHeap)
     {
@@ -3173,17 +3177,17 @@ class MTypedObjectDescr
 // Generic way for constructing a SIMD object in IonMonkey, this instruction
 // takes as argument a SIMD instruction and returns a new SIMD object which
 // corresponds to the MIRType of its operand.
 class MSimdBox
   : public MUnaryInstruction,
     public NoTypePolicy::Data
 {
   protected:
-    AlwaysTenured<InlineTypedObject*> templateObject_;
+    CompilerGCPointer<InlineTypedObject*> templateObject_;
     gc::InitialHeap initialHeap_;
 
     MSimdBox(CompilerConstraintList* constraints,
              MDefinition* op,
              InlineTypedObject* templateObject,
              gc::InitialHeap initialHeap)
       : MUnaryInstruction(op),
         templateObject_(templateObject),
@@ -3526,17 +3530,17 @@ class MMutateProto
     }
 };
 
 // Slow path for adding a property to an object without a known base.
 class MInitProp
   : public MAryInstruction<2>,
     public MixPolicy<ObjectPolicy<0>, BoxPolicy<1> >::Data
 {
-    AlwaysTenuredPropertyName name_;
+    CompilerPropertyName name_;
 
   protected:
     MInitProp(MDefinition* obj, PropertyName* name, MDefinition* value)
       : name_(name)
     {
         initOperand(0, obj);
         initOperand(1, value);
         setResultType(MIRType_None);
@@ -3566,17 +3570,17 @@ class MInitProp
         return true;
     }
 };
 
 class MInitPropGetterSetter
   : public MBinaryInstruction,
     public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
 {
-    AlwaysTenuredPropertyName name_;
+    CompilerPropertyName name_;
 
     MInitPropGetterSetter(MDefinition* obj, PropertyName* name, MDefinition* value)
       : MBinaryInstruction(obj, value),
         name_(name)
     { }
 
   public:
     INSTRUCTION_HEADER(InitPropGetterSetter)
@@ -3668,17 +3672,17 @@ class MCall
   private:
     // An MCall uses the MPrepareCall, MDefinition for the function, and
     // MPassArg instructions. They are stored in the same list.
     static const size_t FunctionOperandIndex   = 0;
     static const size_t NumNonArgumentOperands = 1;
 
   protected:
     // Monomorphic cache of single target from TI, or nullptr.
-    AlwaysTenuredFunction target_;
+    CompilerFunction target_;
 
     // Original value of argc from the bytecode.
     uint32_t numActualArgs_;
 
     // True if the call is for JSOP_NEW.
     bool construct_;
 
     bool needsArgCheck_;
@@ -3843,17 +3847,17 @@ class MArraySplice
 
 // fun.apply(self, arguments)
 class MApplyArgs
   : public MAryInstruction<3>,
     public Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, BoxPolicy<2> >::Data
 {
   protected:
     // Monomorphic cache of single target from TI, or nullptr.
-    AlwaysTenuredFunction target_;
+    CompilerFunction target_;
 
     MApplyArgs(JSFunction* target, MDefinition* fun, MDefinition* argc, MDefinition* self)
       : target_(target)
     {
         initOperand(0, fun);
         initOperand(1, argc);
         initOperand(2, self);
         setResultType(MIRType_Value);
@@ -7186,17 +7190,17 @@ class MThrowUninitializedLexical : publi
     }
 };
 
 // If not defined, set a global variable to |undefined|.
 class MDefVar
   : public MUnaryInstruction,
     public NoTypePolicy::Data
 {
-    AlwaysTenuredPropertyName name_; // Target name to be defined.
+    CompilerPropertyName name_; // Target name to be defined.
     unsigned attrs_; // Attributes to be set.
 
   private:
     MDefVar(PropertyName* name, unsigned attrs, MDefinition* scopeChain)
       : MUnaryInstruction(scopeChain),
         name_(name),
         attrs_(attrs)
     {
@@ -7224,17 +7228,17 @@ class MDefVar
         return true;
     }
 };
 
 class MDefFun
   : public MUnaryInstruction,
     public NoTypePolicy::Data
 {
-    AlwaysTenuredFunction fun_;
+    CompilerFunction fun_;
 
   private:
     MDefFun(JSFunction* fun, MDefinition* scopeChain)
       : MUnaryInstruction(scopeChain),
         fun_(fun)
     {}
 
   public:
@@ -7252,17 +7256,17 @@ class MDefFun
     }
     bool possiblyCalls() const override {
         return true;
     }
 };
 
 class MRegExp : public MNullaryInstruction
 {
-    AlwaysTenured<RegExpObject*> source_;
+    CompilerGCPointer<RegExpObject*> source_;
     bool mustClone_;
 
     MRegExp(CompilerConstraintList* constraints, RegExpObject* source, bool mustClone)
       : source_(source),
         mustClone_(mustClone)
     {
         setResultType(MIRType_Object);
         setResultTypeSet(MakeSingletonTypeSet(constraints, source));
@@ -7508,17 +7512,17 @@ class MSubstr
     }
 };
 
 struct LambdaFunctionInfo
 {
     // The functions used in lambdas are the canonical original function in
     // the script, and are immutable except for delazification. Record this
     // information while still on the main thread to avoid races.
-    AlwaysTenuredFunction fun;
+    CompilerFunction fun;
     uint16_t flags;
     uint16_t nargs;
     gc::Cell* scriptOrLazyScript;
     bool singletonType;
     bool useSingletonForClone;
 
     explicit LambdaFunctionInfo(JSFunction* fun)
       : fun(fun), flags(fun->flags()), nargs(fun->nargs()),
@@ -9025,17 +9029,17 @@ class MStoreUnboxedString
 };
 
 // Passes through an object, after ensuring it is converted from an unboxed
 // object to a native representation.
 class MConvertUnboxedObjectToNative
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
-    AlwaysTenured<ObjectGroup*> group_;
+    CompilerObjectGroup group_;
 
     explicit MConvertUnboxedObjectToNative(MDefinition* obj, ObjectGroup* group)
       : MUnaryInstruction(obj),
         group_(group)
     {
         setGuard();
         setMovable();
         setResultType(MIRType_Object);
@@ -9172,17 +9176,17 @@ class MArrayPush
     ALLOW_CLONE(MArrayPush)
 };
 
 // Array.prototype.concat on two dense arrays.
 class MArrayConcat
   : public MBinaryInstruction,
     public MixPolicy<ObjectPolicy<0>, ObjectPolicy<1> >::Data
 {
-    AlwaysTenuredObject templateObj_;
+    CompilerObject templateObj_;
     gc::InitialHeap initialHeap_;
     JSValueType unboxedType_;
 
     MArrayConcat(CompilerConstraintList* constraints, MDefinition* lhs, MDefinition* rhs,
                  JSObject* templateObj, gc::InitialHeap initialHeap, JSValueType unboxedType)
       : MBinaryInstruction(lhs, rhs),
         templateObj_(templateObj),
         initialHeap_(initialHeap),
@@ -9225,17 +9229,17 @@ class MArrayConcat
     }
 };
 
 // Array.prototype.slice on a dense array.
 class MArraySlice
   : public MTernaryInstruction,
     public Mix3Policy<ObjectPolicy<0>, IntPolicy<1>, IntPolicy<2>>::Data
 {
-    AlwaysTenuredObject templateObj_;
+    CompilerObject templateObj_;
     gc::InitialHeap initialHeap_;
     JSValueType unboxedType_;
 
     MArraySlice(CompilerConstraintList* constraints, MDefinition* obj,
                 MDefinition* begin, MDefinition* end,
                 JSObject* templateObj, gc::InitialHeap initialHeap, JSValueType unboxedType)
       : MTernaryInstruction(obj, begin, end),
         templateObj_(templateObj),
@@ -9519,17 +9523,18 @@ class MLoadTypedArrayElementStatic
         if (type == Scalar::Float32)
             setResultType(MIRType_Float32);
         else if (type == Scalar::Float64)
             setResultType(MIRType_Double);
         else
             setResultType(MIRType_Int32);
     }
 
-    AlwaysTenured<JSObject*> someTypedArray_;
+    CompilerObject someTypedArray_;
+
     // An offset to be encoded in the load instruction - taking advantage of the
     // addressing modes. This is only non-zero when the access is proven to be
     // within bounds.
     int32_t offset_;
     bool needsBoundsCheck_;
     bool fallible_;
 
   public:
@@ -9765,17 +9770,17 @@ class MStoreTypedArrayElementStatic :
     MStoreTypedArrayElementStatic(JSObject* someTypedArray, MDefinition* ptr, MDefinition* v,
                                   int32_t offset, bool needsBoundsCheck)
         : MBinaryInstruction(ptr, v),
           StoreUnboxedScalarBase(AnyTypedArrayType(someTypedArray)),
           someTypedArray_(someTypedArray),
           offset_(offset), needsBoundsCheck_(needsBoundsCheck)
     {}
 
-    AlwaysTenured<JSObject*> someTypedArray_;
+    CompilerObject someTypedArray_;
 
     // An offset to be encoded in the store instruction - taking advantage of the
     // addressing modes. This is only non-zero when the access is proven to be
     // within bounds.
     int32_t offset_;
     bool needsBoundsCheck_;
 
   public:
@@ -9985,18 +9990,18 @@ class MStoreFixedSlot
 };
 
 typedef Vector<JSObject*, 4, JitAllocPolicy> ObjectVector;
 typedef Vector<bool, 4, JitAllocPolicy> BoolVector;
 
 class InlinePropertyTable : public TempObject
 {
     struct Entry : public TempObject {
-        AlwaysTenured<ObjectGroup*> group;
-        AlwaysTenuredFunction func;
+        CompilerObjectGroup group;
+        CompilerFunction func;
 
         Entry(ObjectGroup* group, JSFunction* func)
           : group(group), func(func)
         { }
     };
 
     jsbytecode* pc_;
     MResumePoint* priorResumePoint_;
@@ -10062,17 +10067,17 @@ class CacheLocationList : public InlineC
     jsbytecode* pc;
     JSScript* script;
 };
 
 class MGetPropertyCache
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
-    AlwaysTenuredPropertyName name_;
+    CompilerPropertyName name_;
     bool idempotent_;
     bool monitoredResult_;
 
     CacheLocationList location_;
 
     InlinePropertyTable* inlinePropertyTable_;
 
     MGetPropertyCache(MDefinition* obj, PropertyName* name, bool monitoredResult)
@@ -10166,17 +10171,17 @@ class MGetPropertyPolymorphic
         // The group and/or shape to guard against.
         ReceiverGuard receiver;
 
         // The property to load, null for loads from unboxed properties.
         Shape* shape;
     };
 
     Vector<Entry, 4, JitAllocPolicy> receivers_;
-    AlwaysTenuredPropertyName name_;
+    CompilerPropertyName name_;
 
     MGetPropertyPolymorphic(TempAllocator& alloc, MDefinition* obj, PropertyName* name)
       : MUnaryInstruction(obj),
         receivers_(alloc),
         name_(name)
     {
         setGuard();
         setMovable();
@@ -10246,17 +10251,17 @@ class MSetPropertyPolymorphic
         // The group and/or shape to guard against.
         ReceiverGuard receiver;
 
         // The property to store, null for stores to unboxed properties.
         Shape* shape;
     };
 
     Vector<Entry, 4, JitAllocPolicy> receivers_;
-    AlwaysTenuredPropertyName name_;
+    CompilerPropertyName name_;
     bool needsBarrier_;
 
     MSetPropertyPolymorphic(TempAllocator& alloc, MDefinition* obj, MDefinition* value,
                             PropertyName* name)
       : MBinaryInstruction(obj, value),
         receivers_(alloc),
         name_(name),
         needsBarrier_(false)
@@ -10509,18 +10514,18 @@ class MGetElementCache
 
     bool allowDoubleResult() const;
 };
 
 class MBindNameCache
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
-    AlwaysTenuredPropertyName name_;
-    AlwaysTenuredScript script_;
+    CompilerPropertyName name_;
+    CompilerScript script_;
     jsbytecode* pc_;
 
     MBindNameCache(MDefinition* scopeChain, PropertyName* name, JSScript* script, jsbytecode* pc)
       : MUnaryInstruction(scopeChain), name_(name), script_(script), pc_(pc)
     {
         setResultType(MIRType_Object);
     }
 
@@ -10547,17 +10552,17 @@ class MBindNameCache
     }
 };
 
 // Guard on an object's shape.
 class MGuardShape
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
-    AlwaysTenuredShape shape_;
+    CompilerShape shape_;
     BailoutKind bailoutKind_;
 
     MGuardShape(MDefinition* obj, Shape* shape, BailoutKind bailoutKind)
       : MUnaryInstruction(obj),
         shape_(shape),
         bailoutKind_(bailoutKind)
     {
         setGuard();
@@ -10648,17 +10653,17 @@ class MGuardReceiverPolymorphic
     }
 };
 
 // Guard on an object's group, inclusively or exclusively.
 class MGuardObjectGroup
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
-    AlwaysTenured<ObjectGroup*> group_;
+    CompilerObjectGroup group_;
     bool bailOnEquality_;
     BailoutKind bailoutKind_;
 
     MGuardObjectGroup(MDefinition* obj, ObjectGroup* group, bool bailOnEquality,
                       BailoutKind bailoutKind)
       : MUnaryInstruction(obj),
         group_(group),
         bailOnEquality_(bailOnEquality),
@@ -11023,17 +11028,17 @@ class MGetNameCache
 {
   public:
     enum AccessKind {
         NAMETYPEOF,
         NAME
     };
 
   private:
-    AlwaysTenuredPropertyName name_;
+    CompilerPropertyName name_;
     AccessKind kind_;
 
     MGetNameCache(MDefinition* obj, PropertyName* name, AccessKind kind)
       : MUnaryInstruction(obj),
         name_(name),
         kind_(kind)
     {
         setResultType(MIRType_Value);
@@ -11055,17 +11060,17 @@ class MGetNameCache
     }
     AccessKind accessKind() const {
         return kind_;
     }
 };
 
 class MCallGetIntrinsicValue : public MNullaryInstruction
 {
-    AlwaysTenuredPropertyName name_;
+    CompilerPropertyName name_;
 
     explicit MCallGetIntrinsicValue(PropertyName* name)
       : name_(name)
     {
         setResultType(MIRType_Value);
     }
 
   public:
@@ -11082,17 +11087,17 @@ class MCallGetIntrinsicValue : public MN
     }
     bool possiblyCalls() const override {
         return true;
     }
 };
 
 class MSetPropertyInstruction : public MBinaryInstruction
 {
-    AlwaysTenuredPropertyName name_;
+    CompilerPropertyName name_;
     bool strict_;
     bool needsBarrier_;
 
   protected:
     MSetPropertyInstruction(MDefinition* obj, MDefinition* value, PropertyName* name,
                             bool strict)
       : MBinaryInstruction(obj, value),
         name_(name), strict_(strict), needsBarrier_(true)
@@ -11144,17 +11149,17 @@ class MSetElementInstruction
         return strict_;
     }
 };
 
 class MDeleteProperty
   : public MUnaryInstruction,
     public BoxInputsPolicy::Data
 {
-    AlwaysTenuredPropertyName name_;
+    CompilerPropertyName name_;
     bool strict_;
 
   protected:
     MDeleteProperty(MDefinition* val, PropertyName* name, bool strict)
       : MUnaryInstruction(val),
         name_(name),
         strict_(strict)
     {
@@ -11292,17 +11297,17 @@ class MSetElementCache
 
     bool canConsumeFloat32(MUse* use) const override { return use == getUseFor(2); }
 };
 
 class MCallGetProperty
   : public MUnaryInstruction,
     public BoxInputsPolicy::Data
 {
-    AlwaysTenuredPropertyName name_;
+    CompilerPropertyName name_;
     bool idempotent_;
     bool callprop_;
 
     MCallGetProperty(MDefinition* value, PropertyName* name, bool callprop)
       : MUnaryInstruction(value), name_(name),
         idempotent_(false),
         callprop_(callprop)
     {
@@ -11991,17 +11996,17 @@ class MInArray
     }
 };
 
 // Implementation for instanceof operator with specific rhs.
 class MInstanceOf
   : public MUnaryInstruction,
     public InstanceOfPolicy::Data
 {
-    AlwaysTenuredObject protoObj_;
+    CompilerObject protoObj_;
 
     MInstanceOf(MDefinition* obj, JSObject* proto)
       : MUnaryInstruction(obj),
         protoObj_(proto)
     {
         setResultType(MIRType_Boolean);
     }
 
@@ -12164,17 +12169,17 @@ class MSetFrameArgument
     AliasSet getAliasSet() const override {
         return AliasSet::Store(AliasSet::FrameArgument);
     }
 };
 
 class MRestCommon
 {
     unsigned numFormals_;
-    AlwaysTenured<ArrayObject*> templateObject_;
+    CompilerGCPointer<ArrayObject*> templateObject_;
 
   protected:
     MRestCommon(unsigned numFormals, ArrayObject* templateObject)
       : numFormals_(numFormals),
         templateObject_(templateObject)
    { }
 
   public:
@@ -12388,17 +12393,17 @@ class MPostWriteBarrier : public MBinary
     }
 #endif
 
     ALLOW_CLONE(MPostWriteBarrier)
 };
 
 class MNewDeclEnvObject : public MNullaryInstruction
 {
-    AlwaysTenured<DeclEnvObject*> templateObj_;
+    CompilerGCPointer<DeclEnvObject*> templateObj_;
 
     explicit MNewDeclEnvObject(DeclEnvObject* templateObj)
       : MNullaryInstruction(),
         templateObj_(templateObj)
     {
         setResultType(MIRType_Object);
     }
 
@@ -12414,17 +12419,17 @@ class MNewDeclEnvObject : public MNullar
     }
     AliasSet getAliasSet() const override {
         return AliasSet::None();
     }
 };
 
 class MNewCallObjectBase : public MNullaryInstruction
 {
-    AlwaysTenured<CallObject*> templateObj_;
+    CompilerGCPointer<CallObject*> templateObj_;
 
   protected:
     explicit MNewCallObjectBase(CallObject* templateObj)
       : MNullaryInstruction(),
         templateObj_(templateObj)
     {
         setResultType(MIRType_Object);
     }
@@ -12469,17 +12474,17 @@ class MNewRunOnceCallObject : public MNe
         return new(alloc) MNewRunOnceCallObject(templateObj);
     }
 };
 
 class MNewStringObject :
   public MUnaryInstruction,
   public ConvertToStringPolicy<0>::Data
 {
-    AlwaysTenuredObject templateObj_;
+    CompilerObject templateObj_;
 
     MNewStringObject(MDefinition* input, JSObject* templateObj)
       : MUnaryInstruction(input),
         templateObj_(templateObj)
     {
         setResultType(MIRType_Object);
     }
 
@@ -13605,17 +13610,17 @@ BarrierKind PropertyReadOnPrototypeNeeds
                                                     TemporaryTypeSet* observed);
 bool PropertyReadIsIdempotent(CompilerConstraintList* constraints,
                               MDefinition* obj, PropertyName* name);
 void AddObjectsForPropertyRead(MDefinition* obj, PropertyName* name,
                                TemporaryTypeSet* observed);
 bool CanWriteProperty(TempAllocator& alloc, CompilerConstraintList* constraints,
                       HeapTypeSetKey property, MDefinition* value,
                       MIRType implicitType = MIRType_None);
-bool PropertyWriteNeedsTypeBarrier(MIRGenerator* gen, CompilerConstraintList* constraints,
+bool PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList* constraints,
                                    MBasicBlock* current, MDefinition** pobj,
                                    PropertyName* name, MDefinition** pvalue,
                                    bool canModify, MIRType implicitType = MIRType_None);
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_MIR_h */
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -112,17 +112,19 @@ MResumePoint::writeRecoverData(CompactBu
 #endif
 
     // Test if we honor the maximum of arguments at all times.  This is a sanity
     // check and not an algorithm limit. So check might be a bit too loose.  +4
     // to account for scope chain, return value, this value and maybe
     // arguments_object.
     MOZ_ASSERT(CountArgSlots(script, fun) < SNAPSHOT_MAX_NARGS + 4);
 
+#ifdef DEBUG
     uint32_t implicit = StartArgSlot(script);
+#endif
     uint32_t formalArgs = CountArgSlots(script, fun);
     uint32_t nallocs = formalArgs + script->nfixed() + exprStack;
 
     JitSpew(JitSpew_IonSnapshots, "Starting frame; implicit %u, formals %u, fixed %u, exprs %u",
             implicit, formalArgs - implicit, script->nfixed(), exprStack);
 
     uint32_t pcoff = script->pcToOffset(pc());
     JitSpew(JitSpew_IonSnapshots, "Writing pc offset %u, nslots %u", pcoff, nallocs);
--- a/js/src/jsapi-tests/testGCStoreBufferRemoval.cpp
+++ b/js/src/jsapi-tests/testGCStoreBufferRemoval.cpp
@@ -27,64 +27,64 @@ BEGIN_TEST(testGCStoreBufferRemoval)
     CHECK(js::gc::IsInsideNursery(obj.get()));
     JS_GC(cx->runtime());
     CHECK(!js::gc::IsInsideNursery(obj.get()));
     JS::RootedObject tenuredObject(cx, obj);
 
     // Hide the horrors herein from the static rooting analysis.
     AutoIgnoreRootingHazards ignore;
 
-    // Test removal of store buffer entries added by HeapPtr<T>.
+    // Test removal of store buffer entries added by RelocatablePtr<T>.
     {
         JSObject* badObject = reinterpret_cast<JSObject*>(1);
         JSObject* punnedPtr = nullptr;
-        HeapPtrObject* relocPtr =
-            reinterpret_cast<HeapPtrObject*>(&punnedPtr);
-        new (relocPtr) HeapPtrObject;
+        RelocatablePtrObject* relocPtr =
+            reinterpret_cast<RelocatablePtrObject*>(&punnedPtr);
+        new (relocPtr) RelocatablePtrObject;
         *relocPtr = NurseryObject();
-        relocPtr->~HeapPtrObject();
+        relocPtr->~RelocatablePtrObject();
         punnedPtr = badObject;
         JS_GC(cx->runtime());
 
-        new (relocPtr) HeapPtrObject;
+        new (relocPtr) RelocatablePtrObject;
         *relocPtr = NurseryObject();
         *relocPtr = tenuredObject;
-        relocPtr->~HeapPtrObject();
+        relocPtr->~RelocatablePtrObject();
         punnedPtr = badObject;
         JS_GC(cx->runtime());
 
-        new (relocPtr) HeapPtrObject;
+        new (relocPtr) RelocatablePtrObject;
         *relocPtr = NurseryObject();
         *relocPtr = nullptr;
-        relocPtr->~HeapPtrObject();
+        relocPtr->~RelocatablePtrObject();
         punnedPtr = badObject;
         JS_GC(cx->runtime());
     }
 
-    // Test removal of store buffer entries added by HeapValue.
+    // Test removal of store buffer entries added by RelocatableValue.
     {
         Value punnedValue;
-        HeapValue* relocValue = reinterpret_cast<HeapValue*>(&punnedValue);
-        new (relocValue) HeapValue;
+        RelocatableValue* relocValue = reinterpret_cast<RelocatableValue*>(&punnedValue);
+        new (relocValue) RelocatableValue;
         *relocValue = ObjectValue(*NurseryObject());
-        relocValue->~HeapValue();
+        relocValue->~RelocatableValue();
         punnedValue = ObjectValueCrashOnTouch();
         JS_GC(cx->runtime());
 
-        new (relocValue) HeapValue;
+        new (relocValue) RelocatableValue;
         *relocValue = ObjectValue(*NurseryObject());
         *relocValue = ObjectValue(*tenuredObject);
-        relocValue->~HeapValue();
+        relocValue->~RelocatableValue();
         punnedValue = ObjectValueCrashOnTouch();
         JS_GC(cx->runtime());
 
-        new (relocValue) HeapValue;
+        new (relocValue) RelocatableValue;
         *relocValue = ObjectValue(*NurseryObject());
         *relocValue = NullValue();
-        relocValue->~HeapValue();
+        relocValue->~RelocatableValue();
         punnedValue = ObjectValueCrashOnTouch();
         JS_GC(cx->runtime());
     }
 
     // Test removal of store buffer entries added by Heap<T>.
     {
         JSObject* badObject = reinterpret_cast<JSObject*>(1);
         JSObject* punnedPtr = nullptr;
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -1196,33 +1196,16 @@ js::GetObjectMetadata(JSObject* obj)
 {
     ObjectWeakMap* map = obj->compartment()->objectMetadataTable;
     if (map)
         return map->lookup(obj);
     return nullptr;
 }
 
 JS_FRIEND_API(bool)
-js::DefineOwnProperty(JSContext* cx, JSObject* objArg, jsid idArg,
-                      JS::Handle<js::PropertyDescriptor> descriptor, ObjectOpResult& result)
-{
-    RootedObject obj(cx, objArg);
-    RootedId id(cx, idArg);
-    AssertHeapIsIdle(cx);
-    CHECK_REQUEST(cx);
-    assertSameCompartment(cx, obj, id, descriptor.value());
-    if (descriptor.hasGetterObject())
-        assertSameCompartment(cx, descriptor.getterObject());
-    if (descriptor.hasSetterObject())
-        assertSameCompartment(cx, descriptor.setterObject());
-
-    return StandardDefineProperty(cx, obj, id, descriptor, result);
-}
-
-JS_FRIEND_API(bool)
 js::ReportIsNotFunction(JSContext* cx, HandleValue v)
 {
     return ReportIsNotFunction(cx, v, -1);
 }
 
 JS_FRIEND_API(void)
 js::ReportErrorWithId(JSContext* cx, const char* msg, HandleId id)
 {
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -2808,20 +2808,16 @@ GetSavedFramePrincipals(JS::HandleObject
  * The savedFrame and cx do not need to be in the same compartment.
  */
 extern JS_FRIEND_API(JSObject*)
 GetFirstSubsumedSavedFrame(JSContext* cx, JS::HandleObject savedFrame);
 
 extern JS_FRIEND_API(bool)
 ReportIsNotFunction(JSContext* cx, JS::HandleValue v);
 
-extern JS_FRIEND_API(bool)
-DefineOwnProperty(JSContext* cx, JSObject* objArg, jsid idArg,
-                  JS::Handle<JSPropertyDescriptor> descriptor, JS::ObjectOpResult& result);
-
 extern JS_FRIEND_API(JSObject*)
 ConvertArgsToArray(JSContext* cx, const JS::CallArgs& args);
 
 } /* namespace js */
 
 extern JS_FRIEND_API(void)
 JS_StoreObjectPostBarrierCallback(JSContext* cx,
                                   void (*callback)(JSTracer* trc, JSObject* key, void* data),
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -269,425 +269,17 @@ js::Throw(JSContext* cx, JSObject* obj, 
     } else {
         MOZ_ASSERT(js_ErrorFormatString[errorNumber].argCount == 0);
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, errorNumber);
     }
     return false;
 }
 
 
-/*** Standard-compliant property definition (used by Object.defineProperty) **********************/
-
-static bool
-DefinePropertyOnObject(JSContext* cx, HandleNativeObject obj, HandleId id,
-                       Handle<PropertyDescriptor> desc, ObjectOpResult& result)
-{
-    /* 8.12.9 step 1. */
-    RootedShape shape(cx);
-    MOZ_ASSERT(!obj->getOps()->lookupProperty);
-    if (!NativeLookupOwnProperty<CanGC>(cx, obj, id, &shape))
-        return false;
-
-    MOZ_ASSERT(!obj->getOps()->defineProperty);
-
-    /* 8.12.9 steps 2-4. */
-    if (!shape) {
-        bool extensible;
-        if (!IsExtensible(cx, obj, &extensible))
-            return false;
-        if (!extensible)
-            return result.fail(JSMSG_OBJECT_NOT_EXTENSIBLE);
-
-        if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
-            MOZ_ASSERT(!obj->getOps()->defineProperty);
-            RootedValue v(cx, desc.hasValue() ? desc.value() : UndefinedValue());
-            unsigned attrs = desc.attributes() & (JSPROP_PERMANENT | JSPROP_ENUMERATE | JSPROP_READONLY);
-
-            if (!desc.hasConfigurable())
-                attrs |= JSPROP_PERMANENT;
-            if (!desc.hasWritable())
-                attrs |= JSPROP_READONLY;
-            return NativeDefineProperty(cx, obj, id, v, nullptr, nullptr, attrs, result);
-        }
-
-        MOZ_ASSERT(desc.isAccessorDescriptor());
-
-        unsigned attrs = desc.attributes() & (JSPROP_PERMANENT | JSPROP_ENUMERATE |
-                                              JSPROP_SHARED | JSPROP_GETTER | JSPROP_SETTER);
-        if (!desc.hasConfigurable())
-            attrs |= JSPROP_PERMANENT;
-        return NativeDefineProperty(cx, obj, id, UndefinedHandleValue,
-                                    desc.getter(), desc.setter(), attrs, result);
-    }
-
-    /* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */
-    RootedValue v(cx);
-
-    bool shapeDataDescriptor = true,
-         shapeAccessorDescriptor = false,
-         shapeWritable = true,
-         shapeConfigurable = true,
-         shapeEnumerable = true,
-         shapeHasDefaultGetter = true,
-         shapeHasDefaultSetter = true,
-         shapeHasGetterValue = false,
-         shapeHasSetterValue = false;
-    uint8_t shapeAttributes = GetShapeAttributes(obj, shape);
-    if (!IsImplicitDenseOrTypedArrayElement(shape)) {
-        shapeDataDescriptor = shape->isDataDescriptor();
-        shapeAccessorDescriptor = shape->isAccessorDescriptor();
-        shapeWritable = shape->writable();
-        shapeConfigurable = shape->configurable();
-        shapeEnumerable = shape->enumerable();
-        shapeHasDefaultGetter = shape->hasDefaultGetter();
-        shapeHasDefaultSetter = shape->hasDefaultSetter();
-        shapeHasGetterValue = shape->hasGetterValue();
-        shapeHasSetterValue = shape->hasSetterValue();
-        shapeAttributes = shape->attributes();
-    }
-
-    do {
-        if (desc.isAccessorDescriptor()) {
-            if (!shapeAccessorDescriptor)
-                break;
-
-            if (desc.hasGetterObject()) {
-                if (!shape->hasGetterValue() || desc.getterObject() != shape->getterObject())
-                    break;
-            }
-
-            if (desc.hasSetterObject()) {
-                if (!shape->hasSetterValue() || desc.setterObject() != shape->setterObject())
-                    break;
-            }
-        } else {
-            /*
-             * Determine the current value of the property once, if the current
-             * value might actually need to be used or preserved later.  NB: we
-             * guard on whether the current property is a data descriptor to
-             * avoid calling a getter; we won't need the value if it's not a
-             * data descriptor.
-             */
-            if (IsImplicitDenseOrTypedArrayElement(shape)) {
-                v = obj->getDenseOrTypedArrayElement(JSID_TO_INT(id));
-            } else if (shape->isDataDescriptor()) {
-                /*
-                 * We must rule out a non-configurable js::SetterOp-guarded
-                 * property becoming a writable unguarded data property, since
-                 * such a property can have its value changed to one the getter
-                 * and setter preclude.
-                 *
-                 * A desc lacking writable but with value is a data descriptor
-                 * and we must reject it as if it had writable: true if current
-                 * is writable.
-                 */
-                if (!shape->configurable() &&
-                    (!shape->hasDefaultGetter() || !shape->hasDefaultSetter()) &&
-                    desc.isDataDescriptor() &&
-                    (desc.hasWritable() ? desc.writable() : shape->writable()))
-                {
-                    return result.fail(JSMSG_CANT_REDEFINE_PROP);
-                }
-
-                if (!NativeGetExistingProperty(cx, obj, obj, shape, &v))
-                    return false;
-            }
-
-            if (desc.isDataDescriptor()) {
-                if (!shapeDataDescriptor)
-                    break;
-
-                bool same;
-                if (desc.hasValue()) {
-                    if (!SameValue(cx, desc.value(), v, &same))
-                        return false;
-                    if (!same) {
-                        /*
-                         * Insist that a non-configurable js::GetterOp data
-                         * property is frozen at exactly the last-got value.
-                         *
-                         * Duplicate the first part of the big conjunction that
-                         * we tested above, rather than add a local bool flag.
-                         * Likewise, don't try to keep shape->writable() in a
-                         * flag we veto from true to false for non-configurable
-                         * GetterOp-based data properties and test before the
-                         * SameValue check later on in order to re-use that "if
-                         * (!SameValue) return false" logic.
-                         *
-                         * This function is large and complex enough that it
-                         * seems best to repeat a small bit of code and return
-                         * result.fail() ASAP, instead of being clever.
-                         */
-                        if (!shapeConfigurable &&
-                            (!shape->hasDefaultGetter() || !shape->hasDefaultSetter()))
-                        {
-                            return result.fail(JSMSG_CANT_REDEFINE_PROP);
-                        }
-                        break;
-                    }
-                }
-                if (desc.hasWritable() && desc.writable() != shapeWritable)
-                    break;
-            } else {
-                /* The only fields in desc will be handled below. */
-                MOZ_ASSERT(desc.isGenericDescriptor());
-            }
-        }
-
-        if (desc.hasConfigurable() && desc.configurable() != shapeConfigurable)
-            break;
-        if (desc.hasEnumerable() && desc.enumerable() != shapeEnumerable)
-            break;
-
-        /* The conditions imposed by step 5 or step 6 apply. */
-        return result.succeed();
-    } while (0);
-
-    /* 8.12.9 step 7. */
-    if (!shapeConfigurable) {
-        if ((desc.hasConfigurable() && desc.configurable()) ||
-            (desc.hasEnumerable() && desc.enumerable() != shape->enumerable())) {
-            return result.fail(JSMSG_CANT_REDEFINE_PROP);
-        }
-    }
-
-    bool callDelProperty = false;
-
-    if (desc.isGenericDescriptor()) {
-        /* 8.12.9 step 8, no validation required */
-    } else if (desc.isDataDescriptor() != shapeDataDescriptor) {
-        /* 8.12.9 step 9. */
-        if (!shapeConfigurable)
-            return result.fail(JSMSG_CANT_REDEFINE_PROP);
-    } else if (desc.isDataDescriptor()) {
-        /* 8.12.9 step 10. */
-        MOZ_ASSERT(shapeDataDescriptor);
-        if (!shapeConfigurable && !shape->writable()) {
-            if (desc.hasWritable() && desc.writable())
-                return result.fail(JSMSG_CANT_REDEFINE_PROP);
-            if (desc.hasValue()) {
-                bool same;
-                if (!SameValue(cx, desc.value(), v, &same))
-                    return false;
-                if (!same)
-                    return result.fail(JSMSG_CANT_REDEFINE_PROP);
-            }
-        }
-
-        callDelProperty = !shapeHasDefaultGetter || !shapeHasDefaultSetter;
-    } else {
-        /* 8.12.9 step 11. */
-        MOZ_ASSERT(desc.isAccessorDescriptor() && shape->isAccessorDescriptor());
-        if (!shape->configurable()) {
-            // The hasSetterValue() and hasGetterValue() calls below ought to
-            // be redundant here, because accessor shapes should always have
-            // both JSPROP_GETTER and JSPROP_SETTER. But this is not the case
-            // currently; in particular Object.defineProperty(obj, key, {get: fn})
-            // creates a property without JSPROP_SETTER (bug 1133315).
-            if (desc.hasSetterObject() &&
-                desc.setterObject() != (shape->hasSetterValue() ? shape->setterObject() : nullptr))
-            {
-                return result.fail(JSMSG_CANT_REDEFINE_PROP);
-            }
-
-            if (desc.hasGetterObject() &&
-                desc.getterObject() != (shape->hasGetterValue() ? shape->getterObject() : nullptr))
-            {
-                return result.fail(JSMSG_CANT_REDEFINE_PROP);
-            }
-        }
-    }
-
-    /* 8.12.9 step 12. */
-    unsigned attrs;
-    GetterOp getter;
-    SetterOp setter;
-    if (desc.isGenericDescriptor()) {
-        unsigned changed = 0;
-        if (desc.hasConfigurable())
-            changed |= JSPROP_PERMANENT;
-        if (desc.hasEnumerable())
-            changed |= JSPROP_ENUMERATE;
-
-        attrs = (shapeAttributes & ~changed) | (desc.attributes() & changed);
-        getter = IsImplicitDenseOrTypedArrayElement(shape) ? nullptr : shape->getter();
-        setter = IsImplicitDenseOrTypedArrayElement(shape) ? nullptr : shape->setter();
-    } else if (desc.isDataDescriptor()) {
-        /* Watch out for accessor -> data transformations here. */
-        unsigned changed = JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED;
-        unsigned descAttrs = desc.attributes();
-        if (desc.hasConfigurable())
-            changed |= JSPROP_PERMANENT;
-        if (desc.hasEnumerable())
-            changed |= JSPROP_ENUMERATE;
-
-        if (desc.hasWritable()) {
-            changed |= JSPROP_READONLY;
-        } else if (!shapeDataDescriptor) {
-            changed |= JSPROP_READONLY;
-            descAttrs |= JSPROP_READONLY;
-        }
-
-        if (desc.hasValue())
-            v = desc.value();
-        attrs = (descAttrs & changed) | (shapeAttributes & ~changed);
-        getter = nullptr;
-        setter = nullptr;
-    } else {
-        MOZ_ASSERT(desc.isAccessorDescriptor());
-
-        /* 8.12.9 step 12. */
-        unsigned changed = 0;
-        if (desc.hasConfigurable())
-            changed |= JSPROP_PERMANENT;
-        if (desc.hasEnumerable())
-            changed |= JSPROP_ENUMERATE;
-        if (desc.hasGetterObject())
-            changed |= JSPROP_GETTER | JSPROP_SHARED | JSPROP_READONLY | JSPROP_SHADOWABLE;
-        if (desc.hasSetterObject())
-            changed |= JSPROP_SETTER | JSPROP_SHARED | JSPROP_READONLY | JSPROP_SHADOWABLE;
-
-        attrs = (desc.attributes() & changed) | (shapeAttributes & ~changed);
-        if (desc.hasGetterObject())
-            getter = desc.getter();
-        else
-            getter = shapeHasGetterValue ? shape->getter() : nullptr;
-        if (desc.hasSetterObject())
-            setter = desc.setter();
-        else
-            setter = shapeHasSetterValue ? shape->setter() : nullptr;
-    }
-
-    /*
-     * Since "data" properties implemented using native C functions may rely on
-     * side effects during setting, we must make them aware that they have been
-     * "assigned"; deleting the property before redefining it does the trick.
-     * See bug 539766, where we ran into problems when we redefined
-     * arguments.length without making the property aware that its value had
-     * been changed (which would have happened if we had deleted it before
-     * redefining it or we had invoked its setter to change its value).
-     */
-    if (callDelProperty) {
-        ObjectOpResult ignored;
-        if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, ignored))
-            return false;
-    }
-
-    return NativeDefineProperty(cx, obj, id, v, getter, setter, attrs, result);
-}
-
-/* ES6 20130308 draft 8.4.2.1 [[DefineOwnProperty]] */
-static bool
-DefinePropertyOnArray(JSContext* cx, Handle<ArrayObject*> arr, HandleId id,
-                      Handle<PropertyDescriptor> desc, ObjectOpResult& result)
-{
-    /* Step 2. */
-    if (id == NameToId(cx->names().length)) {
-        // Canonicalize value, if necessary, before proceeding any further.  It
-        // would be better if this were always/only done by ArraySetLength.
-        // But canonicalization may throw a RangeError (or other exception, if
-        // the value is an object with user-defined conversion semantics)
-        // before other attributes are checked.  So as long as our internal
-        // defineProperty hook doesn't match the ECMA one, this duplicate
-        // checking can't be helped.
-        RootedValue v(cx);
-        if (desc.hasValue()) {
-            uint32_t newLen;
-            if (!CanonicalizeArrayLengthValue(cx, desc.value(), &newLen))
-                return false;
-            v.setNumber(newLen);
-        } else {
-            v.setNumber(arr->length());
-        }
-
-        if (desc.hasConfigurable() && desc.configurable())
-            return result.fail(JSMSG_CANT_REDEFINE_PROP);
-        if (desc.hasEnumerable() && desc.enumerable())
-            return result.fail(JSMSG_CANT_REDEFINE_PROP);
-
-        if (desc.isAccessorDescriptor())
-            return result.fail(JSMSG_CANT_REDEFINE_PROP);
-
-        unsigned attrs = arr->lookup(cx, id)->attributes();
-        if (!arr->lengthIsWritable()) {
-            if (desc.hasWritable() && desc.writable())
-                return result.fail(JSMSG_CANT_REDEFINE_PROP);
-        } else {
-            if (desc.hasWritable() && !desc.writable())
-                attrs = attrs | JSPROP_READONLY;
-        }
-
-        return ArraySetLength(cx, arr, id, attrs, v, result);
-    }
-
-    /* Step 3. */
-    uint32_t index;
-    if (IdIsIndex(id, &index)) {
-        /* Step 3b. */
-        uint32_t oldLen = arr->length();
-
-        /* Steps 3a, 3e. */
-        if (index >= oldLen && !arr->lengthIsWritable())
-            return result.fail(JSMSG_CANT_APPEND_TO_ARRAY);
-
-        /* Steps 3f-j. */
-        return DefinePropertyOnObject(cx, arr, id, desc, result);
-    }
-
-    /* Step 4. */
-    return DefinePropertyOnObject(cx, arr, id, desc, result);
-}
-
-// ES6 draft rev31 9.4.5.3 [[DefineOwnProperty]]
-static bool
-DefinePropertyOnTypedArray(JSContext* cx, HandleObject obj, HandleId id,
-                           Handle<PropertyDescriptor> desc, ObjectOpResult& result)
-{
-    MOZ_ASSERT(IsAnyTypedArray(obj));
-    // Steps 3.a-c.
-    uint64_t index;
-    if (IsTypedArrayIndex(id, &index))
-        return DefineTypedArrayElement(cx, obj, index, desc, result);
-
-    // Step 4.
-    return DefinePropertyOnObject(cx, obj.as<NativeObject>(), id, desc, result);
-}
-
-bool
-js::StandardDefineProperty(JSContext* cx, HandleObject obj, HandleId id,
-                           Handle<PropertyDescriptor> desc, ObjectOpResult& result)
-{
-    if (obj->is<ArrayObject>()) {
-        Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
-        return DefinePropertyOnArray(cx, arr, id, desc, result);
-    }
-
-    if (IsAnyTypedArray(obj))
-        return DefinePropertyOnTypedArray(cx, obj, id, desc, result);
-
-    if (obj->is<ProxyObject>()) {
-        Rooted<PropertyDescriptor> pd(cx, desc);
-        pd.object().set(obj);
-        return Proxy::defineProperty(cx, obj, id, pd, result);
-    }
-
-    if (obj->getOps()->defineProperty)
-        return obj->getOps()->defineProperty(cx, obj, id, desc, result);
-
-    return DefinePropertyOnObject(cx, obj.as<NativeObject>(), id, desc, result);
-}
-
-bool
-js::StandardDefineProperty(JSContext* cx, HandleObject obj, HandleId id,
-                           Handle<PropertyDescriptor> desc)
-{
-    ObjectOpResult success;
-    return StandardDefineProperty(cx, obj, id, desc, success) &&
-           success.checkStrict(cx, obj, id);
-}
+/*** PropertyDescriptor operations and DefineProperties ******************************************/
 
 bool
 CheckCallable(JSContext* cx, JSObject* obj, const char* fieldName)
 {
     if (obj && !obj->isCallable()) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
                              fieldName);
         return false;
@@ -874,23 +466,24 @@ bool
 js::DefineProperties(JSContext* cx, HandleObject obj, HandleObject props)
 {
     AutoIdVector ids(cx);
     AutoPropertyDescriptorVector descs(cx);
     if (!ReadPropertyDescriptors(cx, props, true, &ids, &descs))
         return false;
 
     for (size_t i = 0, len = ids.length(); i < len; i++) {
-        if (!StandardDefineProperty(cx, obj, ids[i], descs[i]))
+        if (!DefineProperty(cx, obj, ids[i], descs[i]))
             return false;
     }
 
     return true;
 }
 
+
 /*** Seal and freeze *****************************************************************************/
 
 static unsigned
 GetSealedOrFrozenAttributes(unsigned attrs, IntegrityLevel level)
 {
     /* Make all attributes permanent; if freezing, make data attributes read-only. */
     if (level == IntegrityLevel::Frozen && !(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
         return JSPROP_PERMANENT | JSPROP_READONLY;
@@ -983,25 +576,25 @@ js::SetIntegrityLevel(JSContext* cx, Han
                 // 9.a.iii.1-2
                 if (currentDesc.isAccessorDescriptor())
                     desc.setAttributes(AllowConfigure | JSPROP_PERMANENT);
                 else
                     desc.setAttributes(AllowConfigureAndWritable | JSPROP_PERMANENT | JSPROP_READONLY);
             }
 
             // 8.a.i-ii. / 9.a.iii.3-4
-            if (!StandardDefineProperty(cx, obj, id, desc))
+            if (!DefineProperty(cx, obj, id, desc))
                 return false;
         }
     }
 
     // Ordinarily ArraySetLength handles this, but we're going behind its back
     // right now, so we must do this manually.  Neither the custom property
-    // tree mutations nor the StandardDefineProperty call in the above code will
-    // do this for us.
+    // tree mutations nor the DefineProperty call in the above code will do
+    // this for us.
     //
     // ArraySetLength also implements the capacity <= length invariant for
     // arrays with non-writable length.  We don't need to do anything special
     // for that, because capacity was zeroed out by preventExtensions.  (See
     // the assertion before the if-else above.)
     if (level == IntegrityLevel::Frozen && obj->is<ArrayObject>()) {
         if (!obj->as<ArrayObject>().maybeCopyElementsForWrite(cx))
             return false;
@@ -1499,17 +1092,17 @@ JS_CopyPropertyFrom(JSContext* cx, Handl
         desc.attributesRef() &= ~JSPROP_PERMANENT;
     }
 
     JSAutoCompartment ac(cx, target);
     RootedId wrappedId(cx, id);
     if (!cx->compartment()->wrap(cx, &desc))
         return false;
 
-    return StandardDefineProperty(cx, target, wrappedId, desc);
+    return DefineProperty(cx, target, wrappedId, desc);
 }
 
 JS_FRIEND_API(bool)
 JS_CopyPropertiesFrom(JSContext* cx, HandleObject target, HandleObject obj)
 {
     JSAutoCompartment ac(cx, obj);
 
     AutoIdVector props(cx);
@@ -3003,16 +2596,24 @@ js::GetOwnPropertyDescriptor(JSContext* 
     }
 
     desc.object().set(nobj);
     desc.assertComplete();
     return true;
 }
 
 bool
+js::DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle<PropertyDescriptor> desc)
+{
+    ObjectOpResult result;
+    return DefineProperty(cx, obj, id, desc, result) &&
+           result.checkStrict(cx, obj, id);
+}
+
+bool
 js::DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle<PropertyDescriptor> desc,
                    ObjectOpResult& result)
 {
     desc.assertValid();
     if (DefinePropertyOp op = obj->getOps()->defineProperty)
         return op(cx, obj, id, desc, result);
     return NativeDefineProperty(cx, obj.as<NativeObject>(), id, desc, result);
 }
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -305,17 +305,19 @@ class JSObject : public js::gc::Cell
     MOZ_ALWAYS_INLINE JS::Zone* zoneFromAnyThread() const {
         return group_->zoneFromAnyThread();
     }
     MOZ_ALWAYS_INLINE JS::shadow::Zone* shadowZoneFromAnyThread() const {
         return JS::shadow::Zone::asShadowZone(zoneFromAnyThread());
     }
     static MOZ_ALWAYS_INLINE void readBarrier(JSObject* obj);
     static MOZ_ALWAYS_INLINE void writeBarrierPre(JSObject* obj);
-    static MOZ_ALWAYS_INLINE void writeBarrierPost(void* cellp, JSObject* prev, JSObject* next);
+    static MOZ_ALWAYS_INLINE void writeBarrierPost(JSObject* obj, void* cellp);
+    static MOZ_ALWAYS_INLINE void writeBarrierPostRelocate(JSObject* obj, void* cellp);
+    static MOZ_ALWAYS_INLINE void writeBarrierPostRemove(JSObject* obj, void* cellp);
 
     /* Return the allocKind we would use if we were to tenure this object. */
     js::gc::AllocKind allocKindForTenure(const js::Nursery& nursery) const;
 
     size_t tenuredSizeOfThis() const {
         MOZ_ASSERT(isTenured());
         return js::gc::Arena::thingSize(asTenured().getAllocKind());
     }
@@ -623,36 +625,46 @@ JSObject::readBarrier(JSObject* obj)
 /* static */ MOZ_ALWAYS_INLINE void
 JSObject::writeBarrierPre(JSObject* obj)
 {
     if (!isNullLike(obj) && obj->isTenured())
         obj->asTenured().writeBarrierPre(&obj->asTenured());
 }
 
 /* static */ MOZ_ALWAYS_INLINE void
-JSObject::writeBarrierPost(void* cellp, JSObject* prev, JSObject* next)
+JSObject::writeBarrierPost(JSObject* obj, void* cellp)
+{
+    MOZ_ASSERT(cellp);
+    if (IsNullTaggedPointer(obj))
+        return;
+    MOZ_ASSERT(obj == *static_cast<JSObject**>(cellp));
+    js::gc::StoreBuffer* storeBuffer = obj->storeBuffer();
+    if (storeBuffer)
+        storeBuffer->putCellFromAnyThread(static_cast<js::gc::Cell**>(cellp));
+}
+
+/* static */ MOZ_ALWAYS_INLINE void
+JSObject::writeBarrierPostRelocate(JSObject* obj, void* cellp)
 {
     MOZ_ASSERT(cellp);
+    MOZ_ASSERT(obj);
+    MOZ_ASSERT(obj == *static_cast<JSObject**>(cellp));
+    js::gc::StoreBuffer* storeBuffer = obj->storeBuffer();
+    if (storeBuffer)
+        storeBuffer->putRelocatableCellFromAnyThread(static_cast<js::gc::Cell**>(cellp));
+}
 
-    // If the target needs an entry, add it.
-    js::gc::StoreBuffer* buffer;
-    if (!IsNullTaggedPointer(next) && (buffer = next->storeBuffer())) {
-        // If we know that the prev has already inserted an entry, we can skip
-        // doing the lookup to add the new entry.
-        if (!IsNullTaggedPointer(prev) && prev->storeBuffer()) {
-            buffer->assertHasCellEdge(static_cast<js::gc::Cell**>(cellp));
-            return;
-        }
-        buffer->putCellFromAnyThread(static_cast<js::gc::Cell**>(cellp));
-        return;
-    }
-
-    // Remove the prev entry if the new value does not need it.
-    if (!IsNullTaggedPointer(prev) && (buffer = prev->storeBuffer()))
-        buffer->unputCellFromAnyThread(static_cast<js::gc::Cell**>(cellp));
+/* static */ MOZ_ALWAYS_INLINE void
+JSObject::writeBarrierPostRemove(JSObject* obj, void* cellp)
+{
+    MOZ_ASSERT(cellp);
+    MOZ_ASSERT(obj);
+    MOZ_ASSERT(obj == *static_cast<JSObject**>(cellp));
+    obj->shadowRuntimeFromAnyThread()->gcStoreBufferPtr()->removeRelocatableCellFromAnyThread(
+        static_cast<js::gc::Cell**>(cellp));
 }
 
 namespace js {
 
 inline bool
 IsCallable(const Value& v)
 {
     return v.isObject() && v.toObject().isCallable();
@@ -751,39 +763,17 @@ PreventExtensions(JSContext* cx, HandleO
  *
  * If no such property exists on obj, return true with desc.object() set to
  * null.
  */
 extern bool
 GetOwnPropertyDescriptor(JSContext* cx, HandleObject obj, HandleId id,
                          MutableHandle<PropertyDescriptor> desc);
 
-/*
- * ES6 [[DefineOwnProperty]]. Define a property on obj.
- *
- * If obj is an array, this follows ES5 15.4.5.1.
- * If obj is any other native object, this follows ES5 8.12.9.
- * If obj is a proxy, this calls the proxy handler's defineProperty method.
- * Otherwise, this reports an error and returns false.
- *
- * Both StandardDefineProperty functions hew close to the ES5 spec. Note that
- * the DefineProperty functions do not enforce some invariants mandated by ES6.
- */
-extern bool
-StandardDefineProperty(JSContext* cx, HandleObject obj, HandleId id,
-                       Handle<PropertyDescriptor> descriptor, ObjectOpResult& result);
-
-/*
- * Same as above except without the ObjectOpResult out-parameter. Throws a
- * TypeError on failure.
- */
-extern bool
-StandardDefineProperty(JSContext* cx, HandleObject obj, HandleId id,
-                       Handle<PropertyDescriptor> desc);
-
+/* ES6 [[DefineOwnProperty]]. Define a property on obj. */
 extern bool
 DefineProperty(JSContext* cx, HandleObject obj, HandleId id,
                Handle<PropertyDescriptor> desc, ObjectOpResult& result);
 
 extern bool
 DefineProperty(ExclusiveContext* cx, HandleObject obj, HandleId id, HandleValue value,
                JSGetterOp getter, JSSetterOp setter, unsigned attrs, ObjectOpResult& result);
 
@@ -795,16 +785,19 @@ extern bool
 DefineElement(ExclusiveContext* cx, HandleObject obj, uint32_t index, HandleValue value,
               JSGetterOp getter, JSSetterOp setter, unsigned attrs, ObjectOpResult& result);
 
 /*
  * When the 'result' out-param is omitted, the behavior is the same as above, except
  * that any failure results in a TypeError.
  */
 extern bool
+DefineProperty(JSContext* cx, HandleObject obj, HandleId id, Handle<PropertyDescriptor> desc);
+
+extern bool
 DefineProperty(ExclusiveContext* cx, HandleObject obj, HandleId id, HandleValue value,
                JSGetterOp getter = nullptr,
                JSSetterOp setter = nullptr,
                unsigned attrs = JSPROP_ENUMERATE);
 
 extern bool
 DefineProperty(ExclusiveContext* cx, HandleObject obj, PropertyName* name, HandleValue value,
                JSGetterOp getter = nullptr,
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -714,17 +714,17 @@ Walk(JSContext* cx, HandleObject holder,
                 if (newElement.isUndefined()) {
                     /* Step 2a(iii)(2). The spec deliberately ignores strict failure. */
                     if (!DeleteProperty(cx, obj, id, ignored))
                         return false;
                 } else {
                     /* Step 2a(iii)(3). The spec deliberately ignores strict failure. */
                     Rooted<PropertyDescriptor> desc(cx);
                     desc.setDataDescriptor(newElement, JSPROP_ENUMERATE);
-                    if (!StandardDefineProperty(cx, obj, id, desc, ignored))
+                    if (!DefineProperty(cx, obj, id, desc, ignored))
                         return false;
                 }
             }
         } else {
             /* Step 2b(i). */
             AutoIdVector keys(cx);
             if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY, &keys))
                 return false;
@@ -742,17 +742,17 @@ Walk(JSContext* cx, HandleObject holder,
                 if (newElement.isUndefined()) {
                     /* Step 2b(ii)(2). The spec deliberately ignores strict failure. */
                     if (!DeleteProperty(cx, obj, id, ignored))
                         return false;
                 } else {
                     /* Step 2b(ii)(3). The spec deliberately ignores strict failure. */
                     Rooted<PropertyDescriptor> desc(cx);
                     desc.setDataDescriptor(newElement, JSPROP_ENUMERATE);
-                    if (!StandardDefineProperty(cx, obj, id, desc, ignored))
+                    if (!DefineProperty(cx, obj, id, desc, ignored))
                         return false;
                 }
             }
         }
     }
 
     /* Step 3. */
     RootedString key(cx, IdToString(cx, name));
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -209,17 +209,17 @@ typedef InternalHandle<Bindings*> Intern
  * both function and top-level scripts (the latter is needed to track names in
  * strict mode eval code, to give such code its own lexical environment).
  */
 class Bindings
 {
     friend class BindingIter;
     friend class AliasedFormalIter;
 
-    HeapPtrShape callObjShape_;
+    RelocatablePtrShape callObjShape_;
     uintptr_t bindingArrayAndFlag_;
     uint16_t numArgs_;
     uint16_t numBlockScoped_;
     uint16_t numBodyLevelLexicals_;
     uint16_t aliasedBodyLevelLexicalBegin_;
     uint16_t numUnaliasedBodyLevelLexicals_;
     uint32_t numVars_;
     uint32_t numUnaliasedVars_;
--- a/js/src/proxy/DirectProxyHandler.cpp
+++ b/js/src/proxy/DirectProxyHandler.cpp
@@ -34,17 +34,17 @@ DirectProxyHandler::getOwnPropertyDescri
 
 bool
 DirectProxyHandler::defineProperty(JSContext* cx, HandleObject proxy, HandleId id,
                                    Handle<PropertyDescriptor> desc,
                                    ObjectOpResult& result) const
 {
     assertEnteredPolicy(cx, proxy, id, SET);
     RootedObject target(cx, proxy->as<ProxyObject>().target());
-    return StandardDefineProperty(cx, target, id, desc, result);
+    return DefineProperty(cx, target, id, desc, result);
 }
 
 bool
 DirectProxyHandler::ownPropertyKeys(JSContext* cx, HandleObject proxy,
                                     AutoIdVector& props) const
 {
     assertEnteredPolicy(cx, proxy, JSID_VOID, ENUMERATE);
     RootedObject target(cx, proxy->as<ProxyObject>().target());
--- a/js/src/proxy/ScriptedDirectProxyHandler.cpp
+++ b/js/src/proxy/ScriptedDirectProxyHandler.cpp
@@ -543,17 +543,17 @@ ScriptedDirectProxyHandler::defineProper
 
     // steps 6-7
     RootedValue trap(cx);
     if (!GetProperty(cx, handler, handler, cx->names().defineProperty, &trap))
         return false;
 
     // step 8
     if (trap.isUndefined())
-        return StandardDefineProperty(cx, target, id, desc, result);
+        return DefineProperty(cx, target, id, desc, result);
 
     // step 9
     RootedValue descObj(cx);
     if (!FromPropertyDescriptorToObject(cx, desc, &descObj))
         return false;
 
     // steps 10-11
     RootedValue propKey(cx);
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2481,17 +2481,17 @@ Debugger::trace(JSTracer* trc)
      * Mark Debugger.Frame objects. These are all reachable from JS, because the
      * corresponding JS frames are still on the stack.
      *
      * (Once we support generator frames properly, we will need
      * weakly-referenced Debugger.Frame objects as well, for suspended generator
      * frames.)
      */
     for (FrameMap::Range r = frames.all(); !r.empty(); r.popFront()) {
-        HeapPtrNativeObject& frameobj = r.front().value();
+        RelocatablePtrNativeObject& frameobj = r.front().value();
         MOZ_ASSERT(MaybeForwarded(frameobj.get())->getPrivate());
         TraceEdge(trc, &frameobj, "live Debugger.Frame");
     }
 
     /*
      * Mark every allocation site in our allocation log.
      */
     for (AllocationSite* s = allocationsLog.getFirst(); s; s = s->getNext()) {
@@ -7015,17 +7015,17 @@ DebuggerObject_defineProperty(JSContext*
 
     {
         Maybe<AutoCompartment> ac;
         ac.emplace(cx, obj);
         if (!cx->compartment()->wrap(cx, &desc))
             return false;
 
         ErrorCopier ec(ac);
-        if (!StandardDefineProperty(cx, obj, id, desc))
+        if (!DefineProperty(cx, obj, id, desc))
             return false;
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
@@ -7058,17 +7058,17 @@ DebuggerObject_defineProperties(JSContex
         ac.emplace(cx, obj);
         for (size_t i = 0; i < n; i++) {
             if (!cx->compartment()->wrap(cx, descs[i]))
                 return false;
         }
 
         ErrorCopier ec(ac);
         for (size_t i = 0; i < n; i++) {
-            if (!StandardDefineProperty(cx, obj, ids[i], descs[i]))
+            if (!DefineProperty(cx, obj, ids[i], descs[i]))
                 return false;
         }
     }
 
     args.rval().setUndefined();
     return true;
 }
 
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -59,21 +59,21 @@ typedef HashSet<ReadBarrieredGlobalObjec
  * and compartments may be unnecessarily grouped, however it results in a
  * simpler and faster implementation.
  *
  * If InvisibleKeysOk is true, then the map can have keys in invisible-to-
  * debugger compartments. If it is false, we assert that such entries are never
  * created.
  */
 template <class UnbarrieredKey, bool InvisibleKeysOk=false>
-class DebuggerWeakMap : private WeakMap<PreBarriered<UnbarrieredKey>, HeapPtrObject>
+class DebuggerWeakMap : private WeakMap<PreBarriered<UnbarrieredKey>, RelocatablePtrObject>
 {
   private:
     typedef PreBarriered<UnbarrieredKey> Key;
-    typedef HeapPtrObject Value;
+    typedef RelocatablePtrObject Value;
 
     typedef HashMap<JS::Zone*,
                     uintptr_t,
                     DefaultHasher<JS::Zone*>,
                     RuntimeAllocPolicy> CountMap;
 
     CountMap zoneCounts;
 
@@ -281,20 +281,20 @@ class Debugger : private mozilla::Linked
               ctorName(nullptr)
         {
             MOZ_ASSERT_IF(frame, UncheckedUnwrap(frame)->is<SavedFrame>());
         };
 
         static AllocationSite* create(JSContext* cx, HandleObject frame, double when,
                                       HandleObject obj);
 
-        HeapPtrObject frame;
+        RelocatablePtrObject frame;
         double when;
         const char* className;
-        HeapPtrAtom ctorName;
+        RelocatablePtrAtom ctorName;
     };
     typedef mozilla::LinkedList<AllocationSite> AllocationSiteList;
 
     bool allowUnobservedAsmJS;
     bool trackingAllocationSites;
     double allocationSamplingProbability;
     AllocationSiteList allocationsLog;
     size_t allocationsLogLength;
@@ -357,17 +357,17 @@ class Debugger : private mozilla::Linked
      * removeDebuggee.
      *
      * We don't trace the keys of this map (the frames are on the stack and
      * thus necessarily live), but we do trace the values. It's like a WeakMap
      * that way, but since stack frames are not gc-things, the implementation
      * has to be different.
      */
     typedef HashMap<AbstractFramePtr,
-                    HeapPtrNativeObject,
+                    RelocatablePtrNativeObject,
                     DefaultHasher<AbstractFramePtr>,
                     RuntimeAllocPolicy> FrameMap;
     FrameMap frames;
 
     /* An ephemeral map from JSScript* to Debugger.Script instances. */
     typedef DebuggerWeakMap<JSScript*> ScriptWeakMap;
     ScriptWeakMap scripts;
 
--- a/js/src/vm/DebuggerMemory.cpp
+++ b/js/src/vm/DebuggerMemory.cpp
@@ -213,19 +213,19 @@ DebuggerMemory::drainAllocationsLog(JSCo
         RootedValue ctorName(cx, NullValue());
         if (allocSite->ctorName)
             ctorName.setString(allocSite->ctorName);
         if (!DefineProperty(cx, obj, cx->names().constructor, ctorName))
             return false;
 
         result->setDenseElement(i, ObjectValue(*obj));
 
-        // Pop the front queue entry, and delete it immediately, so that the GC
-        // sees the AllocationSite's HeapPtr barriers run atomically with the
-        // change to the graph (the queue link).
+        // Pop the front queue entry, and delete it immediately, so that
+        // the GC sees the AllocationSite's RelocatablePtr barriers run
+        // atomically with the change to the graph (the queue link).
         MOZ_ALWAYS_TRUE(dbg->allocationsLog.popFirst() == allocSite);
         js_delete(allocSite);
     }
 
     dbg->allocationsLogOverflowed = false;
     dbg->allocationsLogLength = 0;
     args.rval().setObject(*result);
     return true;
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -115,29 +115,29 @@ class RegExpShared
   private:
     friend class RegExpCompartment;
     friend class RegExpStatics;
 
     typedef frontend::TokenStream TokenStream;
 
     struct RegExpCompilation
     {
-        HeapPtrJitCode jitCode;
+        RelocatablePtrJitCode jitCode;
         uint8_t* byteCode;
 
         RegExpCompilation() : byteCode(nullptr) {}
         ~RegExpCompilation() { js_free(byteCode); }
 
         bool compiled(ForceByteCodeEnum force = DontForceByteCode) const {
             return byteCode || (force == DontForceByteCode && jitCode);
         }
     };
 
     /* Source to the RegExp, for lazy compilation. */
-    HeapPtrAtom        source;
+    RelocatablePtrAtom source;
 
     RegExpFlag         flags;
     size_t             parenCount;
     bool               canStringMatch;
     bool               marked_;
 
     RegExpCompilation  compilationArray[4];
 
--- a/js/src/vm/RegExpStatics.h
+++ b/js/src/vm/RegExpStatics.h
@@ -16,29 +16,29 @@ namespace js {
 
 class GlobalObject;
 class RegExpStaticsObject;
 
 class RegExpStatics
 {
     /* The latest RegExp output, set after execution. */
     VectorMatchPairs        matches;
-    HeapPtrLinearString     matchesInput;
+    RelocatablePtrLinearString matchesInput;
 
     /*
      * 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().
      */
-    HeapPtrAtom             lazySource;
+    RelocatablePtrAtom      lazySource;
     RegExpFlag              lazyFlags;
     size_t                  lazyIndex;
 
     /* The latest RegExp input, set before execution. */
-    HeapPtrString           pendingInput;
+    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.
      */
     int32_t                 pendingLazyEvaluation;
 
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -342,18 +342,18 @@ class NewObjectCache
         entry->kind = kind;
 
         entry->nbytes = gc::Arena::thingSize(kind);
         js_memcpy(&entry->templateObject, obj, entry->nbytes);
     }
 
     static void copyCachedToObject(NativeObject* dst, NativeObject* src, gc::AllocKind kind) {
         js_memcpy(dst, src, gc::Arena::thingSize(kind));
-        Shape::writeBarrierPost(&dst->shape_, nullptr, dst->shape_);
-        ObjectGroup::writeBarrierPost(&dst->group_, nullptr, dst->group_);
+        Shape::writeBarrierPost(dst->shape_, &dst->shape_);
+        ObjectGroup::writeBarrierPost(dst->group_, &dst->group_);
     }
 };
 
 /*
  * A FreeOp can do one thing: free memory. For convenience, it has delete_
  * convenience methods that also call destructors.
  *
  * FreeOp is passed to finalizers and other sweep-phase hooks so that we do not
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -863,17 +863,17 @@ class MissingScopeKey
 
 // The value in LiveScopeMap, mapped from by live scope objects.
 class LiveScopeVal
 {
     friend class DebugScopes;
     friend class MissingScopeKey;
 
     AbstractFramePtr frame_;
-    HeapPtrObject staticScope_;
+    RelocatablePtrObject staticScope_;
 
     void sweep();
     static void staticAsserts();
 
   public:
     explicit LiveScopeVal(const ScopeIter& si)
       : frame_(si.initialFrame()),
         staticScope_(si.maybeStaticScope())
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -344,17 +344,17 @@ js::intrinsic_DefineDataProperty(JSConte
     } else {
         // If the fourth argument is unspecified, the attributes are for a
         // plain data property.
         attrs = JSPROP_ENUMERATE;
     }
 
     Rooted<PropertyDescriptor> desc(cx);
     desc.setDataDescriptor(value, attrs);
-    if (!StandardDefineProperty(cx, obj, id, desc))
+    if (!DefineProperty(cx, obj, id, desc))
         return false;
 
     args.rval().setUndefined();
     return true;
 }
 
 bool
 js::intrinsic_UnsafeSetReservedSlot(JSContext* cx, unsigned argc, Value* vp)
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -102,16 +102,35 @@ InterpreterFrame::initExecuteFrame(JSCon
     if (script->isDebuggee())
         setIsDebuggee();
 
 #ifdef DEBUG
     Debug_SetValueRangeToCrashOnTouch(&rval_, 1);
 #endif
 }
 
+void
+InterpreterFrame::writeBarrierPost()
+{
+    /* This needs to follow the same rules as in InterpreterFrame::mark. */
+    if (scopeChain_)
+        JSObject::writeBarrierPost(scopeChain_, &scopeChain_);
+    if (flags_ & HAS_ARGS_OBJ)
+        JSObject::writeBarrierPost(argsObj_, &argsObj_);
+    if (isFunctionFrame()) {
+        JSFunction::writeBarrierPost(exec.fun, &exec.fun);
+        if (isEvalFrame())
+            JSScript::writeBarrierPost(u.evalScript, &u.evalScript);
+    } else {
+        JSScript::writeBarrierPost(exec.script, &exec.script);
+    }
+    if (hasReturnValue())
+        HeapValue::writeBarrierPost(rval_, &rval_);
+}
+
 bool
 InterpreterFrame::copyRawFrameSlots(AutoValueVector* vec)
 {
     if (!vec->resize(numFormalArgs() + script()->nfixed()))
         return false;
     PodCopy(vec->begin(), argv(), numFormalArgs());
     PodCopy(vec->begin() + numFormalArgs(), slots(), script()->nfixed());
     return true;
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -376,16 +376,18 @@ class InterpreterFrame
     Value*              argv_;         /* If hasArgs(), points to frame's arguments. */
     LifoAlloc::Mark     mark_;          /* Used to release memory for this frame. */
 
     static void staticAsserts() {
         JS_STATIC_ASSERT(offsetof(InterpreterFrame, rval_) % sizeof(Value) == 0);
         JS_STATIC_ASSERT(sizeof(InterpreterFrame) % sizeof(Value) == 0);
     }
 
+    void writeBarrierPost();
+
     /*
      * The utilities are private since they are not able to assert that only
      * unaliased vars/formals are accessed. Normal code should prefer the
      * InterpreterFrame::unaliased* members (or InterpreterRegs::stackDepth for
      * the usual "depth is at least" assertions).
      */
     Value* slots() const { return (Value*)(this + 1); }
     Value* base() const { return slots() + script()->nfixed(); }
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -799,17 +799,17 @@ class PreliminaryObjectArray
     }
 
     bool full() const;
     void sweep();
 };
 
 class PreliminaryObjectArrayWithTemplate : public PreliminaryObjectArray
 {
-    HeapPtrShape shape_;
+    RelocatablePtrShape shape_;
 
   public:
     explicit PreliminaryObjectArrayWithTemplate(Shape* shape)
       : shape_(shape)
     {}
 
     void clear() {
         shape_.init(nullptr);
@@ -876,50 +876,50 @@ class TypeNewScript
         uint32_t offset;
         Initializer(Kind kind, uint32_t offset)
           : kind(kind), offset(offset)
         {}
     };
 
   private:
     // Scripted function which this information was computed for.
-    HeapPtrFunction function_;
+    RelocatablePtrFunction function_;
 
     // Any preliminary objects with the type. The analyses are not performed
     // until this array is cleared.
     PreliminaryObjectArray* preliminaryObjects;
 
     // After the new script properties analyses have been performed, a template
     // object to use for newly constructed objects. The shape of this object
     // reflects all definite properties the object will have, and the
     // allocation kind to use. This is null if the new objects have an unboxed
     // layout, in which case the UnboxedLayout provides the initial structure
     // of the object.
-    HeapPtrPlainObject templateObject_;
+    RelocatablePtrPlainObject templateObject_;
 
     // Order in which definite properties become initialized. We need this in
     // case the definite properties are invalidated (such as by adding a setter
     // to an object on the prototype chain) while an object is in the middle of
     // being initialized, so we can walk the stack and fixup any objects which
     // look for in-progress objects which were prematurely set with an incorrect
     // shape. Property assignments in inner frames are preceded by a series of
     // SETPROP_FRAME entries specifying the stack down to the frame containing
     // the write.
     Initializer* initializerList;
 
     // If there are additional properties found by the acquired properties
     // analysis which were not found by the definite properties analysis, this
     // shape contains all such additional properties (plus the definite
     // properties). When an object of this group acquires this shape, it is
     // fully initialized and its group can be changed to initializedGroup.
-    HeapPtrShape initializedShape_;
+    RelocatablePtrShape initializedShape_;
 
     // Group with definite properties set for all properties found by
     // both the definite and acquired properties analyses.
-    HeapPtrObjectGroup initializedGroup_;
+    RelocatablePtrObjectGroup initializedGroup_;
 
   public:
     TypeNewScript() { mozilla::PodZero(this); }
     ~TypeNewScript() {
         js_delete(preliminaryObjects);
         js_free(initializerList);
     }
 
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -604,17 +604,17 @@ UnboxedPlainObject::convertToNative(JSCo
         Rooted<UnboxedExpandoObject*> nexpando(cx, expando);
         RootedId id(cx);
         Rooted<PropertyDescriptor> desc(cx);
         for (size_t i = 0; i < ids.length(); i++) {
             id = ids[i];
             if (!GetOwnPropertyDescriptor(cx, nexpando, id, &desc))
                 return false;
             ObjectOpResult result;
-            if (!StandardDefineProperty(cx, nobj, id, desc, result))
+            if (!DefineProperty(cx, nobj, id, desc, result))
                 return false;
             MOZ_ASSERT(result.ok());
         }
     }
 
     return true;
 }
 
--- a/js/src/vm/WeakMapObject.h
+++ b/js/src/vm/WeakMapObject.h
@@ -7,21 +7,21 @@
 #ifndef vm_WeakMapObject_h
 #define vm_WeakMapObject_h
 
 #include "jsobj.h"
 #include "jsweakmap.h"
 
 namespace js {
 
-class ObjectValueMap : public WeakMap<PreBarrieredObject, HeapValue>
+class ObjectValueMap : public WeakMap<PreBarrieredObject, RelocatableValue>
 {
   public:
     ObjectValueMap(JSContext* cx, JSObject* obj)
-      : WeakMap<PreBarrieredObject, HeapValue>(cx, obj) {}
+      : WeakMap<PreBarrieredObject, RelocatableValue>(cx, obj) {}
 
     virtual bool findZoneEdges();
 };
 
 class WeakMapObject : public NativeObject
 {
   public:
     static const Class class_;
--- a/layout/base/nsCSSRenderingBorders.cpp
+++ b/layout/base/nsCSSRenderingBorders.cpp
@@ -1067,20 +1067,16 @@ nsCSSBorderRenderer::CreateCornerGradien
   aPoint1 = Point(pat1.x, pat1.y);
   aPoint2 = Point(pat2.x, pat2.y);
 
   Color firstColor = Color::FromABGR(aFirstColor);
   Color secondColor = Color::FromABGR(aSecondColor);
 
   nsTArray<gfx::GradientStop> rawStops(2);
   rawStops.SetLength(2);
-  // This is only guaranteed to give correct (and in some cases more correct)
-  // rendering with the Direct2D Azure and Quartz Cairo backends. For other
-  // cairo backends it could create un-antialiased border corner transitions
-  // since that at least used to be pixman's behaviour for hard stops.
   rawStops[0].color = firstColor;
   rawStops[0].offset = 0.5;
   rawStops[1].color = secondColor;
   rawStops[1].offset = 0.5;
   RefPtr<GradientStops> gs =
     gfxGradientCache::GetGradientStops(aDT, rawStops, ExtendMode::CLAMP);
   if (!gs) {
     // Having two corners, both with reversed color stops is pretty common
--- a/layout/reftests/bugs/664127-1.css
+++ b/layout/reftests/bugs/664127-1.css
@@ -1,11 +1,11 @@
 /* This external stylesheet is needed to be able to use a cached image
    (solidblue.png) through a relative path */
 tree {
-  height: 100px; -moz-appearance: none; border: none;
+  height: 100px; -moz-appearance: none; border: none; background-color: transparent;
 }
 treecol, treecolpicker {
   visibility: hidden;
 }
 treechildren::-moz-tree-image {
   list-style-image: url('solidblue.png');
 }
--- a/layout/reftests/bugs/668319-1.xul
+++ b/layout/reftests/bugs/668319-1.xul
@@ -1,11 +1,11 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <?xml-stylesheet type="text/css" href="data:text/css,
-tree { height: 100px; -moz-appearance: none; border: none; }
+tree { height: 100px; -moz-appearance: none; border: none; background-color: transparent; }
 treecol, treecolpicker { visibility: hidden; }
 treechildren::-moz-tree-cell-text { opacity: 0; }
 "?>
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <tree>
     <treecols>
       <treecol flex="1"/>
     </treecols>
--- a/layout/reftests/bugs/966992-1-ref.html
+++ b/layout/reftests/bugs/966992-1-ref.html
@@ -10,16 +10,17 @@ font-face {
 
         html,body {
             color:black; background-color:white; font:16px DejaVuSansMono!important; padding:0; margin:7px;
         }
 
     input {
       width: 100px; padding:50px; -moz-appearance:none; overflow-clip-box:padding-box;
       border: 3px solid black;
+      background-color: white;
     }
     textarea, #textarea {
       width: 160px; height:110px; padding:40px; overflow:scroll; -moz-appearance:none; overflow-clip-box:padding-box;
       border: 3px solid black;
     }
     #textarea { word-break: break-all; font:14px DejaVuSansMono!important; }
 
 
--- a/layout/reftests/bugs/966992-1.html
+++ b/layout/reftests/bugs/966992-1.html
@@ -10,16 +10,17 @@ font-face {
 
         html,body {
             color:black; background-color:white; font:16px DejaVuSansMono!important; padding:0; margin:7px;
         }
 
     input {
       width: 100px; padding:50px; -moz-appearance:none; overflow-clip-box:content-box;
       border: 3px solid black;
+      background-color: white;
     }
     textarea {
       width: 160px; height:110px; padding:40px; overflow:scroll; -moz-appearance:none; overflow-clip-box:content-box;
       border: 3px solid