Merge m-c to fx-team. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 06 Oct 2014 17:40:55 -0400
changeset 209071 650603771acde4563e4b8b20d3efd213c746d9cb
parent 209070 a4a109579ef265390b0179d6a7c0703faeb39764 (current diff)
parent 209040 eaa80e4597a28369a22b5990d0a6bc24f39bdb45 (diff)
child 209072 9ee9e193fc48b0c7e6f27337c64fe4780842ea87
child 209092 805fa09191ab0dc01e64835f72868a806fd0da5f
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersmerge
milestone35.0a1
Merge m-c to fx-team. a=merge
content/base/src/nsDocument.cpp
dom/icc/interfaces/nsIDOMIccInfo.idl
gfx/angle/src/libGLESv2/constants.h
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="470826d13ae130a5c3d572d1029e595105485fb0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="83de447d9ae9a59459d7a445f9348a254c661850"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
--- 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="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
     <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="470826d13ae130a5c3d572d1029e595105485fb0"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="83de447d9ae9a59459d7a445f9348a254c661850"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8986df0f82e15ac2798df0b6c2ee3435400677ac">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="470826d13ae130a5c3d572d1029e595105485fb0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="83de447d9ae9a59459d7a445f9348a254c661850"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="470826d13ae130a5c3d572d1029e595105485fb0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="83de447d9ae9a59459d7a445f9348a254c661850"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
--- 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="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
     <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="470826d13ae130a5c3d572d1029e595105485fb0"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="83de447d9ae9a59459d7a445f9348a254c661850"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
   <!-- 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,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="470826d13ae130a5c3d572d1029e595105485fb0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="83de447d9ae9a59459d7a445f9348a254c661850"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8986df0f82e15ac2798df0b6c2ee3435400677ac">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="470826d13ae130a5c3d572d1029e595105485fb0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="83de447d9ae9a59459d7a445f9348a254c661850"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "842fd7448790cfaeaf0bf22164e599c74e9be0e9", 
+    "revision": "1e5360b21cca807b2ea63ea8fb878b451131c9bd", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
     <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="470826d13ae130a5c3d572d1029e595105485fb0"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="83de447d9ae9a59459d7a445f9348a254c661850"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
     <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="470826d13ae130a5c3d572d1029e595105485fb0"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="83de447d9ae9a59459d7a445f9348a254c661850"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8986df0f82e15ac2798df0b6c2ee3435400677ac">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="470826d13ae130a5c3d572d1029e595105485fb0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="83de447d9ae9a59459d7a445f9348a254c661850"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
     <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="470826d13ae130a5c3d572d1029e595105485fb0"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="83de447d9ae9a59459d7a445f9348a254c661850"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9eec93462df6d01fe33a737bb4a185e9ad6cee15"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/content/base/src/ImportManager.cpp
+++ b/content/base/src/ImportManager.cpp
@@ -86,16 +86,20 @@ ImportLoader::Updater::GetReferrerChain(
   for (uint32_t i = 0; i < l / 2; i++) {
     Swap(aResult[i], aResult[l - i - 1]);
   }
 }
 
 bool
 ImportLoader::Updater::ShouldUpdate(nsTArray<nsINode*>& aNewPath)
 {
+  if (mLoader->Manager()->GetNearestPredecessor(mLoader->GetMainReferrer()) !=
+      mLoader->mBlockingPredecessor) {
+    return true;
+  }
   // Let's walk down on the main referrer chains of both the current main and
   // the new link, and find the last pair of links that are from the same
   // document. This is the junction point between the two referrer chain. Their
   // order in the subimport list of that document will determine if we have to
   // update the spanning tree or this new edge changes nothing in the script
   // execution order.
   nsTArray<nsINode*> oldPath;
   GetReferrerChain(mLoader->mLinks[mLoader->mMainReferrer], oldPath);
@@ -737,32 +741,40 @@ ImportManager::AddLoaderWithNewURI(Impor
 }
 
 nsRefPtr<ImportLoader> ImportManager::GetNearestPredecessor(nsINode* aNode)
 {
   // Return the previous link if there is any in the same document.
   nsIDocument* doc = aNode->OwnerDoc();
   int32_t idx = doc->IndexOfSubImportLink(aNode);
   MOZ_ASSERT(idx != -1, "aNode must be a sub import link of its owner document");
+
+  for (; idx > 0; idx--) {
+    HTMLLinkElement* link =
+      static_cast<HTMLLinkElement*>(doc->GetSubImportLink(idx - 1));
+    nsCOMPtr<nsIURI> uri = link->GetHrefURI();
+    nsRefPtr<ImportLoader> ret;
+    mImports.Get(uri, getter_AddRefs(ret));
+    // Only main referrer links are interesting.
+    if (ret->GetMainReferrer() == link) {
+      return ret;
+    }
+  }
+
   if (idx == 0) {
     if (doc->IsMasterDocument()) {
       // If there is no previous one, and it was the master document, then
       // there is no predecessor.
       return nullptr;
     }
     // Else we find the main referrer of the import parent of the link's document.
     // And do a recursion.
     ImportLoader* owner = Find(doc);
     MOZ_ASSERT(owner);
     nsCOMPtr<nsINode> mainReferrer = owner->GetMainReferrer();
     return GetNearestPredecessor(mainReferrer);
   }
-  MOZ_ASSERT(idx > 0);
-  HTMLLinkElement* link =
-    static_cast<HTMLLinkElement*>(doc->GetSubImportLink(idx - 1));
-  nsCOMPtr<nsIURI> uri = link->GetHrefURI();
-  nsRefPtr<ImportLoader> ret;
-  mImports.Get(uri, getter_AddRefs(ret));
-  return ret;
+
+  return nullptr;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -3515,16 +3515,20 @@ nsDocument::GetBaseTarget(nsAString &aBa
 
 void
 nsDocument::SetDocumentCharacterSet(const nsACString& aCharSetID)
 {
   // XXX it would be a good idea to assert the sanity of the argument,
   // but before we figure out what to do about non-Encoding Standard
   // encodings in the charset menu and in mailnews, assertions are futile.
   if (!mCharacterSet.Equals(aCharSetID)) {
+    if (mMasterDocument && !aCharSetID.EqualsLiteral("UTF-8")) {
+      // Imports are always UTF-8
+      return;
+    }
     mCharacterSet = aCharSetID;
 
     int32_t n = mCharSetObservers.Length();
 
     for (int32_t i = 0; i < n; i++) {
       nsIObserver* observer = mCharSetObservers.ElementAt(i);
 
       observer->Observe(static_cast<nsIDocument *>(this), "charset",
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_1_A.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+  <link rel="import" href="file_cycle_1_B.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+  <script>
+    order.push("A");
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_1_B.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+  <link rel="import" href="file_cycle_1_C.html" onload="loaded()" onerror="failed()"></link>
+  <link rel="import" href="file_cycle_1_A.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+  <script>
+    order.push("B");
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_1_C.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+  <link rel="import" href="file_cycle_1_B.html" onload="loaded()" onerror="failed()"></link>
+  <link rel="import" href="file_cycle_1_A.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+  <script>
+    order.push("C");
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_2_A.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+  <link rel="import" href="file_cycle_2_B.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+  <script>
+    order.push("A");
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_2_B.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+  <link rel="import" href="file_cycle_2_A.html" onload="loaded()" onerror="failed()"></link>
+  <link rel="import" href="file_cycle_2_C.html" onload="loaded()" onerror="failed()"></link>
+  <link rel="import" href="file_cycle_2_D.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+  <script>
+    order.push("B");
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_2_C.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+</head>
+<body>
+  <script>
+    order.push("C");
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_2_D.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+</head>
+<body>
+  <script>
+    order.push("D");
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_3_A.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+  <link rel="import" href="file_cycle_3_C.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+  <script>
+    order.push("A");
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_3_B.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+  <link rel="import" href="file_cycle_3_C.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+  <script>
+    order.push("B");
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_3_C.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+  <link rel="import" href="file_cycle_3_A.html" onload="loaded()" onerror="failed()"></link>
+  <link rel="import" href="file_cycle_3_B.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+  <script>
+    order.push("C");
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_4_A.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+  <link rel="import" href="file_cycle_4_B.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+  <script>
+    order.push("A");
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_4_B.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+  <link rel="import" href="file_cycle_4_C.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+  <script>
+    order.push("B");
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_4_C.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+  <link rel="import" href="file_cycle_4_D.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+  <script>
+    order.push("C");
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_4_D.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+  <link rel="import" href="file_cycle_4_B.html" onload="loaded()" onerror="failed()"></link>
+  <link rel="import" href="file_cycle_4_E.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+  <script>
+    order.push("D");
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_4_E.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+  <link rel="import" href="file_cycle_4_C.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+  <script>
+    order.push("E");
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_5_A.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+  <link rel="import" href="file_cycle_5_B.html" onload="loaded()" onerror="failed()"></link>
+  <link rel="import" href="file_cycle_5_D.html" onload="loaded()" onerror="failed()"></link>
+  <link rel="import" href="file_cycle_5_C.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+  <script>
+    order.push("A");
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_5_B.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+  <link rel="import" href="file_cycle_5_A.html" onload="loaded()" onerror="failed()"></link>
+  <link rel="import" href="file_cycle_5_C.html" onload="loaded()" onerror="failed()"></link>
+  <link rel="import" href="file_cycle_5_D.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+  <script>
+    order.push("B");
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_5_C.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+  <link rel="import" href="file_cycle_5_A.html" onload="loaded()" onerror="failed()"></link>
+  <link rel="import" href="file_cycle_5_B.html" onload="loaded()" onerror="failed()"></link>
+  <link rel="import" href="file_cycle_5_D.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+  <script>
+    order.push("C");
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_cycle_5_D.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en-US">
+<head>
+  <link rel="import" href="file_cycle_5_A.html" onload="loaded()" onerror="failed()"></link>
+  <link rel="import" href="file_cycle_5_B.html" onload="loaded()" onerror="failed()"></link>
+  <link rel="import" href="file_cycle_5_C.html" onload="loaded()" onerror="failed()"></link>
+</head>
+<body>
+  <script>
+    order.push("D");
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_encoding.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<head>
+<meta charset="EUC-KR">
+</head>
+<body>Ignore my encoding</body>
\ No newline at end of file
--- a/content/html/content/test/imports/mochitest.ini
+++ b/content/html/content/test/imports/mochitest.ini
@@ -11,10 +11,40 @@ support-files =
   file_importC5.html
   file_importC6.html
   file_importC7.html
   file_importC8.html
   file_importC9.html
   file_importC10.html
   file_importD.html
   file_importE.html
+  file_cycle_1_A.html
+  file_cycle_1_B.html
+  file_cycle_1_C.html
+  file_cycle_2_A.html
+  file_cycle_2_B.html
+  file_cycle_2_C.html
+  file_cycle_2_D.html
+  file_cycle_3_A.html
+  file_cycle_3_B.html
+  file_cycle_3_C.html
+  file_cycle_4_A.html
+  file_cycle_4_B.html
+  file_cycle_4_C.html
+  file_cycle_4_D.html
+  file_cycle_4_E.html
+  file_cycle_5_A.html
+  file_cycle_5_B.html
+  file_cycle_5_C.html
+  file_cycle_5_D.html
+  file_encoding.html
 
-
+[test_cycle_1.html]
+skip-if = toolkit == 'gonk' # nested imports fail on b2g emulator
+[test_cycle_2.html]
+skip-if = toolkit == 'gonk' # nested imports fail on b2g emulator
+[test_cycle_3.html]
+skip-if = toolkit == 'gonk' # nested imports fail on b2g emulator
+[test_cycle_4.html]
+skip-if = toolkit == 'gonk' # nested imports fail on b2g emulator
+[test_cycle_5.html]
+skip-if = toolkit == 'gonk' # nested imports fail on b2g emulator
+[test_encoding.html]
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/test_cycle_1.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1061469
+-->
+<head>
+  <title>Test for Bug 1061469</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>
+<body>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1061469">Mozilla Bug 1061469</a>
+  <script type="text/javascript">
+    SimpleTest.waitForExplicitFinish();
+    var counter = 0;
+    var fcounter = 0;
+    var order = [];
+    function loaded() {
+      counter++;
+    }
+    function failed() {
+      fcounter++;
+    }
+  </script>
+  <link rel="import" href="file_cycle_1_A.html" onload="loaded()" onerror="failed()"></link>
+  <script type="text/javascript">
+    is(counter, 6, "Imports are loaded");
+    is(fcounter, 0, "No error in imports");
+    var expected = ["C","B","A"];
+    for (i in expected)
+      is(order[i], expected[i], "import " + i + " should be " + expected[i]);
+    SimpleTest.finish();
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/test_cycle_2.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1061469
+-->
+<head>
+  <title>Test for Bug 1061469</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>
+<body>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1061469">Mozilla Bug 1061469</a>
+  <script type="text/javascript">
+    SimpleTest.waitForExplicitFinish();
+    var counter = 0;
+    var fcounter = 0;
+    var order = [];
+    function loaded() {
+      counter++;
+    }
+    function failed() {
+      fcounter++;
+    }
+  </script>
+  <link rel="import" href="file_cycle_2_A.html" onload="loaded()" onerror="failed()"></link>
+  <script type="text/javascript">
+    is(counter, 5, "Imports are loaded");
+    is(fcounter, 0, "No error in imports");
+    var expected = ["C","D","B","A"];
+    for (i in expected)
+      is(order[i], expected[i], "import " + i + " should be " + expected[i]);
+    SimpleTest.finish();
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/test_cycle_3.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1061469
+-->
+<head>
+  <title>Test for Bug 1061469</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>
+<body>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1061469">Mozilla Bug 1061469</a>
+  <script type="text/javascript">
+    SimpleTest.waitForExplicitFinish();
+    var counter = 0;
+    var fcounter = 0;
+    var order = [];
+    function loaded() {
+      counter++;
+    }
+    function failed() {
+      fcounter++;
+    }
+  </script>
+  <link rel="import" href="file_cycle_3_A.html" onload="loaded()" onerror="failed()"></link>
+  <link rel="import" href="file_cycle_3_B.html" onload="loaded()" onerror="failed()"></link>
+  <script type="text/javascript">
+    is(counter, 6, "Imports are loaded");
+    is(fcounter, 0, "No error in imports");
+    var expected = ["B","C","A"];
+    for (i in expected)
+      is(order[i], expected[i], "import " + i + " should be " + expected[i]);
+    SimpleTest.finish();
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/test_cycle_4.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1061469
+-->
+<head>
+  <title>Test for Bug 1061469</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>
+<body>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1061469">Mozilla Bug 1061469</a>
+  <script type="text/javascript">
+    SimpleTest.waitForExplicitFinish();
+    var counter = 0;
+    var fcounter = 0;
+    var order = [];
+    function loaded() {
+      counter++;
+    }
+    function failed() {
+      fcounter++;
+    }
+  </script>
+  <link rel="import" href="file_cycle_4_A.html" onload="loaded()" onerror="failed()"></link>
+  <link rel="import" href="file_cycle_4_D.html" onload="loaded()" onerror="failed()"></link>
+  <script type="text/javascript">
+    is(counter, 8, "Imports are loaded");
+    is(fcounter, 0, "No error in imports");
+    var expected = ["E","D","C","B","A"];
+    for (i in expected)
+      is(order[i], expected[i], "import " + i + " should be " + expected[i]);
+    SimpleTest.finish();
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/test_cycle_5.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1061469
+-->
+<head>
+  <title>Test for Bug 1061469</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>
+<body>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1061469">Mozilla Bug 1061469</a>
+  <script type="text/javascript">
+    SimpleTest.waitForExplicitFinish();
+    var counter = 0;
+    var fcounter = 0;
+    var order = [];
+    function loaded() {
+      counter++;
+    }
+    function failed() {
+      fcounter++;
+    }
+  </script>
+  <link rel="import" href="file_cycle_5_A.html" onload="loaded()" onerror="failed()"></link>
+  <link rel="import" href="file_cycle_5_C.html" onload="loaded()" onerror="failed()"></link>
+  <script type="text/javascript">
+    is(counter, 14, "Imports are loaded");
+    is(fcounter, 0, "No error in imports");
+    var expected = ["D","C","B","A"];
+    for (i in expected)
+      is(order[i], expected[i], "import " + i + " should be " + expected[i]);
+    SimpleTest.finish();
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/test_encoding.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1061469
+-->
+<head>
+  <title>Test for Bug 1061469</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>
+<body>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1061469">Mozilla Bug 1061469</a>
+  <script type="text/javascript">
+    SimpleTest.waitForExplicitFinish();
+    var success = false;
+    function loaded() {
+      success = true;
+    }
+    function failed() {
+      ok(false, "Import loading failed");
+    }
+  </script>
+  <link rel="import" href="file_encoding.html" id="import" onload="loaded()" onerror="failed()"></link>
+  <script type="text/javascript">
+    is(document.getElementById("import").import.characterSet, "UTF-8", "characterSet should be UTF-8 for imports");
+    SimpleTest.finish();
+  </script>
+</body>
+</html>
\ No newline at end of file
--- a/content/html/content/test/test_video_wakelock.html
+++ b/content/html/content/test/test_video_wakelock.html
@@ -16,64 +16,70 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content">
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 868943 **/
 
 function testVideoPlayPause() {
+  info("#1 testVideoPlayPause");
+
   var lockState_cpu = true;
   var lockState_screen = true;
   var count_cpu = 0;
   var count_screen = 0;
 
   var content = document.getElementById('content');
 
   var video = document.createElement('video');
   video.src = "wakelock.ogv";
   content.appendChild(video);
 
   var startDate;
-  video.addEventListener('playing', function() {
-    startDate = new Date();
+  function testVideoPlayPauseListener(topic, state) {
+    info("#1 topic=" + topic + ", state=" + state);
 
-    // The next step is to unlock the resource.
-    lockState_cpu = false;
-    lockState_screen = false;
-    video.pause();
-  });
-
-  function testVideoPlayPauseListener(topic, state) {
     var locked = state == "locked-foreground" ||
                  state == "locked-background";
 
     if (topic == "cpu") {
-      is(locked, lockState_cpu, "Video element locked the cpu - paused");
+      is(locked, lockState_cpu, "#1 Video element locked the cpu");
       count_cpu++;
     } else if (topic == "screen") {
-      is(locked, lockState_screen, "Video element locked the screen - paused");
+      is(locked, lockState_screen, "#1 Video element locked the screen");
       count_screen++;
     }
 
+    if (count_cpu == 1 && count_screen == 1) {
+      info("#1 Both cpu and screen are locked");
+      // The next step is to unlock the resource.
+      lockState_cpu = false;
+      lockState_screen = false;
+      video.pause();
+      startDate = new Date();
+    }
+
     if (count_cpu == 2 && count_screen == 2) {
       var diffDate = (new Date() - startDate);
-      ok(diffDate > 200, "There was at least 200 milliseconds between the stop and the wakelock release");
+      ok(diffDate > 200, "#1 There was at least 200 milliseconds between the stop and the wakelock release");
 
       content.removeChild(video);
       navigator.mozPower.removeWakeLockListener(testVideoPlayPauseListener);
       runTests();
     }
   }
 
   navigator.mozPower.addWakeLockListener(testVideoPlayPauseListener);
   video.play();
 }
 
 function testVideoPlay() {
+  info("#2 testVideoPlay");
+
   var lockState_cpu = true;
   var lockState_screen = true;
   var count_cpu = 0;
   var count_screen = 0;
 
   var content = document.getElementById('content');
 
   var video = document.createElement('video');
@@ -81,34 +87,37 @@ function testVideoPlay() {
   content.appendChild(video);
 
   var startDate;
   video.addEventListener('progress', function() {
     startDate = new Date();
   });
 
   function testVideoPlayListener(topic, state) {
+    info("#2 topic=" + topic + ", state=" + state);
+
     var locked = state == "locked-foreground" ||
                  state == "locked-background";
 
     if (topic == "cpu") {
-      is(locked, lockState_cpu, "Video element locked the cpu - paused");
+      is(locked, lockState_cpu, "#2 Video element locked the cpu");
       count_cpu++;
     } else if (topic == "screen") {
-      is(locked, lockState_screen, "Video element locked the screen - paused");
+      is(locked, lockState_screen, "#2 Video element locked the screen");
       count_screen++;
     }
 
     if (count_cpu == 1 && count_screen == 1) {
+      info("#2 Both cpu and screen are locked");
       // The next step is to unlock the resource.
       lockState_cpu = false;
       lockState_screen = false;
     } else if (count_cpu == 2 && count_screen == 2) {
       var diffDate = (new Date() - startDate);
-      ok(diffDate > 200, "There was at least milliseconds between the stop and the wakelock release");
+      ok(diffDate > 200, "#2 There was at least milliseconds between the stop and the wakelock release");
 
       content.removeChild(video);
       navigator.mozPower.removeWakeLockListener(testVideoPlayListener);
       runTests();
     }
   }
 
   navigator.mozPower.addWakeLockListener(testVideoPlayListener);
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -693,33 +693,47 @@ DOMInterfaces = {
     'nativeType': 'mozilla::dom::HTMLCanvasPrintState',
 },
 
 'MozChannel': {
     'nativeType': 'nsIChannel',
     'notflattened': True
 },
 
+'MozCdmaIccInfo': {
+    'headerFile': 'mozilla/dom/IccInfo.h',
+    'nativeType': 'mozilla::dom::CdmaIccInfo',
+},
+
 'MozCellBroadcast': {
     'nativeType': 'mozilla::dom::CellBroadcast',
 },
 
 'MozCellBroadcastEtwsInfo': {
     'nativeType': 'mozilla::dom::CellBroadcastEtwsInfo',
     'headerFile': 'CellBroadcastMessage.h'
 },
 
 'MozCellBroadcastMessage': {
     'nativeType': 'mozilla::dom::CellBroadcastMessage',
 },
 
+'MozGsmIccInfo': {
+    'headerFile': 'mozilla/dom/IccInfo.h',
+    'nativeType': 'mozilla::dom::GsmIccInfo',
+},
+
 'MozIcc': {
     'nativeType': 'mozilla::dom::Icc',
 },
 
+'MozIccInfo': {
+    'nativeType': 'mozilla::dom::IccInfo',
+},
+
 'MozIccManager': {
     'nativeType': 'mozilla::dom::IccManager',
 },
 
 'MozMobileCellInfo': {
     'nativeType': 'mozilla::dom::MobileCellInfo',
 },
 
@@ -1789,17 +1803,16 @@ addExternalIface('HitRegionOptions', nat
 addExternalIface('imgINotificationObserver', nativeType='imgINotificationObserver')
 addExternalIface('imgIRequest', nativeType='imgIRequest', notflattened=True)
 addExternalIface('MenuBuilder', nativeType='nsIMenuBuilder', notflattened=True)
 addExternalIface('MozBoxObject', nativeType='nsIBoxObject')
 addExternalIface('MozControllers', nativeType='nsIControllers')
 addExternalIface('MozFrameLoader', nativeType='nsIFrameLoader', notflattened=True)
 addExternalIface('MozFrameRequestCallback', nativeType='nsIFrameRequestCallback',
                  notflattened=True)
-addExternalIface('MozIccInfo', headerFile='nsIDOMIccInfo.h')
 addExternalIface('MozMmsMessage')
 addExternalIface('MozObserver', nativeType='nsIObserver', notflattened=True)
 addExternalIface('MozRDFCompositeDataSource', nativeType='nsIRDFCompositeDataSource',
                  notflattened=True)
 addExternalIface('MozRDFResource', nativeType='nsIRDFResource', notflattened=True)
 addExternalIface('MozSmsMessage')
 addExternalIface('MozTreeBoxObject', nativeType='nsITreeBoxObject',
                  notflattened=True)
--- a/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
@@ -11,17 +11,17 @@
 #include "BluetoothUtils.h"
 
 #include "jsapi.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "nsContentUtils.h"
 #include "nsIAudioManager.h"
-#include "nsIDOMIccInfo.h"
+#include "nsIIccInfo.h"
 #include "nsIIccProvider.h"
 #include "nsIMobileConnectionInfo.h"
 #include "nsIMobileConnectionService.h"
 #include "nsIMobileNetworkInfo.h"
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsITelephonyService.h"
 #include "nsRadioInterfaceLayer.h"
@@ -668,21 +668,21 @@ BluetoothHfpManager::HandleVoiceConnecti
 
 void
 BluetoothHfpManager::HandleIccInfoChanged(uint32_t aClientId)
 {
   nsCOMPtr<nsIIccProvider> icc =
     do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
   NS_ENSURE_TRUE_VOID(icc);
 
-  nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
+  nsCOMPtr<nsIIccInfo> iccInfo;
   icc->GetIccInfo(aClientId, getter_AddRefs(iccInfo));
   NS_ENSURE_TRUE_VOID(iccInfo);
 
-  nsCOMPtr<nsIDOMMozGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
+  nsCOMPtr<nsIGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
   NS_ENSURE_TRUE_VOID(gsmIccInfo);
   gsmIccInfo->GetMsisdn(mMsisdn);
 }
 
 void
 BluetoothHfpManager::HandleShutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/bluetooth/bluez/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluez/BluetoothHfpManager.cpp
@@ -22,17 +22,17 @@
 #include "nsContentUtils.h"
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsServiceManagerUtils.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/SettingChangeNotificationBinding.h"
 
 #ifdef MOZ_B2G_RIL
-#include "nsIDOMIccInfo.h"
+#include "nsIIccInfo.h"
 #include "nsIIccProvider.h"
 #include "nsIMobileConnectionInfo.h"
 #include "nsIMobileConnectionService.h"
 #include "nsIMobileNetworkInfo.h"
 #include "nsITelephonyService.h"
 #include "nsRadioInterfaceLayer.h"
 #endif
 
@@ -669,21 +669,21 @@ BluetoothHfpManager::HandleVoiceConnecti
 
 void
 BluetoothHfpManager::HandleIccInfoChanged(uint32_t aClientId)
 {
   nsCOMPtr<nsIIccProvider> icc =
     do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
   NS_ENSURE_TRUE_VOID(icc);
 
-  nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
+  nsCOMPtr<nsIIccInfo> iccInfo;
   icc->GetIccInfo(aClientId, getter_AddRefs(iccInfo));
   NS_ENSURE_TRUE_VOID(iccInfo);
 
-  nsCOMPtr<nsIDOMMozGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
+  nsCOMPtr<nsIGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
   NS_ENSURE_TRUE_VOID(gsmIccInfo);
   gsmIccInfo->GetMsisdn(mMsisdn);
 }
 #endif // MOZ_B2G_RIL
 
 void
 BluetoothHfpManager::HandleShutdown()
 {
--- a/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.cpp
+++ b/dom/bluetooth2/bluedroid/hfp/BluetoothHfpManager.cpp
@@ -11,17 +11,17 @@
 #include "BluetoothUtils.h"
 
 #include "jsapi.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "nsContentUtils.h"
 #include "nsIAudioManager.h"
-#include "nsIDOMIccInfo.h"
+#include "nsIIccInfo.h"
 #include "nsIIccProvider.h"
 #include "nsIMobileConnectionInfo.h"
 #include "nsIMobileConnectionService.h"
 #include "nsIMobileNetworkInfo.h"
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsITelephonyService.h"
 #include "nsRadioInterfaceLayer.h"
@@ -673,21 +673,21 @@ BluetoothHfpManager::HandleVoiceConnecti
 
 void
 BluetoothHfpManager::HandleIccInfoChanged(uint32_t aClientId)
 {
   nsCOMPtr<nsIIccProvider> icc =
     do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
   NS_ENSURE_TRUE_VOID(icc);
 
-  nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
+  nsCOMPtr<nsIIccInfo> iccInfo;
   icc->GetIccInfo(aClientId, getter_AddRefs(iccInfo));
   NS_ENSURE_TRUE_VOID(iccInfo);
 
-  nsCOMPtr<nsIDOMMozGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
+  nsCOMPtr<nsIGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
   NS_ENSURE_TRUE_VOID(gsmIccInfo);
   gsmIccInfo->GetMsisdn(mMsisdn);
 }
 
 void
 BluetoothHfpManager::HandleShutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/bluetooth2/bluez/BluetoothHfpManager.cpp
+++ b/dom/bluetooth2/bluez/BluetoothHfpManager.cpp
@@ -22,17 +22,17 @@
 #include "nsContentUtils.h"
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsServiceManagerUtils.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/SettingChangeNotificationBinding.h"
 
 #ifdef MOZ_B2G_RIL
-#include "nsIDOMIccInfo.h"
+#include "nsIIccInfo.h"
 #include "nsIIccProvider.h"
 #include "nsIMobileConnectionInfo.h"
 #include "nsIMobileConnectionService.h"
 #include "nsIMobileNetworkInfo.h"
 #include "nsITelephonyService.h"
 #include "nsRadioInterfaceLayer.h"
 #endif
 
@@ -669,21 +669,21 @@ BluetoothHfpManager::HandleVoiceConnecti
 
 void
 BluetoothHfpManager::HandleIccInfoChanged(uint32_t aClientId)
 {
   nsCOMPtr<nsIIccProvider> icc =
     do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
   NS_ENSURE_TRUE_VOID(icc);
 
-  nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
+  nsCOMPtr<nsIIccInfo> iccInfo;
   icc->GetIccInfo(aClientId, getter_AddRefs(iccInfo));
   NS_ENSURE_TRUE_VOID(iccInfo);
 
-  nsCOMPtr<nsIDOMMozGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
+  nsCOMPtr<nsIGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
   NS_ENSURE_TRUE_VOID(gsmIccInfo);
   gsmIccInfo->GetMsisdn(mMsisdn);
 }
 #endif // MOZ_B2G_RIL
 
 void
 BluetoothHfpManager::HandleShutdown()
 {
--- a/dom/browser-element/BrowserElementParent.jsm
+++ b/dom/browser-element/BrowserElementParent.jsm
@@ -139,16 +139,18 @@ function BrowserElementParent(frameLoade
 
     defineDOMRequestMethod('getCanGoBack', 'get-can-go-back');
     defineDOMRequestMethod('getCanGoForward', 'get-can-go-forward');
     defineDOMRequestMethod('getContentDimensions', 'get-contentdimensions');
   }
 
   defineMethod('addNextPaintListener', this._addNextPaintListener);
   defineMethod('removeNextPaintListener', this._removeNextPaintListener);
+  defineNoReturnMethod('setActive', this._setActive);
+  defineMethod('getActive', 'this._getActive');
 
   let principal = this._frameElement.ownerDocument.nodePrincipal;
   let perm = Services.perms
              .testExactPermissionFromPrincipal(principal, "input-manage");
   if (perm === Ci.nsIPermissionManager.ALLOW_ACTION) {
     defineMethod('setInputMethodActive', this._setInputMethodActive);
   }
 
@@ -582,16 +584,24 @@ BrowserElementParent.prototype = {
     }
   },
 
   _setVisible: function(visible) {
     this._sendAsyncMsg('set-visible', {visible: visible});
     this._frameLoader.visible = visible;
   },
 
+  _setActive: function(active) {
+    this._frameLoader.visible = active;
+  },
+
+  _getActive: function() {
+    return this._frameLoader.visible;
+  },
+
   _sendMouseEvent: function(type, x, y, button, clickCount, modifiers) {
     this._sendAsyncMsg("send-mouse-event", {
       "type": type,
       "x": x,
       "y": y,
       "button": button,
       "clickCount": clickCount,
       "modifiers": modifiers
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -3087,21 +3087,28 @@ CanvasRenderingContext2D::GetHitRegionRe
  */
 struct MOZ_STACK_CLASS CanvasBidiProcessor : public nsBidiPresUtils::BidiProcessor
 {
   typedef CanvasRenderingContext2D::ContextState ContextState;
 
   virtual void SetText(const char16_t* text, int32_t length, nsBidiDirection direction)
   {
     mFontgrp->UpdateUserFonts(); // ensure user font generation is current
+    // adjust flags for current direction run
+    uint32_t flags = mTextRunFlags;
+    if (direction & 1) {
+      flags |= gfxTextRunFactory::TEXT_IS_RTL;
+    } else {
+      flags &= ~gfxTextRunFactory::TEXT_IS_RTL;
+    }
     mTextRun = mFontgrp->MakeTextRun(text,
                                      length,
                                      mThebes,
                                      mAppUnitsPerDevPixel,
-                                     direction==NSBIDI_RTL ? gfxTextRunFactory::TEXT_IS_RTL : 0);
+                                     flags);
   }
 
   virtual nscoord GetWidth()
   {
     gfxTextRun::Metrics textRunMetrics = mTextRun->MeasureText(0,
                                                                mTextRun->GetLength(),
                                                                mDoMeasureBoundingBox ?
                                                                  gfxFont::TIGHT_INK_EXTENTS :
@@ -3117,34 +3124,38 @@ struct MOZ_STACK_CLASS CanvasBidiProcess
     }
 
     return NSToCoordRound(textRunMetrics.mAdvanceWidth);
   }
 
   virtual void DrawText(nscoord xOffset, nscoord width)
   {
     gfxPoint point = mPt;
-    point.x += xOffset;
+    bool rtl = mTextRun->IsRightToLeft();
+    bool verticalRun = mTextRun->IsVertical();
+
+    gfxFloat& inlineCoord = verticalRun ? point.y : point.x;
+    inlineCoord += xOffset;
 
     // offset is given in terms of left side of string
-    if (mTextRun->IsRightToLeft()) {
+    if (rtl) {
       // Bug 581092 - don't use rounded pixel width to advance to
       // right-hand end of run, because this will cause different
       // glyph positioning for LTR vs RTL drawing of the same
       // glyph string on OS X and DWrite where textrun widths may
       // involve fractional pixels.
       gfxTextRun::Metrics textRunMetrics =
         mTextRun->MeasureText(0,
                               mTextRun->GetLength(),
                               mDoMeasureBoundingBox ?
                                   gfxFont::TIGHT_INK_EXTENTS :
                                   gfxFont::LOOSE_INK_EXTENTS,
                               mThebes,
                               nullptr);
-      point.x += textRunMetrics.mAdvanceWidth;
+      inlineCoord += textRunMetrics.mAdvanceWidth;
       // old code was:
       //   point.x += width * mAppUnitsPerDevPixel;
       // TODO: restore this if/when we move to fractional coords
       // throughout the text layout process
     }
 
     uint32_t numRuns;
     const gfxTextRun::GlyphRun *runs = mTextRun->GetGlyphRuns(&numRuns);
@@ -3153,16 +3164,25 @@ struct MOZ_STACK_CLASS CanvasBidiProcess
     Point baselineOrigin =
       Point(point.x * devUnitsPerAppUnit, point.y * devUnitsPerAppUnit);
 
     float advanceSum = 0;
 
     mCtx->EnsureTarget();
     for (uint32_t c = 0; c < numRuns; c++) {
       gfxFont *font = runs[c].mFont;
+
+      bool verticalFont =
+        runs[c].mOrientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
+
+      const float& baselineOriginInline =
+        verticalFont ? baselineOrigin.y : baselineOrigin.x;
+      const float& baselineOriginBlock =
+        verticalFont ? baselineOrigin.x : baselineOrigin.y;
+
       uint32_t endRun = 0;
       if (c + 1 < numRuns) {
         endRun = runs[c + 1].mCharacterOffset;
       } else {
         endRun = mTextRun->GetLength();
       }
 
       const gfxTextRun::CompressedGlyph *glyphs = mTextRun->GetCharacterGlyphs();
@@ -3170,70 +3190,100 @@ struct MOZ_STACK_CLASS CanvasBidiProcess
       RefPtr<ScaledFont> scaledFont =
         gfxPlatform::GetPlatform()->GetScaledFontForFont(mCtx->mTarget, font);
 
       if (!scaledFont) {
         // This can occur when something switched DirectWrite off.
         return;
       }
 
+      AutoRestoreTransform sidewaysRestore;
+      if (runs[c].mOrientation ==
+          gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT) {
+        sidewaysRestore.Init(mCtx->mTarget);
+        // TODO: The baseline adjustment here is kinda ad-hoc; eventually
+        // perhaps we should check for horizontal and vertical baseline data
+        // in the font, and adjust accordingly.
+        // (The same will be true for HTML text layout.)
+        const gfxFont::Metrics& metrics = mTextRun->GetFontGroup()->
+          GetFirstValidFont()->GetMetrics(gfxFont::eHorizontal);
+        mCtx->mTarget->SetTransform(mCtx->mTarget->GetTransform().Copy().
+          PreTranslate(baselineOrigin).      // translate origin for rotation
+          PreRotate(gfx::Float(M_PI / 2.0)). // turn 90deg clockwise
+          PreTranslate(-baselineOrigin).     // undo the translation
+          PreTranslate(Point(0, metrics.emAscent - metrics.emDescent) / 2));
+                              // and offset the (alphabetic) baseline of the
+                              // horizontally-shaped text from the (centered)
+                              // default baseline used for vertical
+      }
+
       RefPtr<GlyphRenderingOptions> renderingOptions = font->GetGlyphRenderingOptions();
 
       GlyphBuffer buffer;
 
       std::vector<Glyph> glyphBuf;
 
+      // TODO:
+      // This more-or-less duplicates the code found in gfxTextRun::Draw
+      // and the gfxFont methods that uses (Draw, DrawGlyphs, DrawOneGlyph);
+      // it would be nice to refactor and share that code.
       for (uint32_t i = runs[c].mCharacterOffset; i < endRun; i++) {
         Glyph newGlyph;
+
+        float& inlinePos =
+          verticalFont ? newGlyph.mPosition.y : newGlyph.mPosition.x;
+        float& blockPos =
+          verticalFont ? newGlyph.mPosition.x : newGlyph.mPosition.y;
+
         if (glyphs[i].IsSimpleGlyph()) {
           newGlyph.mIndex = glyphs[i].GetSimpleGlyph();
-          if (mTextRun->IsRightToLeft()) {
-            newGlyph.mPosition.x = baselineOrigin.x - advanceSum -
+          if (rtl) {
+            inlinePos = baselineOriginInline - advanceSum -
               glyphs[i].GetSimpleAdvance() * devUnitsPerAppUnit;
           } else {
-            newGlyph.mPosition.x = baselineOrigin.x + advanceSum;
+            inlinePos = baselineOriginInline + advanceSum;
           }
-          newGlyph.mPosition.y = baselineOrigin.y;
+          blockPos = baselineOriginBlock;
           advanceSum += glyphs[i].GetSimpleAdvance() * devUnitsPerAppUnit;
           glyphBuf.push_back(newGlyph);
           continue;
         }
 
         if (!glyphs[i].GetGlyphCount()) {
           continue;
         }
 
-        gfxTextRun::DetailedGlyph *detailedGlyphs =
-          mTextRun->GetDetailedGlyphs(i);
+        const gfxTextRun::DetailedGlyph *d = mTextRun->GetDetailedGlyphs(i);
 
         if (glyphs[i].IsMissing()) {
           newGlyph.mIndex = 0;
-          if (mTextRun->IsRightToLeft()) {
-            newGlyph.mPosition.x = baselineOrigin.x - advanceSum -
-              detailedGlyphs[0].mAdvance * devUnitsPerAppUnit;
+          if (rtl) {
+            inlinePos = baselineOriginInline - advanceSum -
+              d->mAdvance * devUnitsPerAppUnit;
           } else {
-            newGlyph.mPosition.x = baselineOrigin.x + advanceSum;
+            inlinePos = baselineOriginInline + advanceSum;
           }
-          newGlyph.mPosition.y = baselineOrigin.y;
-          advanceSum += detailedGlyphs[0].mAdvance * devUnitsPerAppUnit;
+          blockPos = baselineOriginBlock;
+          advanceSum += d->mAdvance * devUnitsPerAppUnit;
           glyphBuf.push_back(newGlyph);
           continue;
         }
 
-        for (uint32_t c = 0; c < glyphs[i].GetGlyphCount(); c++) {
-          newGlyph.mIndex = detailedGlyphs[c].mGlyphID;
-          if (mTextRun->IsRightToLeft()) {
-            newGlyph.mPosition.x = baselineOrigin.x + detailedGlyphs[c].mXOffset * devUnitsPerAppUnit -
-              advanceSum - detailedGlyphs[c].mAdvance * devUnitsPerAppUnit;
+        for (uint32_t c = 0; c < glyphs[i].GetGlyphCount(); c++, d++) {
+          newGlyph.mIndex = d->mGlyphID;
+          if (rtl) {
+            inlinePos = baselineOriginInline - advanceSum -
+              d->mAdvance * devUnitsPerAppUnit;
           } else {
-            newGlyph.mPosition.x = baselineOrigin.x + detailedGlyphs[c].mXOffset * devUnitsPerAppUnit + advanceSum;
+            inlinePos = baselineOriginInline + advanceSum;
           }
-          newGlyph.mPosition.y = baselineOrigin.y + detailedGlyphs[c].mYOffset * devUnitsPerAppUnit;
+          inlinePos += d->mXOffset * devUnitsPerAppUnit;
+          blockPos = baselineOriginBlock + d->mYOffset * devUnitsPerAppUnit;
           glyphBuf.push_back(newGlyph);
-          advanceSum += detailedGlyphs[c].mAdvance * devUnitsPerAppUnit;
+          advanceSum += d->mAdvance * devUnitsPerAppUnit;
         }
       }
 
       if (!glyphBuf.size()) {
         // This may happen for glyph runs for a 0 size font.
         continue;
       }
 
@@ -3298,16 +3348,19 @@ struct MOZ_STACK_CLASS CanvasBidiProcess
   CanvasRenderingContext2D::TextDrawOperation mOp;
 
   // context state
   ContextState *mState;
 
   // union of bounding boxes of all runs, needed for shadows
   gfxRect mBoundingBox;
 
+  // flags to use when creating textrun, based on CSS style
+  uint32_t mTextRunFlags;
+
   // true iff the bounding box should be measured
   bool mDoMeasureBoundingBox;
 };
 
 nsresult
 CanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText,
                                             float aX,
                                             float aY,
@@ -3337,19 +3390,20 @@ CanvasRenderingContext2D::DrawOrMeasureT
 
   // replace all the whitespace characters with U+0020 SPACE
   nsAutoString textToDraw(aRawText);
   TextReplaceWhitespaceCharacters(textToDraw);
 
   // for now, default to ltr if not in doc
   bool isRTL = false;
 
+  nsRefPtr<nsStyleContext> canvasStyle;
   if (mCanvasElement && mCanvasElement->IsInDoc()) {
     // try to find the closest context
-    nsRefPtr<nsStyleContext> canvasStyle =
+    canvasStyle =
       nsComputedDOMStyle::GetStyleContextForElement(mCanvasElement,
                                                     nullptr,
                                                     presShell);
     if (!canvasStyle) {
       return NS_ERROR_FAILURE;
     }
 
     isRTL = canvasStyle->StyleVisibility()->mDirection ==
@@ -3374,16 +3428,24 @@ CanvasRenderingContext2D::DrawOrMeasureT
 
   const ContextState &state = CurrentState();
 
   // This is only needed to know if we can know the drawing bounding box easily.
   bool doCalculateBounds = NeedToCalculateBounds();
 
   CanvasBidiProcessor processor;
 
+  // If we don't have a style context, we can't set up vertical-text flags
+  // (for now, at least; perhaps we need new Canvas API to control this).
+  processor.mTextRunFlags = canvasStyle ?
+    nsLayoutUtils::GetTextRunFlagsForStyle(canvasStyle,
+                                           canvasStyle->StyleFont(),
+                                           canvasStyle->StyleText(),
+                                           0) : 0;
+
   GetAppUnitsValues(&processor.mAppUnitsPerDevPixel, nullptr);
   processor.mPt = gfxPoint(aX, aY);
   processor.mThebes =
     new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget());
 
   // If we don't have a target then we don't have a transform. A target won't
   // be needed in the case where we're measuring the text size. This allows
   // to avoid creating a target if it's only being used to measure text sizes.
@@ -3440,17 +3502,19 @@ CanvasRenderingContext2D::DrawOrMeasureT
     anchorX = 1;
   }
 
   processor.mPt.x -= anchorX * totalWidth;
 
   // offset pt.y based on text baseline
   processor.mFontgrp->UpdateUserFonts(); // ensure user font generation is current
   const gfxFont::Metrics& fontMetrics =
-    processor.mFontgrp->GetFirstValidFont()->GetMetrics(gfxFont::eHorizontal); // XXX vertical?
+    processor.mFontgrp->GetFirstValidFont()->GetMetrics(
+      processor.mTextRun->IsVertical() ? gfxFont::eVertical :
+                                         gfxFont::eHorizontal);
 
   gfxFloat anchorY;
 
   switch (state.textBaseline)
   {
   case TextBaseline::HANGING:
       // fall through; best we can do with the information available
   case TextBaseline::TOP:
--- a/dom/icc/Icc.cpp
+++ b/dom/icc/Icc.cpp
@@ -1,45 +1,60 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "Icc.h"
+#include "mozilla/dom/Icc.h"
 
+#include "mozilla/dom/DOMRequest.h"
+#include "mozilla/dom/IccInfo.h"
 #include "mozilla/dom/MozIccBinding.h"
 #include "mozilla/dom/MozStkCommandEvent.h"
-#include "mozilla/dom/DOMRequest.h"
 #include "mozilla/dom/ScriptSettings.h"
-#include "nsIDOMIccInfo.h"
+#include "nsIIccInfo.h"
+#include "nsIIccProvider.h"
 #include "nsJSON.h"
 #include "nsRadioInterfaceLayer.h"
 #include "nsServiceManagerUtils.h"
 
 using namespace mozilla::dom;
 
-Icc::Icc(nsPIDOMWindow* aWindow, long aClientId, const nsAString& aIccId)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(Icc, DOMEventTargetHelper, mIccInfo)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Icc)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+NS_IMPL_ADDREF_INHERITED(Icc, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(Icc, DOMEventTargetHelper)
+
+Icc::Icc(nsPIDOMWindow* aWindow, long aClientId, nsIIccInfo* aIccInfo)
   : mLive(true)
   , mClientId(aClientId)
-  , mIccId(aIccId)
 {
   SetIsDOMBinding();
   BindToOwner(aWindow);
 
   mProvider = do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
 
+  if (aIccInfo) {
+    aIccInfo->GetIccid(mIccId);
+    UpdateIccInfo(aIccInfo);
+  }
+
   // Not being able to acquire the provider isn't fatal since we check
   // for it explicitly below.
   if (!mProvider) {
     NS_WARNING("Could not acquire nsIIccProvider!");
   }
 }
 
 void
 Icc::Shutdown()
 {
+  mIccInfo.SetNull();
   mProvider = nullptr;
   mLive = false;
 }
 
 nsresult
 Icc::NotifyEvent(const nsAString& aName)
 {
   return DispatchTrustedEvent(aName);
@@ -69,40 +84,62 @@ Icc::NotifyStkEvent(const nsAString& aNa
   init.mCommand = value;
 
   nsRefPtr<MozStkCommandEvent> event =
     MozStkCommandEvent::Constructor(this, aName, init);
 
   return DispatchTrustedEvent(event);
 }
 
+void
+Icc::UpdateIccInfo(nsIIccInfo* aIccInfo)
+{
+  if (!aIccInfo) {
+    mIccInfo.SetNull();
+    return;
+  }
+
+  nsCOMPtr<nsIGsmIccInfo> gsmIccInfo(do_QueryInterface(aIccInfo));
+  if (gsmIccInfo) {
+    if (mIccInfo.IsNull() || !mIccInfo.Value().IsMozGsmIccInfo()) {
+      mIccInfo.SetValue().SetAsMozGsmIccInfo() = new GsmIccInfo(GetOwner());
+    }
+    mIccInfo.Value().GetAsMozGsmIccInfo().get()->Update(gsmIccInfo);
+    return;
+  }
+
+  nsCOMPtr<nsICdmaIccInfo> cdmaIccInfo(do_QueryInterface(aIccInfo));
+  if (cdmaIccInfo) {
+    if (mIccInfo.IsNull() || !mIccInfo.Value().IsMozCdmaIccInfo()) {
+      mIccInfo.SetValue().SetAsMozCdmaIccInfo() = new CdmaIccInfo(GetOwner());
+    }
+    mIccInfo.Value().GetAsMozCdmaIccInfo().get()->Update(cdmaIccInfo);
+    return;
+  }
+
+  if (mIccInfo.IsNull() || !mIccInfo.Value().IsMozIccInfo()) {
+    mIccInfo.SetValue().SetAsMozIccInfo() = new IccInfo(GetOwner());
+  }
+  mIccInfo.Value().GetAsMozIccInfo().get()->Update(aIccInfo);
+}
+
 // WrapperCache
 
 JSObject*
 Icc::WrapObject(JSContext* aCx)
 {
   return MozIccBinding::Wrap(aCx, this);
 }
 
 // MozIcc WebIDL
 
-already_AddRefed<nsIDOMMozIccInfo>
-Icc::GetIccInfo() const
+void
+Icc::GetIccInfo(Nullable<OwningMozIccInfoOrMozGsmIccInfoOrMozCdmaIccInfo>& aIccInfo) const
 {
-  if (!mProvider) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
-  nsresult rv = mProvider->GetIccInfo(mClientId, getter_AddRefs(iccInfo));
-  if (NS_FAILED(rv)) {
-    return nullptr;
-  }
-
-  return iccInfo.forget();
+  aIccInfo = mIccInfo;
 }
 
 void
 Icc::GetCardState(nsString& aCardState) const
 {
   aCardState.SetIsVoid(true);
 
   if (!mProvider) {
--- a/dom/icc/Icc.h
+++ b/dom/icc/Icc.h
@@ -1,58 +1,66 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_Icc_h
 #define mozilla_dom_Icc_h
 
+#include "mozilla/dom/UnionTypes.h"
 #include "mozilla/DOMEventTargetHelper.h"
-#include "nsIIccProvider.h"
+
+class nsIIccInfo;
+class nsIIccProvider;
 
 namespace mozilla {
 namespace dom {
 
 class DOMRequest;
 
 class Icc MOZ_FINAL : public DOMEventTargetHelper
 {
 public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Icc, DOMEventTargetHelper)
   NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper)
 
-  Icc(nsPIDOMWindow* aWindow, long aClientId, const nsAString& aIccId);
+  Icc(nsPIDOMWindow* aWindow, long aClientId, nsIIccInfo* aIccInfo);
 
   void
   Shutdown();
 
   nsresult
   NotifyEvent(const nsAString& aName);
 
   nsresult
   NotifyStkEvent(const nsAString& aName, const nsAString& aMessage);
 
   nsString
   GetIccId()
   {
     return mIccId;
   }
 
+  void
+  UpdateIccInfo(nsIIccInfo* aIccInfo);
+
   nsPIDOMWindow*
   GetParentObject() const
   {
     return GetOwner();
   }
 
   // WrapperCache
   virtual JSObject*
   WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   // MozIcc WebIDL
-  already_AddRefed<nsIDOMMozIccInfo>
-  GetIccInfo() const;
+  void
+  GetIccInfo(Nullable<OwningMozIccInfoOrMozGsmIccInfoOrMozCdmaIccInfo>& aIccInfo) const;
 
   void
   GetCardState(nsString& aCardState) const;
 
   void
   SendStkResponse(const JSContext* aCx, JS::Handle<JS::Value> aCommand,
                   JS::Handle<JS::Value> aResponse, ErrorResult& aRv);
 
@@ -111,14 +119,15 @@ public:
 
 private:
   bool mLive;
   uint32_t mClientId;
   nsString mIccId;
   // mProvider is a xpcom service and will be released at shutdown, so it
   // doesn't need to be cycle collected.
   nsCOMPtr<nsIIccProvider> mProvider;
+  Nullable<OwningMozIccInfoOrMozGsmIccInfoOrMozCdmaIccInfo> mIccInfo;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_icc_Icc_h
new file mode 100644
--- /dev/null
+++ b/dom/icc/IccInfo.cpp
@@ -0,0 +1,230 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/IccInfo.h"
+
+#include "nsPIDOMWindow.h"
+
+#define CONVERT_STRING_TO_NULLABLE_ENUM(_string, _enumType, _enum)      \
+{                                                                       \
+  uint32_t i = 0;                                                       \
+  for (const EnumEntry* entry = _enumType##Values::strings;             \
+       entry->value;                                                    \
+       ++entry, ++i) {                                                  \
+    if (_string.EqualsASCII(entry->value)) {                            \
+      _enum.SetValue(static_cast<_enumType>(i));                        \
+    }                                                                   \
+  }                                                                     \
+}
+
+using namespace mozilla::dom;
+
+// IccInfo
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(IccInfo, mWindow, mIccInfo)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(IccInfo)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(IccInfo)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IccInfo)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+IccInfo::IccInfo(nsPIDOMWindow* aWindow)
+  : mWindow(aWindow)
+{
+  SetIsDOMBinding();
+}
+
+void
+IccInfo::Update(nsIIccInfo* aInfo)
+{
+  mIccInfo = aInfo;
+}
+
+JSObject*
+IccInfo::WrapObject(JSContext* aCx)
+{
+  return MozIccInfoBinding::Wrap(aCx, this);
+}
+
+Nullable<IccType>
+IccInfo::GetIccType() const
+{
+  if (!mIccInfo) {
+    return Nullable<IccType>();
+  }
+
+  nsAutoString type;
+  Nullable<IccType> iccType;
+
+  mIccInfo->GetIccType(type);
+  CONVERT_STRING_TO_NULLABLE_ENUM(type, IccType, iccType);
+
+  return iccType;
+}
+
+void
+IccInfo::GetIccid(nsAString& aIccId) const
+{
+  if (!mIccInfo) {
+    aIccId.SetIsVoid(true);
+    return;
+  }
+
+  mIccInfo->GetIccid(aIccId);
+}
+
+void
+IccInfo::GetMcc(nsAString& aMcc) const
+{
+  if (!mIccInfo) {
+    aMcc.SetIsVoid(true);
+    return;
+  }
+
+  mIccInfo->GetMcc(aMcc);
+}
+
+void
+IccInfo::GetMnc(nsAString& aMnc) const
+{
+  if (!mIccInfo) {
+    aMnc.SetIsVoid(true);
+    return;
+  }
+
+  mIccInfo->GetMnc(aMnc);
+}
+
+void
+IccInfo::GetSpn(nsAString& aSpn) const
+{
+  if (!mIccInfo) {
+    aSpn.SetIsVoid(true);
+    return;
+  }
+
+  mIccInfo->GetSpn(aSpn);
+}
+
+bool
+IccInfo::IsDisplayNetworkNameRequired() const
+{
+  if (!mIccInfo) {
+    return false;
+  }
+
+  bool isDisplayNetworkNameRequired;
+  mIccInfo->GetIsDisplayNetworkNameRequired(&isDisplayNetworkNameRequired);
+
+  return isDisplayNetworkNameRequired;
+}
+
+bool
+IccInfo::IsDisplaySpnRequired() const
+{
+  if (!mIccInfo) {
+    return false;
+  }
+
+  bool isDisplaySpnRequired;
+  mIccInfo->GetIsDisplaySpnRequired(&isDisplaySpnRequired);
+
+  return isDisplaySpnRequired;
+}
+
+// GsmIccInfo
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(GsmIccInfo, IccInfo, mGsmIccInfo)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(GsmIccInfo)
+NS_INTERFACE_MAP_END_INHERITING(IccInfo)
+
+NS_IMPL_ADDREF_INHERITED(GsmIccInfo, IccInfo)
+NS_IMPL_RELEASE_INHERITED(GsmIccInfo, IccInfo)
+
+GsmIccInfo::GsmIccInfo(nsPIDOMWindow* aWindow)
+  : IccInfo(aWindow)
+{
+  SetIsDOMBinding();
+}
+
+void
+GsmIccInfo::Update(nsIGsmIccInfo* aInfo)
+{
+  IccInfo::Update(aInfo);
+  mGsmIccInfo = aInfo;
+}
+
+JSObject*
+GsmIccInfo::WrapObject(JSContext* aCx)
+{
+  return MozGsmIccInfoBinding::Wrap(aCx, this);
+}
+
+void
+GsmIccInfo::GetMsisdn(nsAString& aMsisdn) const
+{
+  if (!mGsmIccInfo) {
+    aMsisdn.SetIsVoid(true);
+    return;
+  }
+
+  mGsmIccInfo->GetMsisdn(aMsisdn);
+}
+
+// CdmaIccInfo
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(CdmaIccInfo, IccInfo, mCdmaIccInfo)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(CdmaIccInfo)
+NS_INTERFACE_MAP_END_INHERITING(IccInfo)
+
+NS_IMPL_ADDREF_INHERITED(CdmaIccInfo, IccInfo)
+NS_IMPL_RELEASE_INHERITED(CdmaIccInfo, IccInfo)
+
+CdmaIccInfo::CdmaIccInfo(nsPIDOMWindow* aWindow)
+  : IccInfo(aWindow)
+{
+  SetIsDOMBinding();
+}
+
+void
+CdmaIccInfo::Update(nsICdmaIccInfo* aInfo)
+{
+  IccInfo::Update(aInfo);
+  mCdmaIccInfo = aInfo;
+}
+
+JSObject*
+CdmaIccInfo::WrapObject(JSContext* aCx)
+{
+  return MozCdmaIccInfoBinding::Wrap(aCx, this);
+}
+
+void
+CdmaIccInfo::GetMdn(nsAString& aMdn) const
+{
+  if (!mCdmaIccInfo) {
+    aMdn.SetIsVoid(true);
+    return;
+  }
+
+  mCdmaIccInfo->GetMdn(aMdn);
+}
+
+int32_t
+CdmaIccInfo::PrlVersion() const
+{
+  if (!mCdmaIccInfo) {
+    return 0;
+  }
+
+  int32_t prlVersion;
+  mCdmaIccInfo->GetPrlVersion(&prlVersion);
+
+  return prlVersion;
+}
new file mode 100644
--- /dev/null
+++ b/dom/icc/IccInfo.h
@@ -0,0 +1,128 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_IccInfo_h
+#define mozilla_dom_IccInfo_h
+
+#include "MozIccInfoBinding.h"
+#include "nsIIccInfo.h"
+#include "nsWrapperCache.h"
+
+class nsPIDOMWindow;
+
+namespace mozilla {
+namespace dom {
+
+class IccInfo : public nsISupports
+              , public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IccInfo)
+
+  explicit IccInfo(nsPIDOMWindow* aWindow);
+
+  void
+  Update(nsIIccInfo* aInfo);
+
+  nsPIDOMWindow*
+  GetParentObject() const
+  {
+    return mWindow;
+  }
+
+  // WrapperCache
+  virtual JSObject*
+  WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  // WebIDL interface
+  Nullable<IccType>
+  GetIccType() const;
+
+  void
+  GetIccid(nsAString& aIccId) const;
+
+  void
+  GetMcc(nsAString& aMcc) const;
+
+  void
+  GetMnc(nsAString& aMnc) const;
+
+  void
+  GetSpn(nsAString& aSpn) const;
+
+  bool
+  IsDisplayNetworkNameRequired() const;
+
+  bool
+  IsDisplaySpnRequired() const;
+
+protected:
+  ~IccInfo() {}
+
+protected:
+  nsCOMPtr<nsPIDOMWindow> mWindow;
+  nsCOMPtr<nsIIccInfo> mIccInfo;
+};
+
+class GsmIccInfo MOZ_FINAL : public IccInfo
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(GsmIccInfo, IccInfo)
+
+  explicit GsmIccInfo(nsPIDOMWindow* aWindow);
+
+  void
+  Update(nsIGsmIccInfo* aInfo);
+
+  // WrapperCache
+  virtual JSObject*
+  WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  // MozCdmaIccInfo WebIDL
+  void
+  GetMsisdn(nsAString& aMsisdn) const;
+
+private:
+  ~GsmIccInfo() {}
+
+private:
+  nsCOMPtr<nsIGsmIccInfo> mGsmIccInfo;
+};
+
+class CdmaIccInfo MOZ_FINAL : public IccInfo
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CdmaIccInfo, IccInfo)
+
+  explicit CdmaIccInfo(nsPIDOMWindow* aWindow);
+
+  void
+  Update(nsICdmaIccInfo* aInfo);
+
+  // WrapperCache
+  virtual JSObject*
+  WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  // MozCdmaIccInfo WebIDL
+  void
+  GetMdn(nsAString& aMdn) const;
+
+  int32_t
+  PrlVersion() const;
+
+private:
+  ~CdmaIccInfo() {}
+
+private:
+  nsCOMPtr<nsICdmaIccInfo> mCdmaIccInfo;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_IccInfo_h
+
--- a/dom/icc/IccListener.cpp
+++ b/dom/icc/IccListener.cpp
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "IccListener.h"
 
 #include "Icc.h"
 #include "IccManager.h"
 #include "nsIDOMClassInfo.h"
-#include "nsIDOMIccInfo.h"
+#include "nsIIccInfo.h"
 #include "nsRadioInterfaceLayer.h"
 
 using namespace mozilla::dom;
 
 NS_IMPL_ISUPPORTS(IccListener, nsIIccListener)
 
 IccListener::IccListener(IccManager* aIccManager, uint32_t aClientId)
   : mClientId(aClientId)
@@ -22,23 +22,23 @@ IccListener::IccListener(IccManager* aIc
 
   mProvider = do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
 
   if (!mProvider) {
     NS_WARNING("Could not acquire nsIIccProvider!");
     return;
   }
 
-  nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
+  nsCOMPtr<nsIIccInfo> iccInfo;
   mProvider->GetIccInfo(mClientId, getter_AddRefs(iccInfo));
   if (iccInfo) {
     nsString iccId;
     iccInfo->GetIccid(iccId);
     if (!iccId.IsEmpty()) {
-      mIcc = new Icc(mIccManager->GetOwner(), mClientId, iccId);
+      mIcc = new Icc(mIccManager->GetOwner(), mClientId, iccInfo);
     }
   }
 
   DebugOnly<nsresult> rv = mProvider->RegisterIccMsg(mClientId, this);
   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
                    "Failed registering icc messages with provider");
 }
 
@@ -93,35 +93,36 @@ IccListener::NotifyCardStateChanged()
   }
 
   return mIcc->NotifyEvent(NS_LITERAL_STRING("cardstatechange"));
 }
 
 NS_IMETHODIMP
 IccListener::NotifyIccInfoChanged()
 {
-  nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
+  nsCOMPtr<nsIIccInfo> iccInfo;
   mProvider->GetIccInfo(mClientId, getter_AddRefs(iccInfo));
 
   // Create/delete icc object based on current iccInfo.
   // 1. If the mIcc is nullptr and iccInfo has valid data, create icc object and
   //    notify mIccManager a new icc is added.
   // 2. If the mIcc is not nullptr and iccInfo becomes to null, delete existed
   //    icc object and notify mIccManager the icc is removed.
   if (!mIcc) {
     if (iccInfo) {
       nsString iccId;
       iccInfo->GetIccid(iccId);
       if (!iccId.IsEmpty()) {
-        mIcc = new Icc(mIccManager->GetOwner(), mClientId, iccId);
+        mIcc = new Icc(mIccManager->GetOwner(), mClientId, iccInfo);
         mIccManager->NotifyIccAdd(iccId);
         mIcc->NotifyEvent(NS_LITERAL_STRING("iccinfochange"));
       }
     }
   } else {
+    mIcc->UpdateIccInfo(iccInfo);
     mIcc->NotifyEvent(NS_LITERAL_STRING("iccinfochange"));
     if (!iccInfo) {
       nsString iccId = mIcc->GetIccId();
       mIcc->Shutdown();
       mIcc = nullptr;
       mIccManager->NotifyIccRemove(iccId);
     }
   }
--- a/dom/icc/IccManager.cpp
+++ b/dom/icc/IccManager.cpp
@@ -3,17 +3,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "IccManager.h"
 #include "mozilla/dom/MozIccManagerBinding.h"
 #include "Icc.h"
 #include "IccListener.h"
 #include "mozilla/dom/IccChangeEvent.h"
 #include "mozilla/Preferences.h"
-#include "nsIDOMIccInfo.h"
+#include "nsIIccInfo.h"
 
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(IccManager)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IccManager,
                                                   DOMEventTargetHelper)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
--- a/dom/icc/interfaces/moz.build
+++ b/dom/icc/interfaces/moz.build
@@ -1,12 +1,12 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPIDL_SOURCES += [
-    'nsIDOMIccInfo.idl',
+    'nsIIccInfo.idl',
     'nsIIccProvider.idl',
 ]
 
 XPIDL_MODULE = 'dom_icc'
rename from dom/icc/interfaces/nsIDOMIccInfo.idl
rename to dom/icc/interfaces/nsIIccInfo.idl
--- a/dom/icc/interfaces/nsIDOMIccInfo.idl
+++ b/dom/icc/interfaces/nsIIccInfo.idl
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
-[scriptable, uuid(dd9f229c-e5a6-453a-8388-950af0ff9918)]
-interface nsIDOMMozIccInfo : nsISupports
+[scriptable, uuid(3ba11a90-34e0-11e4-8c21-0800200c9a66)]
+interface nsIIccInfo : nsISupports
 {
   /**
    * Integrated Circuit Card Type.
    *
    * Possible values: null(unknown), "sim", "usim", "csim", ruim".
    */
   readonly attribute DOMString iccType;
 
@@ -40,28 +40,28 @@ interface nsIDOMMozIccInfo : nsISupports
   readonly attribute boolean isDisplayNetworkNameRequired;
 
   /**
    * Service provider name must be a part of displayed carrier name.
    */
   readonly attribute boolean isDisplaySpnRequired;
 };
 
-[scriptable, uuid(3c237e39-7af3-4748-baf4-4a3b6c3e0e66)]
-interface nsIDOMMozGsmIccInfo : nsIDOMMozIccInfo
+[scriptable, uuid(6c9c78b0-34e0-11e4-8c21-0800200c9a66)]
+interface nsIGsmIccInfo : nsIIccInfo
 {
   /**
    * Mobile Station ISDN Number (MSISDN) of the subscriber, aka
    * his phone number.
    */
   readonly attribute DOMString msisdn;
 };
 
-[scriptable, uuid(7e937d09-4d1d-43c5-96d8-c91396022809)]
-interface nsIDOMMozCdmaIccInfo : nsIDOMMozIccInfo
+[scriptable, uuid(7452f570-34e0-11e4-8c21-0800200c9a66)]
+interface nsICdmaIccInfo : nsIIccInfo
 {
   /**
    * Mobile Directory Number (MDN) of the subscriber, aka his phone number.
    */
   readonly attribute DOMString mdn;
 
   /**
    * Preferred Roaming List (PRL) version of the subscriber.
--- a/dom/icc/interfaces/nsIIccProvider.idl
+++ b/dom/icc/interfaces/nsIIccProvider.idl
@@ -1,45 +1,45 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIDOMDOMRequest;
-interface nsIDOMMozIccInfo;
 interface nsIDOMWindow;
+interface nsIIccInfo;
 
 [scriptable, uuid(7c0ada3d-d8d4-493e-9243-fa3df39855e4)]
 interface nsIIccListener : nsISupports
 {
   void notifyStkCommand(in DOMString aMessage);
   void notifyStkSessionEnd();
   void notifyCardStateChanged();
   void notifyIccInfoChanged();
 };
 
 /**
  * XPCOM component (in the content process) that provides the ICC information.
  */
-[scriptable, uuid(7c67ab92-52a3-4e11-995c-c0ad2f66c4cb)]
+[scriptable, uuid(1afa72d0-3d70-11e4-916c-0800200c9a66)]
 interface nsIIccProvider : nsISupports
 {
   /**
    * Called when a content process registers receiving unsolicited messages from
    * RadioInterfaceLayer in the chrome process. Only a content process that has
    * the 'mobileconnection' permission is allowed to register.
    */
   void registerIccMsg(in unsigned long clientId, in nsIIccListener listener);
   void unregisterIccMsg(in unsigned long clientId, in nsIIccListener listener);
 
   /**
    * UICC Information
    */
-  nsIDOMMozIccInfo getIccInfo(in unsigned long clientId);
+  nsIIccInfo getIccInfo(in unsigned long clientId);
 
   /**
    * Card State
    */
   DOMString getCardState(in unsigned long clientId);
 
   /**
    * STK interfaces.
--- a/dom/icc/moz.build
+++ b/dom/icc/moz.build
@@ -3,21 +3,23 @@
 # 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/.
 
 DIRS += ['interfaces']
 
 EXPORTS.mozilla.dom += [
     'Icc.h',
+    'IccInfo.h',
     'IccManager.h',
 ]
 
 UNIFIED_SOURCES += [
     'Icc.cpp',
+    "IccInfo.cpp",
     'IccListener.cpp',
     'IccManager.cpp',
 ]
 
 FAIL_ON_WARNINGS = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
new file mode 100644
--- a/dom/icc/tests/marionette/test_icc_info.js
+++ b/dom/icc/tests/marionette/test_icc_info.js
@@ -30,17 +30,17 @@ function setEmulatorMccMnc(mcc, mnc) {
 /* Basic test */
 taskHelper.push(function basicTest() {
   let iccInfo = icc.iccInfo;
 
   // The emulator's hard coded iccid value.
   // See it here {B2G_HOME}/external/qemu/telephony/sim_card.c#L299.
   is(iccInfo.iccid, 89014103211118510720);
 
-  if (iccInfo instanceof Ci.nsIDOMMozGsmIccInfo) {
+  if (iccInfo instanceof MozGsmIccInfo) {
     log("Test Gsm IccInfo");
     is(iccInfo.iccType, "sim");
     is(iccInfo.spn, "Android");
     // The emulator's hard coded mcc and mnc codes.
     // See it here {B2G_HOME}/external/qemu/telephony/android_modem.c#L2465.
     is(iccInfo.mcc, 310);
     is(iccInfo.mnc, 260);
     // Phone number is hardcoded in MSISDN
--- a/dom/mobilemessage/gonk/MmsService.js
+++ b/dom/mobilemessage/gonk/MmsService.js
@@ -334,32 +334,32 @@ MmsConnection.prototype = {
     if (DEBUG) debug("isVoiceRoaming = " + isRoaming);
     return isRoaming;
   },
 
   /**
    * Get phone number from iccInfo.
    *
    * If the icc card is gsm card, the phone number is in msisdn.
-   * @see nsIDOMMozGsmIccInfo
+   * @see nsIGsmIccInfo
    *
    * Otherwise, the phone number is in mdn.
-   * @see nsIDOMMozCdmaIccInfo
+   * @see nsICdmaIccInfo
    */
   getPhoneNumber: function() {
     let number;
     // Get the proper IccInfo based on the current card type.
     try {
       let iccInfo = null;
       let baseIccInfo = this.radioInterface.rilContext.iccInfo;
       if (baseIccInfo.iccType === 'ruim' || baseIccInfo.iccType === 'csim') {
-        iccInfo = baseIccInfo.QueryInterface(Ci.nsIDOMMozCdmaIccInfo);
+        iccInfo = baseIccInfo.QueryInterface(Ci.nsICdmaIccInfo);
         number = iccInfo.mdn;
       } else {
-        iccInfo = baseIccInfo.QueryInterface(Ci.nsIDOMMozGsmIccInfo);
+        iccInfo = baseIccInfo.QueryInterface(Ci.nsIGsmIccInfo);
         number = iccInfo.msisdn;
       }
     } catch (e) {
       if (DEBUG) {
        debug("Exception - QueryInterface failed on iccinfo for GSM/CDMA info");
       }
       return null;
     }
--- a/dom/system/gonk/GonkGPSGeolocationProvider.cpp
+++ b/dom/system/gonk/GonkGPSGeolocationProvider.cpp
@@ -29,17 +29,17 @@
 #include "nsIObserverService.h"
 #include "nsJSUtils.h"
 #include "nsPrintfCString.h"
 #include "nsServiceManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "prtime.h"
 
 #ifdef MOZ_B2G_RIL
-#include "nsIDOMIccInfo.h"
+#include "nsIIccInfo.h"
 #include "nsIMobileConnectionInfo.h"
 #include "nsIMobileConnectionService.h"
 #include "nsIMobileCellInfo.h"
 #include "nsIRadioInterfaceLayer.h"
 #endif
 
 #ifdef AGPS_TYPE_INVALID
 #define AGPS_HAVE_DUAL_APN
@@ -454,20 +454,20 @@ GonkGPSGeolocationProvider::RequestSetID
   if (rilCtx) {
     nsAutoString id;
     if (flags & AGPS_RIL_REQUEST_SETID_IMSI) {
       type = AGPS_SETID_TYPE_IMSI;
       rilCtx->GetImsi(id);
     }
 
     if (flags & AGPS_RIL_REQUEST_SETID_MSISDN) {
-      nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
+      nsCOMPtr<nsIIccInfo> iccInfo;
       rilCtx->GetIccInfo(getter_AddRefs(iccInfo));
       if (iccInfo) {
-        nsCOMPtr<nsIDOMMozGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
+        nsCOMPtr<nsIGsmIccInfo> gsmIccInfo = do_QueryInterface(iccInfo);
         if (gsmIccInfo) {
           type = AGPS_SETID_TYPE_MSISDN;
           gsmIccInfo->GetMsisdn(id);
         }
       }
     }
 
     NS_ConvertUTF16toUTF8 idBytes(id);
@@ -489,17 +489,17 @@ GonkGPSGeolocationProvider::SetReference
   mRadioInterface->GetRilContext(getter_AddRefs(rilCtx));
 
   AGpsRefLocation location;
 
   // TODO: Bug 772750 - get mobile connection technology from rilcontext
   location.type = AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
 
   if (rilCtx) {
-    nsCOMPtr<nsIDOMMozIccInfo> iccInfo;
+    nsCOMPtr<nsIIccInfo> iccInfo;
     rilCtx->GetIccInfo(getter_AddRefs(iccInfo));
     if (iccInfo) {
       nsresult result;
       nsAutoString mcc, mnc;
 
       iccInfo->GetMcc(mcc);
       iccInfo->GetMnc(mnc);
 
--- a/dom/system/gonk/RILContentHelper.js
+++ b/dom/system/gonk/RILContentHelper.js
@@ -36,22 +36,16 @@ const kPrefRilDebuggingEnabled = "ril.de
 
 let DEBUG;
 function debug(s) {
   dump("-*- RILContentHelper: " + s + "\n");
 }
 
 const RILCONTENTHELPER_CID =
   Components.ID("{472816e1-1fd6-4405-996c-806f9ea68174}");
-const ICCINFO_CID =
-  Components.ID("{39d64d90-26a6-11e4-8c21-0800200c9a66}");
-const GSMICCINFO_CID =
-  Components.ID("{e0fa785b-ad3f-46ed-bc56-fcb0d6fe4fa8}");
-const CDMAICCINFO_CID =
-  Components.ID("{3d1f844f-9ec5-48fb-8907-aed2e5421709}");
 const ICCCARDLOCKERROR_CID =
   Components.ID("{08a71987-408c-44ff-93fd-177c0a85c3dd}");
 
 const RIL_IPC_MSG_NAMES = [
   "RIL:CardStateChanged",
   "RIL:IccInfoChanged",
   "RIL:CardLockResult",
   "RIL:CardLockRetryCount",
@@ -103,66 +97,45 @@ function MobileIccCardLockRetryCount(opt
 MobileIccCardLockRetryCount.prototype = {
   __exposedProps__ : {lockType: 'r',
                       retryCount: 'r',
                       success: 'r'}
 };
 
 function IccInfo() {}
 IccInfo.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozIccInfo]),
-  classID: ICCINFO_CID,
-  classInfo: XPCOMUtils.generateCI({
-    classID:          ICCINFO_CID,
-    classDescription: "MozIccInfo",
-    flags:            Ci.nsIClassInfo.DOM_OBJECT,
-    interfaces:       [Ci.nsIDOMMozIccInfo]
-  }),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIIccInfo]),
 
-  // nsIDOMMozIccInfo
+  // nsIIccInfo
 
   iccType: null,
   iccid: null,
   mcc: null,
   mnc: null,
   spn: null,
   isDisplayNetworkNameRequired: null,
   isDisplaySpnRequired: null
 };
 
 function GsmIccInfo() {}
 GsmIccInfo.prototype = {
-  __proto__: IccInfo.prototype,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozGsmIccInfo]),
-  classID: GSMICCINFO_CID,
-  classInfo: XPCOMUtils.generateCI({
-    classID:          GSMICCINFO_CID,
-    classDescription: "MozGsmIccInfo",
-    flags:            Ci.nsIClassInfo.DOM_OBJECT,
-    interfaces:       [Ci.nsIDOMMozGsmIccInfo]
-  }),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIGsmIccInfo,
+                                         Ci.nsIIccInfo]),
 
-  // nsIDOMMozGsmIccInfo
+  // nsIGsmIccInfo
 
   msisdn: null
 };
 
 function CdmaIccInfo() {}
 CdmaIccInfo.prototype = {
-  __proto__: IccInfo.prototype,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozCdmaIccInfo]),
-  classID: CDMAICCINFO_CID,
-  classInfo: XPCOMUtils.generateCI({
-    classID:          CDMAICCINFO_CID,
-    classDescription: "MozCdmaIccInfo",
-    flags:            Ci.nsIClassInfo.DOM_OBJECT,
-    interfaces:       [Ci.nsIDOMMozCdmaIccInfo]
-  }),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsICdmaIccInfo,
+                                         Ci.nsIIccInfo]),
 
-  // nsIDOMMozCdmaIccInfo
+  // nsICdmaIccInfo
 
   mdn: null,
   prlVersion: 0
 };
 
 function IccCardLockError() {
 }
 IccCardLockError.prototype = {
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -48,22 +48,16 @@ let RILQUIRKS_HAVE_IPV6 =
   libcutils.property_get("ro.moz.ril.ipv6", "false") == "true";
 
 const RADIOINTERFACELAYER_CID =
   Components.ID("{2d831c8d-6017-435b-a80c-e5d422810cea}");
 const RADIOINTERFACE_CID =
   Components.ID("{6a7c91f0-a2b3-4193-8562-8969296c0b54}");
 const RILNETWORKINTERFACE_CID =
   Components.ID("{3bdd52a9-3965-4130-b569-0ac5afed045e}");
-const ICCINFO_CID =
-  Components.ID("{52eec7f0-26a4-11e4-8c21-0800200c9a66}");
-const GSMICCINFO_CID =
-  Components.ID("{d90c4261-a99d-47bc-8b05-b057bb7e8f8a}");
-const CDMAICCINFO_CID =
-  Components.ID("{39ba3c08-aacc-46d0-8c04-9b619c387061}");
 const NEIGHBORINGCELLINFO_CID =
   Components.ID("{f9dfe26a-851e-4a8b-a769-cbb1baae7ded}");
 const GSMCELLINFO_CID =
   Components.ID("{41f6201e-7263-42e3-b31f-38a9dc8a280a}");
 const WCDMACELLINFO_CID =
   Components.ID("{eeaaf307-df6e-4c98-b121-e3302b1fc468}");
 const CDMACELLINFO_CID =
   Components.ID("{b497d6e4-4cb8-4d6e-b673-840c7d5ddf25}");
@@ -936,66 +930,47 @@ try {
     } catch (e) {}
 
     return 1;
   })());
 } catch (e) {}
 
 function IccInfo() {}
 IccInfo.prototype = {
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozIccInfo]),
-  classID: ICCINFO_CID,
-  classInfo: XPCOMUtils.generateCI({
-    classID:          ICCINFO_CID,
-    classDescription: "MozIccInfo",
-    flags:            Ci.nsIClassInfo.DOM_OBJECT,
-    interfaces:       [Ci.nsIDOMMozIccInfo]
-  }),
-
-  // nsIDOMMozIccInfo
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIIccInfo]),
+
+  // nsIIccInfo
 
   iccType: null,
   iccid: null,
   mcc: null,
   mnc: null,
   spn: null,
   isDisplayNetworkNameRequired: null,
   isDisplaySpnRequired: null
 };
 
 function GsmIccInfo() {}
 GsmIccInfo.prototype = {
   __proto__: IccInfo.prototype,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozGsmIccInfo]),
-  classID: GSMICCINFO_CID,
-  classInfo: XPCOMUtils.generateCI({
-    classID:          GSMICCINFO_CID,
-    classDescription: "MozGsmIccInfo",
-    flags:            Ci.nsIClassInfo.DOM_OBJECT,
-    interfaces:       [Ci.nsIDOMMozGsmIccInfo]
-  }),
-
-  // nsIDOMMozGsmIccInfo
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIGsmIccInfo,
+                                         Ci.nsIIccInfo]),
+
+  // nsIGsmIccInfo
 
   msisdn: null
 };
 
 function CdmaIccInfo() {}
 CdmaIccInfo.prototype = {
   __proto__: IccInfo.prototype,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMMozCdmaIccInfo]),
-  classID: CDMAICCINFO_CID,
-  classInfo: XPCOMUtils.generateCI({
-    classID:          CDMAICCINFO_CID,
-    classDescription: "MozCdmaIccInfo",
-    flags:            Ci.nsIClassInfo.DOM_OBJECT,
-    interfaces:       [Ci.nsIDOMMozCdmaIccInfo]
-  }),
-
-  // nsIDOMMozCdmaIccInfo
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsICdmaIccInfo,
+                                         Ci.nsIIccInfo]),
+
+  // nsICdmaIccInfo
 
   mdn: null,
   prlVersion: 0
 };
 
 function NeighboringCellInfo() {}
 NeighboringCellInfo.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsINeighboringCellInfo]),
@@ -2136,30 +2111,30 @@ RadioInterface.prototype = {
                         message.rilMessageType);
     }
   },
 
   /**
    * Get phone number from iccInfo.
    *
    * If the icc card is gsm card, the phone number is in msisdn.
-   * @see nsIDOMMozGsmIccInfo
+   * @see nsIGsmIccInfo
    *
    * Otherwise, the phone number is in mdn.
-   * @see nsIDOMMozCdmaIccInfo
+   * @see nsICdmaIccInfo
    */
   getPhoneNumber: function() {
     let iccInfo = this.rilContext.iccInfo;
 
     if (!iccInfo) {
       return null;
     }
 
     // After moving SMS code out of RadioInterfaceLayer, we could use
-    // |iccInfo instanceof Ci.nsIDOMMozGsmIccInfo| here.
+    // |iccInfo instanceof Ci.nsIGsmIccInfo| here.
     // TODO: Bug 873351 - B2G SMS: move SMS code out of RadioInterfaceLayer to
     //                    SmsService
     let number = (iccInfo instanceof GsmIccInfo) ? iccInfo.msisdn : iccInfo.mdn;
 
     // Workaround an xpconnect issue with undefined string objects.
     // See bug 808220
     if (number === undefined || number === "undefined") {
       return null;
--- a/dom/system/gonk/nsIRadioInterfaceLayer.idl
+++ b/dom/system/gonk/nsIRadioInterfaceLayer.idl
@@ -1,41 +1,41 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 #include "nsINetworkManager.idl"
 
-interface nsIDOMMozIccInfo;
+interface nsICellInfoListCallback;
+interface nsIIccInfo;
 interface nsIMobileConnectionInfo;
 interface nsIMobileMessageCallback;
 interface nsINeighboringCellIdsCallback;
-interface nsICellInfoListCallback;
 
 [scriptable, uuid(6e0f45b8-410e-11e3-8c8e-b715b2cd0128)]
 interface nsIRilNetworkInterface : nsINetworkInterface
 {
   readonly attribute unsigned long serviceId;
   readonly attribute DOMString iccId;
 
   /* The following attributes are for MMS proxy settings. */
   readonly attribute DOMString mmsc;     // Empty string if not set.
   readonly attribute DOMString mmsProxy; // Empty string if not set.
   readonly attribute long      mmsPort;  // -1 if not set.
 };
 
-[scriptable, uuid(0226a2c1-a3b9-416a-92cb-c89e4dad4be0)]
+[scriptable, uuid(08a69c70-34b1-11e4-8c21-0800200c9a66)]
 interface nsIRilContext : nsISupports
 {
   readonly attribute DOMString cardState;
 
   readonly attribute DOMString imsi;
 
-  readonly attribute nsIDOMMozIccInfo iccInfo;
+  readonly attribute nsIIccInfo iccInfo;
 };
 
 [scriptable, function, uuid(3bc96351-53b0-47a1-a888-c74c64b60f25)]
 interface nsIRilSendWorkerMessageCallback : nsISupports
 {
   boolean handleResponse(in jsval response);
 };
 
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -2720,16 +2720,23 @@ RilObject.prototype = {
    * @param program
    *        One of CALL_BARRING_PROGRAM_* constants.
    * @param serviceClass
    *        One of ICC_SERVICE_CLASS_* constants.
    */
   queryCallBarringStatus: function(options) {
     options.facility = CALL_BARRING_PROGRAM_TO_FACILITY[options.program];
     options.password = ""; // For query no need to provide it.
+
+    // For some operators, querying specific serviceClass doesn't work. We use
+    // serviceClass 0 instead, and then process the response to extract the
+    // answer for queryServiceClass.
+    options.queryServiceClass = options.serviceClass;
+    options.serviceClass = 0;
+
     this.queryICCFacilityLock(options);
   },
 
   /**
    * Configures call barring rule.
    *
    * @param program
    *        One of CALL_BARRING_PROGRAM_* constants.
@@ -5925,30 +5932,33 @@ RilObject.prototype[REQUEST_DEACTIVATE_D
 RilObject.prototype[REQUEST_QUERY_FACILITY_LOCK] = function REQUEST_QUERY_FACILITY_LOCK(length, options) {
   options.success = (options.rilRequestError === 0);
   if (!options.success) {
     options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
     this.sendChromeMessage(options);
     return;
   }
 
-  let services;
-  if (length) {
-    // Buf.readInt32List()[0] for Call Barring is a bit vector of services.
-    services = this.context.Buf.readInt32List()[0];
-  } else {
+  if (!length) {
     options.success = false;
     options.errorMsg = GECKO_ERROR_GENERIC_FAILURE;
     this.sendChromeMessage(options);
     return;
   }
 
-  options.enabled = services === 0 ? false : true;
-
-  if (options.success && (options.rilMessageType === "sendMMI")) {
+  // Buf.readInt32List()[0] for Call Barring is a bit vector of services.
+  let services = this.context.Buf.readInt32List()[0];
+
+  if (options.queryServiceClass) {
+    options.enabled = (services & options.queryServiceClass) ? true : false;
+  } else {
+    options.enabled = services ? true : false;
+  }
+
+  if (options.rilMessageType === "sendMMI") {
     if (!options.enabled) {
       options.statusMessage = MMI_SM_KS_SERVICE_DISABLED;
     } else {
       options.statusMessage = MMI_SM_KS_SERVICE_ENABLED_FOR;
       let serviceClass = [];
       for (let serviceClassMask = 1;
            serviceClassMask <= ICC_SERVICE_CLASS_MAX;
            serviceClassMask <<= 1) {
--- a/dom/tests/mochitest/general/mochitest.ini
+++ b/dom/tests/mochitest/general/mochitest.ini
@@ -1,10 +1,9 @@
 [DEFAULT]
-skip-if = e10s
 support-files =
   497633.html
   file_MozEnteredDomFullscreen.html
   file_bug628069.html
   file_clonewrapper.html
   file_domWindowUtils_scrollbarSize.html
   file_frameElementWrapping.html
   file_interfaces.xml
@@ -30,62 +29,62 @@ support-files =
   res6.resource^headers^
   res7.resource
   res7.resource^headers^
   res8.resource
   res8.resource^headers^
   resource_timing.js
 
 [test_497898.html]
-skip-if = (buildapp == 'b2g' && toolkit != 'gonk') || toolkit == 'android' #Bug 931116, b2g desktop specific, initial triage
+skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') || toolkit == 'android' #Bug 931116, b2g desktop specific, initial triage
 [test_bug504220.html]
 [test_bug628069_1.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug628069_2.html]
 [test_bug631440.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug653364.html]
 [test_bug861217.html]
 [test_clientRects.html]
 [test_clipboard_events.html]
-skip-if = buildapp == 'b2g' # b2g(clipboard undefined) b2g-debug(clipboard undefined) b2g-desktop(clipboard undefined)
+skip-if = e10s || buildapp == 'b2g' # b2g(clipboard undefined) b2g-debug(clipboard undefined) b2g-desktop(clipboard undefined)
 [test_consoleAPI.html]
 [test_DOMMatrix.html]
 [test_domWindowUtils.html]
 [test_domWindowUtils_scrollXY.html]
 [test_domWindowUtils_scrollbarSize.html]
 [test_donottrack.html]
 skip-if = buildapp == 'mulet'
 [test_focus_legend_noparent.html]
 [test_focusrings.xul]
-skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
+skip-if = e10s || buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
 [test_for_of.html]
 [test_frameElementWrapping.html]
 [test_framedhistoryframes.html]
 [test_idleapi_permissions.html]
-skip-if = buildapp == 'b2g' || buildapp == 'mulet'
+skip-if = e10s || buildapp == 'b2g' || buildapp == 'mulet'
 [test_interfaces.html]
 skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 # [test_network_events.html]
 # Disable this test until bug 795711 is fixed.
 [test_offsets.html]
 [test_offsets.js]
 [test_outerHTML.html]
 [test_outerHTML.xhtml]
 skip-if = buildapp == 'mulet'
 [test_paste_selection.html]
 skip-if = buildapp == 'mulet'
 [test_picture_pref.html]
 [test_resource_timing.html]
-skip-if = buildapp == 'b2g' || buildapp == 'mulet' # b2g(No clipboard) b2g-debug(No clipboard) b2g-desktop(No clipboard)
+skip-if = buildapp == 'b2g' || buildapp == 'mulet'
 [test_resource_timing_cross_origin.html]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet'
 [test_performance_now.html]
 [test_srcset_pref.html]
 [test_showModalDialog.html]
-skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #Don't run modal tests on Android # b2g(showmodaldialog) b2g-debug(showmodaldialog) b2g-desktop(showmodaldialog)
+skip-if = e10s || buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #Don't run modal tests on Android # b2g(showmodaldialog) b2g-debug(showmodaldialog) b2g-desktop(showmodaldialog)
 [test_stylesheetPI.html]
 [test_vibrator.html]
 skip-if = buildapp == 'mulet' || toolkit == 'android' #CRASH_SUTAGENT
 [test_windowProperties.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_windowedhistoryframes.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -696,26 +696,32 @@ var interfaceNamesInGlobalScope =
     "mozContact",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MozContactChangeEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MozCSSKeyframeRule",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MozCSSKeyframesRule",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "MozCdmaIccInfo", b2g: true, pref: "dom.icc.enabled"},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozEmergencyCbModeEvent", b2g: true, pref: "dom.mobileconnection.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozIcc", b2g: true, pref: "dom.icc.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "MozIccInfo", b2g: true, pref: "dom.icc.enabled"},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozIccManager", b2g: true, pref: "dom.icc.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozInputContext", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozInputMethodManager", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "MozGsmIccInfo", b2g: true, pref: "dom.icc.enabled"},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozMessageDeletedEvent", b2g: true, pref: "dom.sms.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MozMmsEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MozMmsMessage",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MozMobileCellInfo", b2g: true, pref: "dom.mobileconnection.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/tests/mochitest/general/test_resource_timing.html
+++ b/dom/tests/mochitest/general/test_resource_timing.html
@@ -15,21 +15,24 @@ https://bugzilla.mozilla.org/show_bug.cg
 <body>
 
 <pre id="test">
 <script type="application/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
 // Resource timing is prefed off by default, so we had to use this workaround
-SpecialPowers.setBoolPref("dom.enable_resource_timing", true);
-var subwindow = window.open("resource_timing_main_test.html");
+SpecialPowers.pushPrefEnv({"set": [["dom.enable_resource_timing", true]]}, start);
+var subwindow = null;
+
+function start() {
+  subwindow = window.open("resource_timing_main_test.html");
+}
 
 function finishTests() {
   subwindow.close();
-  SpecialPowers.setBoolPref("dom.enable_resource_timing", false);
   SimpleTest.finish();
 }
 </script>
 </pre>
 
 </body>
 </html>
--- a/dom/webidl/MozIcc.webidl
+++ b/dom/webidl/MozIcc.webidl
@@ -1,27 +1,25 @@
 /* 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/. */
 
-interface MozIccInfo;
-
 [Pref="dom.icc.enabled"]
 interface MozIcc : EventTarget
 {
   // Integrated Circuit Card Information.
 
   /**
    * Information stored in the device's ICC.
    *
    * Once the ICC becomes undetectable, iccinfochange event will be notified.
    * Also, the attribute is set to null and this MozIcc object becomes invalid.
    * Calling asynchronous functions raises exception then.
    */
-  readonly attribute MozIccInfo? iccInfo;
+  readonly attribute (MozIccInfo or MozGsmIccInfo or MozCdmaIccInfo)? iccInfo;
 
   /**
    * The 'iccinfochange' event is notified whenever the icc info object
    * changes.
    */
   attribute EventHandler oniccinfochange;
 
   // Integrated Circuit Card State.
new file mode 100644
--- /dev/null
+++ b/dom/webidl/MozIccInfo.webidl
@@ -0,0 +1,65 @@
+/* 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/. */
+
+enum IccType {"sim", "usim", "csim", "ruim"};
+
+[Pref="dom.icc.enabled"]
+interface MozIccInfo {
+  /**
+   * Integrated Circuit Card Type.
+   */
+  readonly attribute IccType? iccType;
+
+  /**
+   * Integrated Circuit Card Identifier.
+   */
+  readonly attribute DOMString? iccid;
+
+  /**
+   * Mobile Country Code (MCC) of the subscriber's home network.
+   */
+  readonly attribute DOMString? mcc;
+
+  /**
+   * Mobile Network Code (MNC) of the subscriber's home network.
+   */
+  readonly attribute DOMString? mnc;
+
+  /**
+   * Service Provider Name (SPN) of the subscriber's home network.
+   */
+  readonly attribute DOMString? spn;
+
+  /**
+   * Network name must be a part of displayed carrier name.
+   */
+  readonly attribute boolean isDisplayNetworkNameRequired;
+
+  /**
+   * Service provider name must be a part of displayed carrier name.
+   */
+  readonly attribute boolean isDisplaySpnRequired;
+};
+
+[Pref="dom.icc.enabled"]
+interface MozGsmIccInfo : MozIccInfo {
+  /**
+   * Mobile Station ISDN Number (MSISDN) of the subscriber, aka
+   * his phone number.
+   */
+  readonly attribute DOMString? msisdn;
+};
+
+[Pref="dom.icc.enabled"]
+interface MozCdmaIccInfo : MozIccInfo {
+  /**
+   * Mobile Directory Number (MDN) of the subscriber, aka his phone number.
+   */
+  readonly attribute DOMString? mdn;
+
+  /**
+   * Preferred Roaming List (PRL) version of the subscriber.
+   */
+  readonly attribute long prlVersion;
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -611,16 +611,17 @@ if CONFIG['MOZ_B2G_BT']:
             'BluetoothDevice.webidl',
             'BluetoothManager.webidl',
         ]
 
 if CONFIG['MOZ_B2G_RIL']:
     WEBIDL_FILES += [
         'IccCardLockError.webidl',
         'MozIcc.webidl',
+        'MozIccInfo.webidl',
         'MozIccManager.webidl',
     ]
 
 if CONFIG['MOZ_NFC']:
     WEBIDL_FILES += [
          'MozNDEFRecord.webidl',
          'MozNFC.webidl',
          'MozNFCPeer.webidl',
--- a/gfx/2d/Helpers.h
+++ b/gfx/2d/Helpers.h
@@ -9,16 +9,20 @@
 #include "2D.h"
 
 namespace mozilla {
 namespace gfx {
 
 class AutoRestoreTransform
 {
  public:
+  AutoRestoreTransform()
+  {
+  }
+
   explicit AutoRestoreTransform(DrawTarget *aTarget)
    : mDrawTarget(aTarget),
      mOldTransform(aTarget->GetTransform())
   {
   }
 
   void Init(DrawTarget *aTarget)
   {
--- a/gfx/2d/Logging.h
+++ b/gfx/2d/Logging.h
@@ -5,16 +5,20 @@
 
 #ifndef MOZILLA_GFX_LOGGING_H_
 #define MOZILLA_GFX_LOGGING_H_
 
 #include <string>
 #include <sstream>
 #include <stdio.h>
 
+#ifdef MOZ_LOGGING
+#include <prlog.h>
+#endif
+
 #if defined(MOZ_WIDGET_GONK) || defined(MOZ_WIDGET_ANDROID)
 #include "nsDebug.h"
 #endif
 #include "Point.h"
 #include "BaseRect.h"
 #include "Matrix.h"
 #include "mozilla/TypedEnum.h"
 
@@ -24,30 +28,28 @@
 // thing we need from windows.h, we just declare it here directly.
 // Note: the function's documented signature is
 //  WINBASEAPI void WINAPI OutputDebugStringA(LPCSTR lpOutputString)
 // but if we don't include windows.h, the macros WINBASEAPI, WINAPI, and 
 // LPCSTR are not defined, so we need to replace them with their expansions.
 extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char* lpOutputString);
 #endif
 
-#if defined(DEBUG) || defined(PR_LOGGING)
-#include <prlog.h>
-
+#if defined(PR_LOGGING)
 extern GFX2D_API PRLogModuleInfo *GetGFX2DLog();
 #endif
 
 namespace mozilla {
 namespace gfx {
 
 const int LOG_DEBUG = 1;
 const int LOG_WARNING = 2;
 const int LOG_CRITICAL = 3;
 
-#if defined(DEBUG) || defined(PR_LOGGING)
+#if defined(PR_LOGGING)
 
 inline PRLogModuleLevel PRLogLevelForLevel(int aLevel) {
   switch (aLevel) {
   case LOG_DEBUG:
     return PR_LOG_DEBUG;
   case LOG_WARNING:
     return PR_LOG_WARNING;
   }
--- a/gfx/angle/src/common/debug.h
+++ b/gfx/angle/src/common/debug.h
@@ -119,17 +119,17 @@ namespace gl
 // A macro that determines whether an object has a given runtime type.
 #if !defined(NDEBUG) && (!defined(_MSC_VER) || defined(_CPPRTTI)) && (!defined(__GNUC__) || __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3) || defined(__GXX_RTTI))
 #define HAS_DYNAMIC_TYPE(type, obj) (dynamic_cast<type >(obj) != NULL)
 #else
 #define HAS_DYNAMIC_TYPE(type, obj) true
 #endif
 
 // A macro functioning as a compile-time assert to validate constant conditions
-#if defined(_MSC_VER) && _MSC_VER >= 1600
+#if (defined(_MSC_VER) && _MSC_VER >= 1600) || (defined(__GNUC__) && (__GNUC__ > 4 || __GNUC_MINOR__ >= 3))
 #define META_ASSERT_MSG(condition, msg) static_assert(condition, msg)
 #else
 #define META_ASSERT_CONCAT(a, b) a ## b
 #define META_ASSERT_CONCAT2(a, b) META_ASSERT_CONCAT(a, b)
 #define META_ASSERT_MSG(condition, msg) typedef int META_ASSERT_CONCAT2(COMPILE_TIME_ASSERT_, __LINE__)[static_cast<bool>(condition)?1:-1]
 #endif
 #define META_ASSERT(condition) META_ASSERT_MSG(condition, "compile time assertion failed.")
 
--- a/gfx/angle/src/libEGL/Display.cpp
+++ b/gfx/angle/src/libEGL/Display.cpp
@@ -9,16 +9,17 @@
 // [EGL 1.4] section 2.1.2 page 3.
 
 #include "libEGL/Display.h"
 
 #include <algorithm>
 #include <map>
 #include <vector>
 #include <sstream>
+#include <iterator>
 
 #include "common/debug.h"
 #include "common/mathutil.h"
 #include "libGLESv2/main.h"
 #include "libGLESv2/Context.h"
 #include "libGLESv2/renderer/SwapChain.h"
 
 #include "libEGL/main.h"
--- a/gfx/angle/src/libEGL/moz.build
+++ b/gfx/angle/src/libEGL/moz.build
@@ -49,14 +49,13 @@ DEFINES['GL_GLEXT_PROTOTYPES'] = ""
 DEFINES['EGLAPI'] = ""
 
 # ANGLE uses the STL, so we can't use our derpy STL wrappers.
 DISABLE_STL_WRAPPING = True
 
 
 LOCAL_INCLUDES += [ '../../include', '../../src' ]
 USE_LIBS += [ 'libGLESv2' ]
-EXTRA_DSO_LDOPTS += [ '../libGLESv2/libGLESv2.lib' ]
 
 SharedLibrary('libEGL')
 
 RCFILE = SRCDIR + '/libEGL.rc'
 DEFFILE = SRCDIR + '/libEGL.def'
--- a/gfx/angle/src/libGLESv2.gypi
+++ b/gfx/angle/src/libGLESv2.gypi
@@ -41,16 +41,17 @@
             'common/utilities.cpp',
             'common/utilities.h',
             'common/version.h',
             'libGLESv2/BinaryStream.h',
             'libGLESv2/Buffer.cpp',
             'libGLESv2/Buffer.h',
             'libGLESv2/Caps.cpp',
             'libGLESv2/Caps.h',
+            'libGLESv2/Constants.h',
             'libGLESv2/Context.cpp',
             'libGLESv2/Context.h',
             'libGLESv2/Error.cpp',
             'libGLESv2/Error.h',
             'libGLESv2/Fence.cpp',
             'libGLESv2/Fence.h',
             'libGLESv2/Float16ToFloat32.cpp',
             'libGLESv2/Framebuffer.cpp',
@@ -82,17 +83,16 @@
             'libGLESv2/Uniform.cpp',
             'libGLESv2/Uniform.h',
             'libGLESv2/VertexArray.cpp',
             'libGLESv2/VertexArray.h',
             'libGLESv2/VertexAttribute.cpp',
             'libGLESv2/VertexAttribute.h',
             'libGLESv2/angletypes.cpp',
             'libGLESv2/angletypes.h',
-            'libGLESv2/constants.h',
             'libGLESv2/formatutils.cpp',
             'libGLESv2/formatutils.h',
             'libGLESv2/libGLESv2.cpp',
             'libGLESv2/libGLESv2.def',
             'libGLESv2/libGLESv2.rc',
             'libGLESv2/main.cpp',
             'libGLESv2/main.h',
             'libGLESv2/queryconversions.cpp',
rename from gfx/angle/src/libGLESv2/constants.h
rename to gfx/angle/src/libGLESv2/Constants.h
--- a/gfx/angle/src/libGLESv2/Context.cpp
+++ b/gfx/angle/src/libGLESv2/Context.cpp
@@ -28,16 +28,17 @@
 #include "libGLESv2/VertexArray.h"
 #include "libGLESv2/Sampler.h"
 #include "libGLESv2/validationES.h"
 #include "libGLESv2/TransformFeedback.h"
 
 #include "libEGL/Surface.h"
 
 #include <sstream>
+#include <iterator>
 
 namespace gl
 {
 
 Context::Context(int clientVersion, const gl::Context *shareContext, rx::Renderer *renderer, bool notifyResets, bool robustAccess)
     : mRenderer(renderer)
 {
     ASSERT(robustAccess == false);   // Unimplemented
@@ -336,17 +337,17 @@ void Context::deleteRenderbuffer(GLuint 
 }
 
 void Context::deleteFenceSync(GLsync fenceSync)
 {
     // The spec specifies the underlying Fence object is not deleted until all current
     // wait commands finish. However, since the name becomes invalid, we cannot query the fence,
     // and since our API is currently designed for being called from a single thread, we can delete
     // the fence immediately.
-    mResourceManager->deleteFenceSync(reinterpret_cast<GLuint>(fenceSync));
+    mResourceManager->deleteFenceSync(reinterpret_cast<uintptr_t>(fenceSync));
 }
 
 void Context::deleteVertexArray(GLuint vertexArray)
 {
     auto vertexArrayObject = mVertexArrayMap.find(vertexArray);
 
     if (vertexArrayObject != mVertexArrayMap.end())
     {
@@ -442,17 +443,17 @@ Texture *Context::getTexture(GLuint hand
 
 Renderbuffer *Context::getRenderbuffer(GLuint handle)
 {
     return mResourceManager->getRenderbuffer(handle);
 }
 
 FenceSync *Context::getFenceSync(GLsync handle) const
 {
-    return mResourceManager->getFenceSync(reinterpret_cast<GLuint>(handle));
+    return mResourceManager->getFenceSync(reinterpret_cast<uintptr_t>(handle));
 }
 
 VertexArray *Context::getVertexArray(GLuint handle) const
 {
     auto vertexArray = mVertexArrayMap.find(handle);
 
     if (vertexArray == mVertexArrayMap.end())
     {
--- a/gfx/angle/src/libGLESv2/Framebuffer.h
+++ b/gfx/angle/src/libGLESv2/Framebuffer.h
@@ -7,17 +7,17 @@
 // Framebuffer.h: Defines the gl::Framebuffer class. Implements GL framebuffer
 // objects and related functionality. [OpenGL ES 2.0.24] section 4.4 page 105.
 
 #ifndef LIBGLESV2_FRAMEBUFFER_H_
 #define LIBGLESV2_FRAMEBUFFER_H_
 
 #include "common/angleutils.h"
 #include "common/RefCountObject.h"
-#include "constants.h"
+#include "Constants.h"
 
 namespace rx
 {
 class Renderer;
 }
 
 namespace gl
 {
--- a/gfx/angle/src/libGLESv2/Texture.h
+++ b/gfx/angle/src/libGLESv2/Texture.h
@@ -9,17 +9,17 @@
 // related functionality. [OpenGL ES 2.0.24] section 3.7 page 63.
 
 #ifndef LIBGLESV2_TEXTURE_H_
 #define LIBGLESV2_TEXTURE_H_
 
 #include "common/debug.h"
 #include "common/RefCountObject.h"
 #include "libGLESv2/angletypes.h"
-#include "libGLESv2/constants.h"
+#include "libGLESv2/Constants.h"
 #include "libGLESv2/renderer/TextureImpl.h"
 #include "libGLESv2/Caps.h"
 
 #include "angle_gl.h"
 
 #include <vector>
 
 namespace egl
--- a/gfx/angle/src/libGLESv2/VertexArray.h
+++ b/gfx/angle/src/libGLESv2/VertexArray.h
@@ -9,17 +9,17 @@
 //   together to form a vertex array object. All state related to the definition of data used
 //   by the vertex processor is encapsulated in a vertex array object.
 //
 
 #ifndef LIBGLESV2_VERTEXARRAY_H_
 #define LIBGLESV2_VERTEXARRAY_H_
 
 #include "common/RefCountObject.h"
-#include "libGLESv2/constants.h"
+#include "libGLESv2/Constants.h"
 #include "libGLESv2/VertexAttribute.h"
 
 #include <vector>
 
 namespace rx
 {
 class Renderer;
 class VertexArrayImpl;
--- a/gfx/angle/src/libGLESv2/angletypes.h
+++ b/gfx/angle/src/libGLESv2/angletypes.h
@@ -4,17 +4,17 @@
 // found in the LICENSE file.
 //
 
 // angletypes.h : Defines a variety of structures and enum types that are used throughout libGLESv2
 
 #ifndef LIBGLESV2_ANGLETYPES_H_
 #define LIBGLESV2_ANGLETYPES_H_
 
-#include "libGLESv2/constants.h"
+#include "libGLESv2/Constants.h"
 #include "common/RefCountObject.h"
 
 namespace gl
 {
 class Buffer;
 class ProgramBinary;
 struct VertexAttribute;
 struct VertexAttribCurrentValueData;
--- a/gfx/angle/src/libGLESv2/moz.build
+++ b/gfx/angle/src/libGLESv2/moz.build
@@ -112,17 +112,16 @@ UNIFIED_SOURCES += [
     'renderer/d3d/d3d9/renderer9_utils.cpp',
     'renderer/d3d/d3d9/RenderTarget9.cpp',
     'renderer/d3d/d3d9/ShaderExecutable9.cpp',
     'renderer/d3d/d3d9/SwapChain9.cpp',
     'renderer/d3d/d3d9/TextureStorage9.cpp',
     'renderer/d3d/d3d9/VertexBuffer9.cpp',
     'renderer/d3d/d3d9/VertexDeclarationCache.cpp',
     'renderer/d3d/DynamicHLSL.cpp',
-    'renderer/d3d/HLSLCompiler.cpp',
     'renderer/d3d/ImageD3D.cpp',
     'renderer/d3d/IndexBuffer.cpp',
     'renderer/d3d/IndexDataManager.cpp',
     'renderer/d3d/MemoryBuffer.cpp',
     'renderer/d3d/ShaderD3D.cpp',
     'renderer/d3d/TextureD3D.cpp',
     'renderer/d3d/TextureStorage.cpp',
     'renderer/d3d/TransformFeedbackD3D.cpp',
@@ -143,16 +142,17 @@ UNIFIED_SOURCES += [
     'validationES2.cpp',
     'validationES3.cpp',
     'VertexArray.cpp',
     'VertexAttribute.cpp',
 ]
 SOURCES += [
     '../compiler/translator/glslang_lex.cpp',
     '../compiler/translator/glslang_tab.cpp',
+    'renderer/d3d/HLSLCompiler.cpp',
     'renderer/loadimageSSE2.cpp',
 ]
 SOURCES['renderer/loadimageSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
 if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
     UNIFIED_SOURCES += [
         'renderer/d3d/d3d11/Blit11.cpp',
         'renderer/d3d/d3d11/Buffer11.cpp',
         'renderer/d3d/d3d11/Clear11.cpp',
@@ -208,22 +208,22 @@ DEFINES['EGLAPI'] = ""
 # ANGLE uses the STL, so we can't use our derpy STL wrappers.
 DISABLE_STL_WRAPPING = True
 
 
 LOCAL_INCLUDES += [ '../../include', '../../src' ]
 
 
 if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
-  EXTRA_DSO_LDOPTS += [ 'd3d9.lib', 'dxguid.lib' ]
+  OS_LIBS += [ 'd3d9', 'dxguid' ]
 else:
   EXTRA_DSO_LDOPTS += [
     '\'%s/lib/%s/d3d9.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
     '\'%s/lib/%s/dxguid.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
   ]
 
 SharedLibrary('libGLESv2')
 
 RCFILE = SRCDIR + '/libGLESv2.rc'
 DEFFILE = SRCDIR + '/libGLESv2.def'
 
-DEFINES['ANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES'] = '{ TEXT("d3dcompiler_47.dll"), TEXT("d3dcompiler_46.dll"), TEXT("d3dcompiler_43.dll") }'
+SOURCES['renderer/d3d/HLSLCompiler.cpp'].flags += ['-DANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES=\'{ TEXT("d3dcompiler_47.dll"), TEXT("d3dcompiler_46.dll"), TEXT("d3dcompiler_43.dll") }\'']
 
--- a/gfx/angle/src/libGLESv2/renderer/copyimage.cpp
+++ b/gfx/angle/src/libGLESv2/renderer/copyimage.cpp
@@ -1,17 +1,17 @@
 //
 // Copyright (c) 2013 The ANGLE Project Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
 
 // copyimage.cpp: Defines image copying functions
 
-#include "libGLESv2/renderer/copyImage.h"
+#include "libGLESv2/renderer/copyimage.h"
 
 namespace rx
 {
 
 void CopyBGRA8ToRGBA8(const uint8_t *source, uint8_t *dest)
 {
     uint32_t argb = *reinterpret_cast<const uint32_t*>(source);
     *reinterpret_cast<uint32_t*>(dest) = (argb & 0xFF00FF00) |       // Keep alpha and green
--- a/gfx/angle/src/libGLESv2/renderer/copyimage.inl
+++ b/gfx/angle/src/libGLESv2/renderer/copyimage.inl
@@ -19,14 +19,14 @@ template <typename destType, typename co
 inline void WriteColor(const uint8_t *source, uint8_t *dest)
 {
     destType::writeColor(reinterpret_cast<destType*>(dest), reinterpret_cast<const gl::Color<colorDataType>*>(source));
 }
 
 template <typename sourceType, typename destType, typename colorDataType>
 inline void CopyPixel(const uint8_t *source, uint8_t *dest)
 {
-    colorType temp;
+    colorDataType temp;
     ReadColor<sourceType, colorDataType>(source, &temp);
     WriteColor<destType, colorDataType>(&temp, dest);
 }
 
 }
--- a/gfx/angle/src/libGLESv2/renderer/d3d/DynamicHLSL.h
+++ b/gfx/angle/src/libGLESv2/renderer/d3d/DynamicHLSL.h
@@ -5,17 +5,17 @@
 //
 // DynamicHLSL.h: Interface for link and run-time HLSL generation
 //
 
 #ifndef LIBGLESV2_RENDERER_DYNAMIC_HLSL_H_
 #define LIBGLESV2_RENDERER_DYNAMIC_HLSL_H_
 
 #include "common/angleutils.h"
-#include "libGLESv2/constants.h"
+#include "libGLESv2/Constants.h"
 
 #include "angle_gl.h"
 
 #include <vector>
 #include <map>
 
 namespace rx
 {
--- a/gfx/angle/src/libGLESv2/renderer/d3d/TextureD3D.cpp
+++ b/gfx/angle/src/libGLESv2/renderer/d3d/TextureD3D.cpp
@@ -124,17 +124,17 @@ bool TextureD3D::subImage(GLint xoffset,
                        GLenum format, GLenum type, const gl::PixelUnpackState &unpack, const void *pixels, Image *image)
 {
     const void *pixelData = pixels;
 
     // CPU readback & copy where direct GPU copy is not supported
     if (unpack.pixelBuffer.id() != 0)
     {
         gl::Buffer *pixelBuffer = unpack.pixelBuffer.get();
-        unsigned int offset = reinterpret_cast<unsigned int>(pixels);
+        uintptr_t offset = reinterpret_cast<uintptr_t>(pixels);
         // TODO: setImage/subImage is the only place outside of renderer that asks for a buffers raw data.
         // This functionality should be moved into renderer and the getData method of BufferImpl removed.
         const void *bufferData = pixelBuffer->getImplementation()->getData();
         pixelData = static_cast<const unsigned char *>(bufferData) + offset;
     }
 
     if (pixelData != NULL)
     {
@@ -178,17 +178,17 @@ bool TextureD3D::fastUnpackPixels(const 
     {
         return true;
     }
 
     // In order to perform the fast copy through the shader, we must have the right format, and be able
     // to create a render target.
     ASSERT(mRenderer->supportsFastCopyBufferToTexture(sizedInternalFormat));
 
-    unsigned int offset = reinterpret_cast<unsigned int>(pixels);
+    uintptr_t offset = reinterpret_cast<uintptr_t>(pixels);
 
     return mRenderer->fastCopyBufferToTexture(unpack, offset, destRenderTarget, sizedInternalFormat, type, destArea);
 }
 
 GLint TextureD3D::creationLevels(GLsizei width, GLsizei height, GLsizei depth) const
 {
     if ((gl::isPow2(width) && gl::isPow2(height) && gl::isPow2(depth)) || mRenderer->getRendererExtensions().textureNPOT)
     {
--- a/gfx/angle/src/libGLESv2/renderer/d3d/TextureD3D.h
+++ b/gfx/angle/src/libGLESv2/renderer/d3d/TextureD3D.h
@@ -6,17 +6,17 @@
 
 // TextureD3D.h: Implementations of the Texture interfaces shared betweeen the D3D backends.
 
 #ifndef LIBGLESV2_RENDERER_TEXTURED3D_H_
 #define LIBGLESV2_RENDERER_TEXTURED3D_H_
 
 #include "libGLESv2/renderer/TextureImpl.h"
 #include "libGLESv2/angletypes.h"
-#include "libGLESv2/constants.h"
+#include "libGLESv2/Constants.h"
 
 namespace gl
 {
 class Framebuffer;
 }
 
 namespace rx
 {
--- a/gfx/angle/src/libGLESv2/validationES.cpp
+++ b/gfx/angle/src/libGLESv2/validationES.cpp
@@ -1641,17 +1641,17 @@ bool ValidateDrawElements(Context *conte
         return false;
     }
 
     // Use max index to validate if our vertex buffers are large enough for the pull.
     // TODO: offer fast path, with disabled index validation.
     // TODO: also disable index checking on back-ends that are robust to out-of-range accesses.
     if (elementArrayBuffer)
     {
-        unsigned int offset = reinterpret_cast<unsigned int>(indices);
+        uintptr_t offset = reinterpret_cast<uintptr_t>(indices);
         if (!elementArrayBuffer->getIndexRangeCache()->findRange(type, offset, count, indexRangeOut, NULL))
         {
             const void *dataPointer = elementArrayBuffer->getImplementation()->getData();
             const uint8_t *offsetPointer = static_cast<const uint8_t *>(dataPointer) + offset;
             *indexRangeOut = rx::IndexRangeCache::ComputeRange(type, offsetPointer, count);
         }
     }
     else
--- a/gfx/angle/src/third_party/trace_event/trace_event.h
+++ b/gfx/angle/src/third_party/trace_event/trace_event.h
@@ -582,17 +582,17 @@ const int zeroNumArgs = 0;
 const unsigned long long noEventId = 0;
 
 // TraceID encapsulates an ID that can either be an integer or pointer. Pointers
 // are mangled with the Process ID so that they are unlikely to collide when the
 // same pointer is used on different processes.
 class TraceID {
 public:
     explicit TraceID(const void* id, unsigned char* flags) :
-        m_data(static_cast<unsigned long long>(reinterpret_cast<unsigned long>(id)))
+        m_data(static_cast<unsigned long long>(reinterpret_cast<uintptr_t>(id)))
     {
         *flags |= TRACE_EVENT_FLAG_MANGLE_ID;
     }
     explicit TraceID(unsigned long long id, unsigned char* flags) : m_data(id) { (void)flags; }
     explicit TraceID(unsigned long id, unsigned char* flags) : m_data(id) { (void)flags; }
     explicit TraceID(unsigned int id, unsigned char* flags) : m_data(id) { (void)flags; }
     explicit TraceID(unsigned short id, unsigned char* flags) : m_data(id) { (void)flags; }
     explicit TraceID(unsigned char id, unsigned char* flags) : m_data(id) { (void)flags; }
@@ -783,44 +783,13 @@ private:
     struct Data {
         const unsigned char* categoryEnabled;
         const char* name;
     };
     Data* m_pdata;
     Data m_data;
 };
 
-// TraceEventSamplingStateScope records the current sampling state
-// and sets a new sampling state. When the scope exists, it restores
-// the sampling state having recorded.
-template<size_t BucketNumber>
-class SamplingStateScope {
-public:
-    SamplingStateScope(const char* categoryAndName)
-    {
-        m_previousState = SamplingStateScope<BucketNumber>::current();
-        SamplingStateScope<BucketNumber>::set(categoryAndName);
-    }
-
-    ~SamplingStateScope()
-    {
-        SamplingStateScope<BucketNumber>::set(m_previousState);
-    }
-
-    // FIXME: Make load/store to traceSamplingState[] thread-safe and atomic.
-    static inline const char* current()
-    {
-        return reinterpret_cast<const char*>(*gl::traceSamplingState[BucketNumber]);
-    }
-    static inline void set(const char* categoryAndName)
-    {
-        *gl::traceSamplingState[BucketNumber] = reinterpret_cast<long>(const_cast<char*>(categoryAndName));
-    }
-
-private:
-    const char* m_previousState;
-};
-
 } // namespace TraceEvent
 
 } // namespace gl
 
 #endif
--- a/gfx/layers/RotatedBuffer.cpp
+++ b/gfx/layers/RotatedBuffer.cpp
@@ -723,16 +723,22 @@ RotatedContentBuffer::BorrowDrawTargetFo
   }
   if (result->GetBackendType() == BackendType::DIRECT2D ||
       result->GetBackendType() == BackendType::DIRECT2D1_1) {
     drawPtr->SimplifyOutwardByArea(100 * 100);
   }
 
   if (aPaintState.mMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
     MOZ_ASSERT(mDTBuffer && mDTBufferOnWhite);
+    if (!mDTBuffer || !mDTBufferOnWhite) {
+      // This can happen in release builds if allocating one of the two buffers
+      // failed. This is pretty bad and the reason for the failure is already
+      // reported through gfxCriticalError.
+      return nullptr;
+    }
     nsIntRegionRectIterator iter(*drawPtr);
     const nsIntRect *iterRect;
     while ((iterRect = iter.Next())) {
       mDTBuffer->FillRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height),
                           ColorPattern(Color(0.0, 0.0, 0.0, 1.0)));
       mDTBufferOnWhite->FillRect(Rect(iterRect->x, iterRect->y, iterRect->width, iterRect->height),
                                  ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
     }
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -620,17 +620,17 @@ nsEventStatus
 APZCTreeManager::ProcessTouchInput(MultiTouchInput& aInput,
                                    ScrollableLayerGuid* aOutTargetGuid)
 {
   if (aInput.mType == MultiTouchInput::MULTITOUCH_START) {
     // If we are in an overscrolled state and a second finger goes down,
     // ignore that second touch point completely. The touch-start for it is
     // dropped completely; subsequent touch events until the touch-end for it
     // will have this touch point filtered out.
-    if (mApzcForInputBlock && mApzcForInputBlock->IsOverscrolled()) {
+    if (mApzcForInputBlock && BuildOverscrollHandoffChain(mApzcForInputBlock)->HasOverscrolledApzc()) {
       if (mRetainedTouchIdentifier == -1) {
         mRetainedTouchIdentifier = mApzcForInputBlock->GetLastTouchIdentifier();
       }
       return nsEventStatus_eConsumeNoDefault;
     }
 
     // NS_TOUCH_START event contains all active touches of the current
     // session thus resetting mTouchCount.
--- a/gfx/layers/apz/src/OverscrollHandoffState.cpp
+++ b/gfx/layers/apz/src/OverscrollHandoffState.cpp
@@ -64,16 +64,28 @@ void
 OverscrollHandoffChain::ForEachApzc(APZCMethod aMethod) const
 {
   MOZ_ASSERT(Length() > 0);
   for (uint32_t i = 0; i < Length(); ++i) {
     (mChain[i]->*aMethod)();
   }
 }
 
+bool
+OverscrollHandoffChain::AnyApzc(APZCPredicate aPredicate) const
+{
+  MOZ_ASSERT(Length() > 0);
+  for (uint32_t i = 0; i < Length(); ++i) {
+    if ((mChain[i]->*aPredicate)()) {
+      return true;
+    }
+  }
+  return false;
+}
+
 void
 OverscrollHandoffChain::FlushRepaints() const
 {
   ForEachApzc(&AsyncPanZoomController::FlushRepaintForOverscrollHandoff);
 }
 
 void
 OverscrollHandoffChain::CancelAnimations() const
@@ -124,10 +136,17 @@ OverscrollHandoffChain::CanBePanned(cons
     if (mChain[j]->IsPannable()) {
       return true;
     }
   }
 
   return false;
 }
 
+bool
+OverscrollHandoffChain::HasOverscrolledApzc() const
+{
+  return AnyApzc(&AsyncPanZoomController::IsOverscrolled);
+}
+
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/apz/src/OverscrollHandoffState.h
+++ b/gfx/layers/apz/src/OverscrollHandoffState.h
@@ -100,21 +100,26 @@ public:
 
   // Snap back the APZC that is overscrolled on the subset of the chain from
   // |aStart| onwards, if any.
   void SnapBackOverscrolledApzc(const AsyncPanZoomController* aStart) const;
 
   // Determine whether the given APZC, or any APZC further in the chain,
   // has room to be panned.
   bool CanBePanned(const AsyncPanZoomController* aApzc) const;
+
+  // Determine whether any APZC along this handoff chain is overscrolled.
+  bool HasOverscrolledApzc() const;
 private:
   std::vector<nsRefPtr<AsyncPanZoomController>> mChain;
 
   typedef void (AsyncPanZoomController::*APZCMethod)();
+  typedef bool (AsyncPanZoomController::*APZCPredicate)() const;
   void ForEachApzc(APZCMethod aMethod) const;
+  bool AnyApzc(APZCPredicate aPredicate) const;
 };
 
 /**
  * This class groups the state maintained during overscroll handoff.
  */
 struct OverscrollHandoffState {
   OverscrollHandoffState(const OverscrollHandoffChain& aChain,
                          const ScreenPoint& aPanDistance)
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -548,17 +548,20 @@ CompositorD3D11::ClearRect(const gfx::Re
   mContext->VSSetShader(mAttachments->mVSQuadShader[MaskType::MaskNone], nullptr, 0);
 
   mContext->PSSetShader(mAttachments->mSolidColorShader[MaskType::MaskNone], nullptr, 0);
   mPSConstants.layerColor[0] = 0;
   mPSConstants.layerColor[1] = 0;
   mPSConstants.layerColor[2] = 0;
   mPSConstants.layerColor[3] = 0;
 
-  UpdateConstantBuffers();
+  if (!UpdateConstantBuffers()) {
+    NS_WARNING("Failed to update shader constant buffers");
+    return;
+  }
 
   mContext->Draw(4, 0);
 
   mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor, 0xFFFFFFFF);
 }
 
 void
 CompositorD3D11::DrawQuad(const gfx::Rect& aRect,
@@ -734,17 +737,20 @@ CompositorD3D11::DrawQuad(const gfx::Rec
       mContext->OMSetBlendState(mAttachments->mComponentBlendState, sBlendFactor, 0xFFFFFFFF);
       restoreBlendMode = true;
     }
     break;
   default:
     NS_WARNING("Unknown shader type");
     return;
   }
-  UpdateConstantBuffers();
+  if (!UpdateConstantBuffers()) {
+    NS_WARNING("Failed to update shader constant buffers");
+    return;
+  }
 
   mContext->Draw(4, 0);
   if (restoreBlendMode) {
     mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor, 0xFFFFFFFF);
   }
 }
 
 void
@@ -961,33 +967,72 @@ CompositorD3D11::CreateShaders()
                                   byRef(mAttachments->mRGBAShader[MaskType::Mask3d]));
   if (FAILED(hr)) {
     return false;
   }
 
   return true;
 }
 
-void
+static
+bool ShouldRecoverFromMapFailure(HRESULT hr, ID3D11Device* device)
+{
+  // XXX - it would be nice to use gfxCriticalError, but it needs to
+  // be made to work off the main thread first.
+  if (SUCCEEDED(hr)) {
+    return true;
+  }
+  if (hr == DXGI_ERROR_DEVICE_REMOVED) {
+    switch (device->GetDeviceRemovedReason()) {
+      case DXGI_ERROR_DEVICE_HUNG:
+      case DXGI_ERROR_DEVICE_REMOVED:
+      case DXGI_ERROR_DEVICE_RESET:
+      case DXGI_ERROR_DRIVER_INTERNAL_ERROR:
+        return true;
+      case DXGI_ERROR_INVALID_CALL:
+      default:
+        return false;
+    }
+  }
+  return false;
+}
+
+bool
 CompositorD3D11::UpdateConstantBuffers()
 {
+  HRESULT hr;
   D3D11_MAPPED_SUBRESOURCE resource;
-  mContext->Map(mAttachments->mVSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
+
+  hr = mContext->Map(mAttachments->mVSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
+  if (FAILED(hr)) {
+    if (ShouldRecoverFromMapFailure(hr, GetDevice())) {
+      return false;
+    }
+    MOZ_CRASH();
+  }
   *(VertexShaderConstants*)resource.pData = mVSConstants;
   mContext->Unmap(mAttachments->mVSConstantBuffer, 0);
-  mContext->Map(mAttachments->mPSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
+
+  hr = mContext->Map(mAttachments->mPSConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource);
+  if (FAILED(hr)) {
+    if (ShouldRecoverFromMapFailure(hr, GetDevice())) {
+      return false;
+    }
+    MOZ_CRASH();
+  }
   *(PixelShaderConstants*)resource.pData = mPSConstants;
   mContext->Unmap(mAttachments->mPSConstantBuffer, 0);
 
   ID3D11Buffer *buffer = mAttachments->mVSConstantBuffer;
 
   mContext->VSSetConstantBuffers(0, 1, &buffer);
 
   buffer = mAttachments->mPSConstantBuffer;
   mContext->PSSetConstantBuffers(0, 1, &buffer);
+  return true;
 }
 
 void
 CompositorD3D11::SetSamplerForFilter(Filter aFilter)
 {
   ID3D11SamplerState *sampler;
   switch (aFilter) {
   default:
--- a/gfx/layers/d3d11/CompositorD3D11.h
+++ b/gfx/layers/d3d11/CompositorD3D11.h
@@ -143,17 +143,17 @@ public:
   ID3D11DeviceContext* GetDC() { return mContext; }
 
 private:
   // ensure mSize is up to date with respect to mWidget
   void EnsureSize();
   void VerifyBufferSize();
   void UpdateRenderTarget();
   bool CreateShaders();
-  void UpdateConstantBuffers();
+  bool UpdateConstantBuffers();
   void SetSamplerForFilter(gfx::Filter aFilter);
   void SetPSForEffect(Effect *aEffect, MaskType aMaskType, gfx::SurfaceFormat aFormat);
   void PaintToTarget();
 
   virtual gfx::IntSize GetWidgetSize() const MOZ_OVERRIDE { return gfx::ToIntSize(mSize); }
 
   RefPtr<ID3D11DeviceContext> mContext;
   RefPtr<ID3D11Device> mDevice;
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -1607,16 +1607,65 @@ protected:
     SetScrollableFrameMetrics(layers[0], FrameMetrics::START_SCROLL_ID);
     SetScrollableFrameMetrics(layers[2], FrameMetrics::START_SCROLL_ID + 1);
     SetScrollableFrameMetrics(layers[5], FrameMetrics::START_SCROLL_ID + 1);
     SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID + 2);
     SetScrollableFrameMetrics(layers[6], FrameMetrics::START_SCROLL_ID + 3);
   }
 };
 
+// A version of ApzcPan() that routes the pan through the tree manager,
+// so that the tree manager has the appropriate state for testing.
+static void
+ApzctmPan(APZCTreeManager* aTreeManager,
+          int& aTime,
+          int aTouchStartY,
+          int aTouchEndY,
+          bool aKeepFingerDown = false)
+{
+  // TODO: Reuse some code between this and ApzcPan().
+
+  // Reduce the touch start tolerance to a tiny value.
+  // We can't do what ApzcPan() does to overcome the tolerance (send the
+  // touch-start at (aTouchStartY + some_large_value)) because the tree manager
+  // does hit testing based on the touch-start coordinates, and a different
+  // APZC than the one we intend might be hit.
+  SCOPED_GFX_PREF(APZTouchStartTolerance, float, 1.0f / 1000.0f);
+  const int OVERCOME_TOUCH_TOLERANCE = 1;
+
+  const int TIME_BETWEEN_TOUCH_EVENT = 100;
+
+  // Make sure the move is large enough to not be handled as a tap
+  MultiTouchInput mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_START, aTime, TimeStamp(), 0);
+  mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchStartY), ScreenSize(0, 0), 0, 0));
+  aTreeManager->ReceiveInputEvent(mti, nullptr);
+
+  aTime += TIME_BETWEEN_TOUCH_EVENT;
+
+  mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime, TimeStamp(), 0);
+  mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchStartY + OVERCOME_TOUCH_TOLERANCE), ScreenSize(0, 0), 0, 0));
+  aTreeManager->ReceiveInputEvent(mti, nullptr);
+
+  aTime += TIME_BETWEEN_TOUCH_EVENT;
+
+  mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_MOVE, aTime, TimeStamp(), 0);
+  mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchEndY), ScreenSize(0, 0), 0, 0));
+  aTreeManager->ReceiveInputEvent(mti, nullptr);
+
+  aTime += TIME_BETWEEN_TOUCH_EVENT;
+
+  if (!aKeepFingerDown) {
+    mti = MultiTouchInput(MultiTouchInput::MULTITOUCH_END, aTime, TimeStamp(), 0);
+    mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchEndY), ScreenSize(0, 0), 0, 0));
+    aTreeManager->ReceiveInputEvent(mti, nullptr);
+  }
+
+  aTime += TIME_BETWEEN_TOUCH_EVENT;
+}
+
 class APZHitTestingTester : public APZCTreeManagerTester {
 protected:
   Matrix4x4 transformToApzc;
   Matrix4x4 transformToGecko;
 
   already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScreenPoint& aPoint) {
     nsRefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(aPoint, nullptr);
     if (hit) {
@@ -2184,16 +2233,53 @@ TEST_F(APZOverscrollHandoffTester, Layer
 
   // Make sure things have scrolled according to the handoff chain in
   // place at the time the touch-start of the second pan was queued.
   EXPECT_EQ(0, childApzc->GetFrameMetrics().GetScrollOffset().y);
   EXPECT_EQ(10, rootApzc->GetFrameMetrics().GetScrollOffset().y);
   EXPECT_EQ(-10, middleApzc->GetFrameMetrics().GetScrollOffset().y);
 }
 
+// Test that putting a second finger down on an APZC while a down-chain APZC
+// is overscrolled doesn't result in being stuck in overscroll.
+TEST_F(APZOverscrollHandoffTester, StuckInOverscroll_Bug1073250) {
+  // Enable overscrolling.
+  SCOPED_GFX_PREF(APZOverscrollEnabled, bool, true);
+
+  CreateOverscrollHandoffLayerTree1();
+
+  TestAsyncPanZoomController* child = ApzcOf(layers[1]);
+
+  // Pan, causing the parent APZC to overscroll.
+  int time = 0;
+  ApzctmPan(manager, time, 10, 40, true /* keep finger down */);
+  EXPECT_FALSE(child->IsOverscrolled());
+  EXPECT_TRUE(rootApzc->IsOverscrolled());
+
+  // Put a second finger down.
+  MultiTouchInput secondFingerDown(MultiTouchInput::MULTITOUCH_START, 0, TimeStamp(), 0);
+  // Use the same touch identifier for the first touch (0) as ApzctmPan(). (A bit hacky.)
+  secondFingerDown.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, 40), ScreenSize(0, 0), 0, 0));
+  secondFingerDown.mTouches.AppendElement(SingleTouchData(1, ScreenIntPoint(30, 20), ScreenSize(0, 0), 0, 0));
+  manager->ReceiveInputEvent(secondFingerDown, nullptr);
+
+  // Release the fingers.
+  MultiTouchInput fingersUp = secondFingerDown;
+  fingersUp.mType = MultiTouchInput::MULTITOUCH_END;
+  manager->ReceiveInputEvent(fingersUp, nullptr);
+
+  // Allow any animations to run their course.
+  child->AdvanceAnimationsUntilEnd(testStartTime);
+  rootApzc->AdvanceAnimationsUntilEnd(testStartTime);
+
+  // Make sure nothing is overscrolled.
+  EXPECT_FALSE(child->IsOverscrolled());
+  EXPECT_FALSE(rootApzc->IsOverscrolled());
+}
+
 // Here we test that if two flings are happening simultaneously, overscroll
 // is handed off correctly for each.
 TEST_F(APZOverscrollHandoffTester, SimultaneousFlings) {
   // Set up an initial APZC tree.
   CreateOverscrollHandoffLayerTree3();
 
   TestAsyncPanZoomController* parent1 = ApzcOf(layers[1]);
   TestAsyncPanZoomController* child1 = ApzcOf(layers[2]);
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -1,31 +1,26 @@
 /* -*- 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 "ImageLogging.h"
-#include "nsPNGDecoder.h"
-
+#include "ImageLogging.h" // Must appear first
+#include "gfxColor.h"
+#include "gfxPlatform.h"
+#include "nsColor.h"
+#include "nsIInputStream.h"
 #include "nsMemory.h"
+#include "nsPNGDecoder.h"
 #include "nsRect.h"
-
-#include "nsIInputStream.h"
-
+#include "nspr.h"
+#include "png.h"
 #include "RasterImage.h"
 
-#include "gfxColor.h"
-#include "nsColor.h"
-
-#include "nspr.h"
-#include "png.h"
-
-#include "gfxPlatform.h"
 #include <algorithm>
 
 namespace mozilla {
 namespace image {
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo *
 GetPNGLog()
@@ -41,17 +36,17 @@ GetPNGDecoderAccountingLog()
 {
   static PRLogModuleInfo *sPNGDecoderAccountingLog;
   if (!sPNGDecoderAccountingLog)
     sPNGDecoderAccountingLog = PR_NewLogModule("PNGDecoderAccounting");
   return sPNGDecoderAccountingLog;
 }
 #endif
 
-/* limit image dimensions (bug #251381, #591822, and #967656) */
+// Limit image dimensions (bug #251381, #591822, and #967656)
 #ifndef MOZ_PNG_MAX_DIMENSION
 #  define MOZ_PNG_MAX_DIMENSION 32767
 #endif
 
 // For size decodes
 #define WIDTH_OFFSET 16
 #define HEIGHT_OFFSET (WIDTH_OFFSET + 4)
 #define BYTES_NEEDED_FOR_DIMENSIONS (HEIGHT_OFFSET + 4)
@@ -64,33 +59,34 @@ nsPNGDecoder::AnimFrameInfo::AnimFrameIn
 
 #ifdef PNG_APNG_SUPPORTED
 nsPNGDecoder::AnimFrameInfo::AnimFrameInfo(png_structp aPNG, png_infop aInfo)
  : mDispose(FrameBlender::kDisposeKeep)
  , mBlend(FrameBlender::kBlendOver)
  , mTimeout(0)
 {
   png_uint_16 delay_num, delay_den;
-  /* delay, in seconds is delay_num/delay_den */
+  // delay, in seconds is delay_num/delay_den
   png_byte dispose_op;
   png_byte blend_op;
   delay_num = png_get_next_frame_delay_num(aPNG, aInfo);
   delay_den = png_get_next_frame_delay_den(aPNG, aInfo);
   dispose_op = png_get_next_frame_dispose_op(aPNG, aInfo);
   blend_op = png_get_next_frame_blend_op(aPNG, aInfo);
 
   if (delay_num == 0) {
     mTimeout = 0; // SetFrameTimeout() will set to a minimum
   } else {
     if (delay_den == 0)
       delay_den = 100; // so says the APNG spec
 
     // Need to cast delay_num to float to have a proper division and
     // the result to int to avoid compiler warning
-    mTimeout = static_cast<int32_t>(static_cast<double>(delay_num) * 1000 / delay_den);
+    mTimeout = static_cast<int32_t>(static_cast<double>(delay_num) *
+                                    1000 / delay_den);
   }
 
   if (dispose_op == PNG_DISPOSE_OP_PREVIOUS) {
     mDispose = FrameBlender::kDisposeRestorePrevious;
   } else if (dispose_op == PNG_DISPOSE_OP_BACKGROUND) {
     mDispose = FrameBlender::kDisposeClear;
   } else {
     mDispose = FrameBlender::kDisposeKeep;
@@ -126,17 +122,17 @@ nsPNGDecoder::~nsPNGDecoder()
     png_destroy_read_struct(&mPNG, mInfo ? &mInfo : nullptr, nullptr);
   if (mCMSLine)
     nsMemory::Free(mCMSLine);
   if (interlacebuf)
     nsMemory::Free(interlacebuf);
   if (mInProfile) {
     qcms_profile_release(mInProfile);
 
-    /* mTransform belongs to us only if mInProfile is non-null */
+    // mTransform belongs to us only if mInProfile is non-null
     if (mTransform)
       qcms_transform_release(mTransform);
   }
 }
 
 // CreateFrame() is used for both simple and animated images
 void nsPNGDecoder::CreateFrame(png_uint_32 x_offset, png_uint_32 y_offset,
                                int32_t width, int32_t height,
@@ -190,55 +186,57 @@ void nsPNGDecoder::EndImageFrame()
   uint32_t numFrames = GetFrameCount();
 
   // We can't use mPNG->num_frames_read as it may be one ahead.
   if (numFrames > 1) {
     PostInvalidation(mFrameRect);
   }
 #endif
 
-  PostFrameStop(alpha, mAnimInfo.mDispose, mAnimInfo.mTimeout, mAnimInfo.mBlend);
+  PostFrameStop(alpha, mAnimInfo.mDispose, mAnimInfo.mTimeout,
+                mAnimInfo.mBlend);
 }
 
 void
 nsPNGDecoder::InitInternal()
 {
   // For size decodes, we don't need to initialize the png decoder
   if (IsSizeDecode()) {
     return;
   }
 
   mCMSMode = gfxPlatform::GetCMSMode();
   if ((mDecodeFlags & DECODER_NO_COLORSPACE_CONVERSION) != 0)
     mCMSMode = eCMSMode_Off;
-  mDisablePremultipliedAlpha = (mDecodeFlags & DECODER_NO_PREMULTIPLY_ALPHA) != 0;
+  mDisablePremultipliedAlpha = (mDecodeFlags & DECODER_NO_PREMULTIPLY_ALPHA)
+                                != 0;
 
 #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
   static png_byte color_chunks[]=
-       { 99,  72,  82,  77, '\0',   /* cHRM */
-        105,  67,  67,  80, '\0'};  /* iCCP */
+       { 99,  72,  82,  77, '\0',   // cHRM
+        105,  67,  67,  80, '\0'};  // iCCP
   static png_byte unused_chunks[]=
-       { 98,  75,  71,  68, '\0',   /* bKGD */
-        104,  73,  83,  84, '\0',   /* hIST */
-        105,  84,  88, 116, '\0',   /* iTXt */
-        111,  70,  70, 115, '\0',   /* oFFs */
-        112,  67,  65,  76, '\0',   /* pCAL */
-        115,  67,  65,  76, '\0',   /* sCAL */
-        112,  72,  89, 115, '\0',   /* pHYs */
-        115,  66,  73,  84, '\0',   /* sBIT */
-        115,  80,  76,  84, '\0',   /* sPLT */
-        116,  69,  88, 116, '\0',   /* tEXt */
-        116,  73,  77,  69, '\0',   /* tIME */
-        122,  84,  88, 116, '\0'};  /* zTXt */
+       { 98,  75,  71,  68, '\0',   // bKGD
+        104,  73,  83,  84, '\0',   // hIST
+        105,  84,  88, 116, '\0',   // iTXt
+        111,  70,  70, 115, '\0',   // oFFs
+        112,  67,  65,  76, '\0',   // pCAL
+        115,  67,  65,  76, '\0',   // sCAL
+        112,  72,  89, 115, '\0',   // pHYs
+        115,  66,  73,  84, '\0',   // sBIT
+        115,  80,  76,  84, '\0',   // sPLT
+        116,  69,  88, 116, '\0',   // tEXt
+        116,  73,  77,  69, '\0',   // tIME
+        122,  84,  88, 116, '\0'};  // zTXt
 #endif
 
-  /* For full decodes, do png init stuff */
+  // For full decodes, do png init stuff
 
-  /* Initialize the container's source image header. */
-  /* Always decode to 24 bit pixdepth */
+  // Initialize the container's source image header
+  // Always decode to 24 bit pixdepth
 
   mPNG = png_create_read_struct(PNG_LIBPNG_VER_STRING,
                                 nullptr, nsPNGDecoder::error_callback,
                                 nsPNGDecoder::warning_callback);
   if (!mPNG) {
     PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
@@ -246,57 +244,57 @@ nsPNGDecoder::InitInternal()
   mInfo = png_create_info_struct(mPNG);
   if (!mInfo) {
     PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
     png_destroy_read_struct(&mPNG, nullptr, nullptr);
     return;
   }
 
 #ifdef PNG_HANDLE_AS_UNKNOWN_SUPPORTED
-  /* Ignore unused chunks */
+  // Ignore unused chunks
   if (mCMSMode == eCMSMode_Off)
     png_set_keep_unknown_chunks(mPNG, 1, color_chunks, 2);
 
   png_set_keep_unknown_chunks(mPNG, 1, unused_chunks,
                               (int)sizeof(unused_chunks)/5);
 #endif
 
 #ifdef PNG_SET_CHUNK_MALLOC_LIMIT_SUPPORTED
   if (mCMSMode != eCMSMode_Off)
     png_set_chunk_malloc_max(mPNG, 4000000L);
 #endif
 
 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
 #ifndef PR_LOGGING
-  /* Disallow palette-index checking, for speed; we would ignore the warning
-   * anyhow unless we have defined PR_LOGGING.  This feature was added at
-   * libpng version 1.5.10 and is disabled in the embedded libpng but enabled
-   * by default in the system libpng.  This call also disables it in the
-   * system libpng, for decoding speed.  Bug #745202.
-   */
+  // Disallow palette-index checking, for speed; we would ignore the warning
+  // anyhow unless we have defined PR_LOGGING.  This feature was added at
+  // libpng version 1.5.10 and is disabled in the embedded libpng but enabled
+  // by default in the system libpng.  This call also disables it in the
+  // system libpng, for decoding speed.  Bug #745202.
   png_set_check_for_invalid_index(mPNG, 0);
 #endif
 #endif
 
 #if defined(PNG_SET_OPTION_SUPPORTED) && defined(PNG_sRGB_PROFILE_CHECKS) && \
             PNG_sRGB_PROFILE_CHECKS >= 0
-  /* Skip checking of sRGB ICC profiles */
+  // Skip checking of sRGB ICC profiles
   png_set_option(mPNG, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
 #endif
 
-  /* use this as libpng "progressive pointer" (retrieve in callbacks) */
+  // use this as libpng "progressive pointer" (retrieve in callbacks)
   png_set_progressive_read_fn(mPNG, static_cast<png_voidp>(this),
                               nsPNGDecoder::info_callback,
                               nsPNGDecoder::row_callback,
                               nsPNGDecoder::end_callback);
 
 }
 
 void
-nsPNGDecoder::WriteInternal(const char *aBuffer, uint32_t aCount, DecodeStrategy)
+nsPNGDecoder::WriteInternal(const char *aBuffer, uint32_t aCount,
+                            DecodeStrategy)
 {
   NS_ABORT_IF_FALSE(!HasError(), "Shouldn't call WriteInternal after error!");
 
   // If we only want width/height, we don't need to go through libpng
   if (IsSizeDecode()) {
 
     // Are we done?
     if (mHeaderBytesRead == BYTES_NEEDED_FOR_DIMENSIONS)
@@ -388,34 +386,24 @@ PNGGetColorProfile(png_structp png_ptr, 
                    int color_type, qcms_data_type *inType, uint32_t *intent)
 {
   qcms_profile *profile = nullptr;
   *intent = QCMS_INTENT_PERCEPTUAL; // Our default
 
   // First try to see if iCCP chunk is present
   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP)) {
     png_uint_32 profileLen;
-#if (PNG_LIBPNG_VER < 10500)
-    char *profileData, *profileName;
-#else
     png_bytep profileData;
     png_charp profileName;
-#endif
     int compression;
 
     png_get_iCCP(png_ptr, info_ptr, &profileName, &compression,
                  &profileData, &profileLen);
 
-    profile = qcms_profile_from_memory(
-#if (PNG_LIBPNG_VER < 10500)
-                                       profileData,
-#else
-                                       (char *)profileData,
-#endif
-                                       profileLen);
+    profile = qcms_profile_from_memory((char *)profileData, profileLen);
     if (profile) {
       uint32_t profileSpace = qcms_profile_get_color_space(profile);
 
       bool mismatch = false;
       if (color_type & PNG_COLOR_MASK_COLOR) {
         if (profileSpace != icSigRgbData)
           mismatch = true;
       } else {
@@ -493,81 +481,81 @@ PNGGetColorProfile(png_structp png_ptr, 
   }
 
   return profile;
 }
 
 void
 nsPNGDecoder::info_callback(png_structp png_ptr, png_infop info_ptr)
 {
-/*  int number_passes;   NOT USED  */
+//  int number_passes;   NOT USED
   png_uint_32 width, height;
   int bit_depth, color_type, interlace_type, compression_type, filter_type;
   unsigned int channels;
 
   png_bytep trans = nullptr;
   int num_trans = 0;
 
   nsPNGDecoder *decoder =
                static_cast<nsPNGDecoder*>(png_get_progressive_ptr(png_ptr));
 
-  /* always decode to 24-bit RGB or 32-bit RGBA  */
+  // Always decode to 24-bit RGB or 32-bit RGBA
   png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
                &interlace_type, &compression_type, &filter_type);
 
-  /* Are we too big? */
+  // Are we too big?
   if (width > MOZ_PNG_MAX_DIMENSION || height > MOZ_PNG_MAX_DIMENSION)
-    longjmp(png_jmpbuf(decoder->mPNG), 1);
+    png_longjmp(decoder->mPNG, 1);
 
   // Post our size to the superclass
   decoder->PostSize(width, height);
   if (decoder->HasError()) {
     // Setting the size led to an error.
-    longjmp(png_jmpbuf(decoder->mPNG), 1);
+    png_longjmp(decoder->mPNG, 1);
   }
 
   if (color_type == PNG_COLOR_TYPE_PALETTE)
     png_set_expand(png_ptr);
 
   if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
     png_set_expand(png_ptr);
 
   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
     int sample_max = (1 << bit_depth);
     png_color_16p trans_values;
     png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values);
-    /* libpng doesn't reject a tRNS chunk with out-of-range samples
-       so we check it here to avoid setting up a useless opacity
-       channel or producing unexpected transparent pixels when using
-       libpng-1.2.19 through 1.2.26 (bug #428045) */
+    // libpng doesn't reject a tRNS chunk with out-of-range samples
+    // so we check it here to avoid setting up a useless opacity
+    // channel or producing unexpected transparent pixels when using
+    // libpng-1.2.19 through 1.2.26 (bug #428045)
     if ((color_type == PNG_COLOR_TYPE_GRAY &&
        (int)trans_values->gray > sample_max) ||
        (color_type == PNG_COLOR_TYPE_RGB &&
        ((int)trans_values->red > sample_max ||
        (int)trans_values->green > sample_max ||
        (int)trans_values->blue > sample_max)))
       {
-        /* clear the tRNS valid flag and release tRNS memory */
+        // clear the tRNS valid flag and release tRNS memory
         png_free_data(png_ptr, info_ptr, PNG_FREE_TRNS, 0);
       }
     else
       png_set_expand(png_ptr);
   }
 
   if (bit_depth == 16)
     png_set_scale_16(png_ptr);
 
   qcms_data_type inType = QCMS_DATA_RGBA_8;
   uint32_t intent = -1;
   uint32_t pIntent;
   if (decoder->mCMSMode != eCMSMode_Off) {
     intent = gfxPlatform::GetRenderingIntent();
     decoder->mInProfile = PNGGetColorProfile(png_ptr, info_ptr,
                                              color_type, &inType, &pIntent);
-    /* If we're not mandating an intent, use the one from the image. */
+    // If we're not mandating an intent, use the one from the image.
     if (intent == uint32_t(-1))
       intent = pIntent;
   }
   if (decoder->mInProfile && gfxPlatform::GetCMSOutputProfile()) {
     qcms_data_type outType;
 
     if (color_type & PNG_COLOR_MASK_ALPHA || num_trans)
       outType = QCMS_DATA_RGBA_8;
@@ -589,52 +577,30 @@ nsPNGDecoder::info_callback(png_structp 
     if (decoder->mCMSMode == eCMSMode_All) {
       if (color_type & PNG_COLOR_MASK_ALPHA || num_trans)
         decoder->mTransform = gfxPlatform::GetCMSRGBATransform();
       else
         decoder->mTransform = gfxPlatform::GetCMSRGBTransform();
     }
   }
 
-  /* let libpng expand interlaced images */
+  // let libpng expand interlaced images
   if (interlace_type == PNG_INTERLACE_ADAM7) {
-    /* number_passes = */
+    // number_passes =
     png_set_interlace_handling(png_ptr);
   }
 
-  /* now all of those things we set above are used to update various struct
-   * members and whatnot, after which we can get channels, rowbytes, etc. */
+  // now all of those things we set above are used to update various struct
+  // members and whatnot, after which we can get channels, rowbytes, etc.
   png_read_update_info(png_ptr, info_ptr);
   decoder->mChannels = channels = png_get_channels(png_ptr, info_ptr);
 
-  /*---------------------------------------------------------------*/
-  /* copy PNG info into imagelib structs (formerly png_set_dims()) */
-  /*---------------------------------------------------------------*/
-
-  // This code is currently unused, but it will be needed for bug 517713.
-#if 0
-  int32_t alpha_bits = 1;
-
-  if (channels == 2 || channels == 4) {
-    /* check if alpha is coming from a tRNS chunk and is binary */
-    if (num_trans) {
-      /* if it's not an indexed color image, tRNS means binary */
-      if (color_type == PNG_COLOR_TYPE_PALETTE) {
-        for (int i=0; i<num_trans; i++) {
-          if ((trans[i] != 0) && (trans[i] != 255)) {
-            alpha_bits = 8;
-            break;
-          }
-        }
-      }
-    } else {
-      alpha_bits = 8;
-    }
-  }
-#endif
+  //---------------------------------------------------------------//
+  // copy PNG info into imagelib structs (formerly png_set_dims()) //
+  //---------------------------------------------------------------//
 
   if (channels == 1 || channels == 3)
     decoder->format = gfx::SurfaceFormat::B8G8R8X8;
   else if (channels == 2 || channels == 4)
     decoder->format = gfx::SurfaceFormat::B8G8R8A8;
 
 #ifdef PNG_APNG_SUPPORTED
   if (png_get_valid(png_ptr, info_ptr, PNG_INFO_acTL))
@@ -651,32 +617,31 @@ nsPNGDecoder::info_callback(png_structp 
 #endif
 
   if (decoder->mTransform &&
       (channels <= 2 || interlace_type == PNG_INTERLACE_ADAM7)) {
     uint32_t bpp[] = { 0, 3, 4, 3, 4 };
     decoder->mCMSLine =
       (uint8_t *)moz_malloc(bpp[channels] * width);
     if (!decoder->mCMSLine) {
-      longjmp(png_jmpbuf(decoder->mPNG), 5); // NS_ERROR_OUT_OF_MEMORY
+      png_longjmp(decoder->mPNG, 5); // NS_ERROR_OUT_OF_MEMORY
     }
   }
 
   if (interlace_type == PNG_INTERLACE_ADAM7) {
     if (height < INT32_MAX / (width * channels))
       decoder->interlacebuf = (uint8_t *)moz_malloc(channels * width * height);
     if (!decoder->interlacebuf) {
-      longjmp(png_jmpbuf(decoder->mPNG), 5); // NS_ERROR_OUT_OF_MEMORY
+      png_longjmp(decoder->mPNG, 5); // NS_ERROR_OUT_OF_MEMORY
     }
   }
 
   if (decoder->NeedsNewFrame()) {
-    /* We know that we need a new frame, so pause input so the decoder
-     * infrastructure can give it to us.
-     */
+    // We know that we need a new frame, so pause input so the decoder
+    // infrastructure can give it to us.
     png_process_data_pause(png_ptr, /* save = */ 1);
   }
 }
 
 void
 nsPNGDecoder::row_callback(png_structp png_ptr, png_bytep new_row,
                            png_uint_32 row_num, int pass)
 {
@@ -730,17 +695,17 @@ nsPNGDecoder::row_callback(png_structp p
     uint32_t bpr = width * sizeof(uint32_t);
     uint32_t *cptr32 = (uint32_t*)(decoder->mImageData + (row_num*bpr));
     bool rowHasNoAlpha = true;
 
     if (decoder->mTransform) {
       if (decoder->mCMSLine) {
         qcms_transform_data(decoder->mTransform, line, decoder->mCMSLine,
                             iwidth);
-        /* copy alpha over */
+        // copy alpha over
         uint32_t channels = decoder->mChannels;
         if (channels == 2 || channels == 4) {
           for (uint32_t i = 0; i < iwidth; i++)
             decoder->mCMSLine[4 * i + 3] = line[channels * i + channels - 1];
         }
         line = decoder->mCMSLine;
       } else {
         qcms_transform_data(decoder->mTransform, line, line, iwidth);
@@ -781,26 +746,27 @@ nsPNGDecoder::row_callback(png_structp p
           for (uint32_t x=width; x>0; --x) {
             *cptr32++ = gfxPackedPixel(line[3], line[0], line[1], line[2]);
             if (line[3] != 0xff)
               rowHasNoAlpha = false;
             line += 4;
           }
         } else {
           for (uint32_t x=width; x>0; --x) {
-            *cptr32++ = gfxPackedPixelNoPreMultiply(line[3], line[0], line[1], line[2]);
+            *cptr32++ = gfxPackedPixelNoPreMultiply(line[3], line[0], line[1],
+                                                    line[2]);
             if (line[3] != 0xff)
               rowHasNoAlpha = false;
             line += 4;
           }
         }
       }
       break;
       default:
-        longjmp(png_jmpbuf(decoder->mPNG), 1);
+        png_longjmp(decoder->mPNG, 1);
     }
 
     if (!rowHasNoAlpha)
       decoder->mFrameHasNoAlpha = false;
 
     if (decoder->mNumFrames <= 1) {
       // Only do incremental image display for the first frame
       // XXXbholley - this check should be handled in the superclass
@@ -830,19 +796,18 @@ nsPNGDecoder::frame_info_callback(png_st
   x_offset = png_get_next_frame_x_offset(png_ptr, decoder->mInfo);
   y_offset = png_get_next_frame_y_offset(png_ptr, decoder->mInfo);
   width = png_get_next_frame_width(png_ptr, decoder->mInfo);
   height = png_get_next_frame_height(png_ptr, decoder->mInfo);
 
   decoder->CreateFrame(x_offset, y_offset, width, height, decoder->format);
 
   if (decoder->NeedsNewFrame()) {
-    /* We know that we need a new frame, so pause input so the decoder
-     * infrastructure can give it to us.
-     */
+    // We know that we need a new frame, so pause input so the decoder
+    // infrastructure can give it to us.
     png_process_data_pause(png_ptr, /* save = */ 1);
   }
 }
 #endif
 
 void
 nsPNGDecoder::end_callback(png_structp png_ptr, png_infop info_ptr)
 {
@@ -877,17 +842,17 @@ nsPNGDecoder::end_callback(png_structp p
   decoder->PostDecodeDone(loop_count);
 }
 
 
 void
 nsPNGDecoder::error_callback(png_structp png_ptr, png_const_charp error_msg)
 {
   PR_LOG(GetPNGLog(), PR_LOG_ERROR, ("libpng error: %s\n", error_msg));
-  longjmp(png_jmpbuf(png_ptr), 1);
+  png_longjmp(png_ptr, 1);
 }
 
 
 void
 nsPNGDecoder::warning_callback(png_structp png_ptr, png_const_charp warning_msg)
 {
   PR_LOG(GetPNGLog(), PR_LOG_WARNING, ("libpng warning: %s\n", warning_msg));
 }
--- a/image/decoders/nsPNGDecoder.h
+++ b/image/decoders/nsPNGDecoder.h
@@ -1,16 +1,16 @@
 /* -*- 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/. */
 
-#ifndef nsPNGDecoder_h__
-#define nsPNGDecoder_h__
+#ifndef nsPNGDecoder_h
+#define nsPNGDecoder_h
 
 #include "Decoder.h"
 
 #include "gfxTypes.h"
 
 #include "nsCOMPtr.h"
 
 #include "png.h"
@@ -128,9 +128,9 @@ public:
   // This is defined in the PNG spec as an invariant. We use it to
   // do manual validation without libpng.
   static const uint8_t pngSignatureBytes[];
 };
 
 } // namespace image
 } // namespace mozilla
 
-#endif // nsPNGDecoder_h__
+#endif // nsPNGDecoder_h
--- a/image/encoders/png/nsPNGEncoder.cpp
+++ b/image/encoders/png/nsPNGEncoder.cpp
@@ -1,31 +1,45 @@
 /* -*- 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 "ImageLogging.h"
 #include "nsCRT.h"
 #include "nsPNGEncoder.h"
-#include "prprf.h"
+#include "nsStreamUtils.h"
 #include "nsString.h"
-#include "nsStreamUtils.h"
+#include "prprf.h"
 
 using namespace mozilla;
 
-NS_IMPL_ISUPPORTS(nsPNGEncoder, imgIEncoder, nsIInputStream, nsIAsyncInputStream)
+#ifdef PR_LOGGING
+static PRLogModuleInfo *
+GetPNGEncoderLog()
+{
+  static PRLogModuleInfo *sPNGEncoderLog;
+  if (!sPNGEncoderLog)
+    sPNGEncoderLog = PR_NewLogModule("PNGEncoder");
+  return sPNGEncoderLog;
+}
+#endif
+
+NS_IMPL_ISUPPORTS(nsPNGEncoder, imgIEncoder, nsIInputStream,
+                  nsIAsyncInputStream)
 
 nsPNGEncoder::nsPNGEncoder() : mPNG(nullptr), mPNGinfo(nullptr),
                                mIsAnimation(false),
                                mFinished(false),
                                mImageBuffer(nullptr), mImageBufferSize(0),
                                mImageBufferUsed(0), mImageBufferReadPoint(0),
                                mCallback(nullptr),
                                mCallbackTarget(nullptr), mNotifyThreshold(0),
-                               mReentrantMonitor("nsPNGEncoder.mReentrantMonitor")
+                               mReentrantMonitor(
+                                              "nsPNGEncoder.mReentrantMonitor")
 {
 }
 
 nsPNGEncoder::~nsPNGEncoder()
 {
   if (mImageBuffer) {
     moz_free(mImageBuffer);
     mImageBuffer = nullptr;
@@ -67,17 +81,17 @@ NS_IMETHODIMP nsPNGEncoder::InitFromData
   rv = EndImageEncode();
 
   return rv;
 }
 
 
 // nsPNGEncoder::StartImageEncode
 //
-// 
+//
 // See ::InitFromData for other info.
 NS_IMETHODIMP nsPNGEncoder::StartImageEncode(uint32_t aWidth,
                                              uint32_t aHeight,
                                              uint32_t aInputFormat,
                                              const nsAString& aOutputOptions)
 {
   bool useTransparency = true, skipFirstFrame = false;
   uint32_t numFrames = 1;
@@ -236,17 +250,17 @@ NS_IMETHODIMP nsPNGEncoder::AddImageFram
   if (mIsAnimation) {
     // XXX the row pointers arg (#3) is unused, can it be removed?
     png_write_frame_head(mPNG, mPNGinfo, nullptr,
                          aWidth, aHeight, x_offset, y_offset,
                          delay_ms, 1000, dispose_op, blend_op);
   }
 #endif
 
-  // Stride is the padded width of each row, so it better be longer 
+  // Stride is the padded width of each row, so it better be longer
   // (I'm afraid people will not understand what stride means, so
   // check it well)
   if ((aInputFormat == INPUT_FORMAT_RGB &&
       aStride < aWidth * 3) ||
       ((aInputFormat == INPUT_FORMAT_RGBA ||
       aInputFormat == INPUT_FORMAT_HOSTARGB) &&
       aStride < aWidth * 4)) {
     NS_WARNING("Invalid stride for InitFromData/AddImageFrame");
@@ -562,17 +576,18 @@ NS_IMETHODIMP nsPNGEncoder::AsyncWait(ns
   // 0 means "any number of bytes except 0"
   mNotifyThreshold = aRequestedCount;
   if (!aRequestedCount)
     mNotifyThreshold = 1024; // We don't want to notify incessantly
 
   // We set the callback absolutely last, because NotifyListener uses it to
   // determine if someone needs to be notified.  If we don't set it last,
   // NotifyListener might try to fire off a notification to a null target
-  // which will generally cause non-threadsafe objects to be used off the main thread
+  // which will generally cause non-threadsafe objects to be used off the main
+  // thread
   mCallback = aCallback;
 
   // What we are being asked for may be present already
   NotifyListener();
   return NS_OK;
 }
 
 NS_IMETHODIMP nsPNGEncoder::CloseWithStatus(nsresult aStatus)
@@ -626,47 +641,35 @@ nsPNGEncoder::StripAlpha(const uint8_t* 
     pixelOut[1] = pixelIn[1];
     pixelOut[2] = pixelIn[2];
   }
 }
 
 
 // nsPNGEncoder::WarningCallback
 
-void // static
+void
 nsPNGEncoder::WarningCallback(png_structp png_ptr,
                             png_const_charp warning_msg)
 {
-#ifdef DEBUG
-	// XXX: these messages are probably useful callers...
-        // use nsIConsoleService?
-	PR_fprintf(PR_STDERR, "PNG Encoder: %s\n", warning_msg);;
-#endif
+  PR_LOG(GetPNGEncoderLog(), PR_LOG_WARNING,
+         ("libpng warning: %s\n", warning_msg));
 }
 
 
 // nsPNGEncoder::ErrorCallback
 
-void // static
+void
 nsPNGEncoder::ErrorCallback(png_structp png_ptr,
                             png_const_charp error_msg)
 {
-#ifdef DEBUG
-	// XXX: these messages are probably useful callers...
-        // use nsIConsoleService?
-	PR_fprintf(PR_STDERR, "PNG Encoder: %s\n", error_msg);;
-#endif
-#if PNG_LIBPNG_VER < 10500
-        longjmp(png_ptr->jmpbuf, 1);
-#else
-        png_longjmp(png_ptr, 1);
-#endif
+  PR_LOG(GetPNGEncoderLog(), PR_LOG_ERROR, ("libpng error: %s\n", error_msg));
+  png_longjmp(png_ptr, 1);
 }
 
-
 // nsPNGEncoder::WriteCallback
 
 void // static
 nsPNGEncoder::WriteCallback(png_structp png, png_bytep data,
                             png_size_t size)
 {
   nsPNGEncoder* that = static_cast<nsPNGEncoder*>(png_get_io_ptr(png));
   if (! that->mImageBuffer)
--- a/image/encoders/png/nsPNGEncoder.h
+++ b/image/encoders/png/nsPNGEncoder.h
@@ -1,21 +1,22 @@
 /* -*- 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 "mozilla/Attributes.h"
-#include "mozilla/ReentrantMonitor.h"
+#ifndef nsPNGEncoder_h
+
+#include <png.h>
 
 #include "imgIEncoder.h"
-
 #include "nsCOMPtr.h"
 
-#include <png.h>
+#include "mozilla/Attributes.h"
+#include "mozilla/ReentrantMonitor.h"
 
 #define NS_PNGENCODER_CID \
 { /* 38d1592e-b81e-432b-86f8-471878bbfe07 */         \
      0x38d1592e,                                     \
      0xb81e,                                         \
      0x432b,                                         \
     {0x86, 0xf8, 0x47, 0x18, 0x78, 0xbb, 0xfe, 0x07} \
 }
@@ -67,16 +68,16 @@ protected:
   uint32_t mImageBufferUsed;
 
   uint32_t mImageBufferReadPoint;
 
   nsCOMPtr<nsIInputStreamCallback> mCallback;
   nsCOMPtr<nsIEventTarget> mCallbackTarget;
   uint32_t mNotifyThreshold;
 
-  /*
-    nsPNGEncoder is designed to allow one thread to pump data into it while another
-    reads from it.  We lock to ensure that the buffer remains append-only while
-    we read from it (that it is not realloced) and to ensure that only one thread
-    dispatches a callback for each call to AsyncWait.
-   */
+  // nsPNGEncoder is designed to allow one thread to pump data into it while
+  // another reads from it.  We lock to ensure that the buffer remains
+  // append-only while we read from it (that it is not realloced) and to
+  // ensure that only one thread dispatches a callback for each call to
+  // AsyncWait.
   ReentrantMonitor mReentrantMonitor;
 };
+#endif // nsPNGEncoder_h
--- a/js/src/builtin/SymbolObject.cpp
+++ b/js/src/builtin/SymbolObject.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "builtin/SymbolObject.h"
 
 #include "vm/StringBuffer.h"
 
 #include "jsobjinlines.h"
 
+#include "vm/ObjectImpl-inl.h"
 #include "vm/Symbol-inl.h"
 
 using JS::Symbol;
 using namespace js;
 
 const Class SymbolObject::class_ = {
     "Symbol",
     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) | JSCLASS_HAS_CACHED_PROTO(JSProto_Symbol),
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -31,38 +31,33 @@ namespace gc {
 typedef Vector<JS::Zone *, 4, SystemAllocPolicy> ZoneVector;
 
 class MarkingValidator;
 struct AutoPrepareForTracing;
 class AutoTraceSession;
 
 class ChunkPool
 {
-    Chunk   *emptyChunkListHead;
-    size_t  emptyCount;
+    Chunk *head_;
+    size_t count_;
 
   public:
-    ChunkPool()
-      : emptyChunkListHead(nullptr),
-        emptyCount(0)
-    {}
+    ChunkPool() : head_(nullptr), count_(0) {}
 
-    size_t getEmptyCount() const {
-        return emptyCount;
-    }
+    size_t count() const { return count_; }
 
     /* Must be called with the GC lock taken. */
     inline Chunk *get(JSRuntime *rt);
 
     /* Must be called either during the GC or with the GC lock taken. */
     inline void put(Chunk *chunk);
 
     class Enum {
       public:
-        explicit Enum(ChunkPool &pool) : pool(pool), chunkp(&pool.emptyChunkListHead) {}
+        explicit Enum(ChunkPool &pool) : pool(pool), chunkp(&pool.head_) {}
         bool empty() { return !*chunkp; }
         Chunk *front();
         inline void popFront();
         inline void removeAndPopFront();
       private:
         ChunkPool &pool;
         Chunk **chunkp;
     };
@@ -590,17 +585,17 @@ class GCRuntime
      * Doubly-linked lists of chunks from user and system compartments. The GC
      * allocates its arenas from the corresponding list and when all arenas
      * in the list head are taken, then the chunk is removed from the list.
      * During the GC when all arenas in a chunk become free, that chunk is
      * removed from the list and scheduled for release.
      */
     js::gc::Chunk         *systemAvailableChunkListHead;
     js::gc::Chunk         *userAvailableChunkListHead;
-    js::gc::ChunkPool     chunkPool;
+    js::gc::ChunkPool     emptyChunks;
 
     js::RootedValueMap    rootsHash;
 
     size_t                maxMallocBytes;
 
     /*
      * Number of the committed arenas in all GC chunks including empty chunks.
      */
@@ -693,16 +688,22 @@ class GCRuntime
     JS::Zone              *zoneGroups;
     JS::Zone              *currentZoneGroup;
     int                   finalizePhase;
     JS::Zone              *sweepZone;
     int                   sweepKindIndex;
     bool                  abortSweepAfterCurrentGroup;
 
     /*
+     * Concurrent sweep infrastructure.
+     */
+    void startTask(GCParallelTask &task, gcstats::Phase phase);
+    void joinTask(GCParallelTask &task, gcstats::Phase phase);
+
+    /*
      * List head of arenas allocated during the sweep phase.
      */
     js::gc::ArenaHeader   *arenasAllocatedDuringSweep;
 
 #ifdef JS_GC_MARKING_VALIDATION
     js::gc::MarkingValidator *markingValidator;
 #endif
 
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -467,16 +467,25 @@ IsMarked(T **thingp)
 #endif
     return (*thingp)->asTenured().isMarked();
 }
 
 template <typename T>
 static bool
 IsAboutToBeFinalized(T **thingp)
 {
+    MOZ_ASSERT_IF(!ThingIsPermanentAtom(*thingp),
+                  CurrentThreadCanAccessRuntime((*thingp)->runtimeFromMainThread()));
+    return IsAboutToBeFinalizedFromAnyThread(thingp);
+}
+
+template <typename T>
+static bool
+IsAboutToBeFinalizedFromAnyThread(T **thingp)
+{
     MOZ_ASSERT(thingp);
     MOZ_ASSERT(*thingp);
 
     T *thing = *thingp;
     JSRuntime *rt = thing->runtimeFromAnyThread();
 
     /* Permanent atoms are never finalized by non-owning runtimes. */
     if (ThingIsPermanentAtom(thing) && !TlsPerThreadData.get()->associatedWith(rt))
@@ -498,17 +507,17 @@ IsAboutToBeFinalized(T **thingp)
         if (rt->isHeapMinorCollecting()) {
             if (IsInsideNursery(thing))
                 return !nursery.getForwardedPointer(thingp);
             return false;
         }
     }
 #endif  // JSGC_GENERATIONAL
 
-    Zone *zone = thing->asTenured().zone();
+    Zone *zone = thing->asTenured().zoneFromAnyThread();
     if (zone->isGCSweeping()) {
         /*
          * We should return false for things that have been allocated during
          * incremental sweeping, but this possibility doesn't occur at the moment
          * because this function is only called at the very start of the sweeping a
          * compartment group and during minor gc. Rather than do the extra check,
          * we just assert that it's not necessary.
          */
@@ -611,16 +620,22 @@ Is##base##Marked(BarrieredBase<type*> *t
                                                                                                   \
 bool                                                                                              \
 Is##base##AboutToBeFinalized(type **thingp)                                                       \
 {                                                                                                 \
     return IsAboutToBeFinalized<type>(thingp);                                                    \
 }                                                                                                 \
                                                                                                   \
 bool                                                                                              \
+Is##base##AboutToBeFinalizedFromAnyThread(type **thingp)                                          \
+{                                                                                                 \
+    return IsAboutToBeFinalizedFromAnyThread<type>(thingp);                                       \
+}                                                                                                 \
+                                                                                                  \
+bool                                                                                              \
 Is##base##AboutToBeFinalized(BarrieredBase<type*> *thingp)                                        \
 {                                                                                                 \
     return IsAboutToBeFinalized<type>(thingp->unsafeGet());                                       \
 }                                                                                                 \
                                                                                                   \
 type *                                                                                            \
 Update##base##IfRelocated(JSRuntime *rt, BarrieredBase<type*> *thingp)                            \
 {                                                                                                 \
@@ -905,16 +920,38 @@ gc::IsValueAboutToBeFinalized(Value *v)
         MOZ_ASSERT(v->isSymbol());
         JS::Symbol *sym = v->toSymbol();
         rv = IsAboutToBeFinalized<JS::Symbol>(&sym);
         v->setSymbol(sym);
     }
     return rv;
 }
 
+bool
+gc::IsValueAboutToBeFinalizedFromAnyThread(Value *v)
+{
+    MOZ_ASSERT(v->isMarkable());
+    bool rv;
+    if (v->isString()) {
+        JSString *str = (JSString *)v->toGCThing();
+        rv = IsAboutToBeFinalizedFromAnyThread<JSString>(&str);
+        v->setString(str);
+    } else if (v->isObject()) {
+        JSObject *obj = (JSObject *)v->toGCThing();
+        rv = IsAboutToBeFinalizedFromAnyThread<JSObject>(&obj);
+        v->setObject(*obj);
+    } else {
+        MOZ_ASSERT(v->isSymbol());
+        JS::Symbol *sym = v->toSymbol();
+        rv = IsAboutToBeFinalizedFromAnyThread<JS::Symbol>(&sym);
+        v->setSymbol(sym);
+    }
+    return rv;
+}
+
 /*** Slot Marking ***/
 
 bool
 gc::IsSlotMarked(HeapSlot *s)
 {
     return IsMarked(s);
 }
 
@@ -1026,16 +1063,22 @@ gc::IsCellMarked(Cell **thingp)
 }
 
 bool
 gc::IsCellAboutToBeFinalized(Cell **thingp)
 {
     return IsAboutToBeFinalized<Cell>(thingp);
 }
 
+bool
+gc::IsCellAboutToBeFinalizedFromAnyThread(Cell **thingp)
+{
+    return IsAboutToBeFinalizedFromAnyThread<Cell>(thingp);
+}
+
 /*** Push Mark Stack ***/
 
 #define JS_COMPARTMENT_ASSERT(rt, thing)                                \
     MOZ_ASSERT((thing)->zone()->isGCMarking())
 
 #define JS_COMPARTMENT_ASSERT_STR(rt, thing)                            \
     MOZ_ASSERT((thing)->zone()->isGCMarking() ||                        \
                (rt)->isAtomsZone((thing)->zone()));
--- a/js/src/gc/Marking.h
+++ b/js/src/gc/Marking.h
@@ -93,16 +93,17 @@ namespace gc {
 void Mark##base(JSTracer *trc, BarrieredBase<type*> *thing, const char *name);                    \
 void Mark##base##Root(JSTracer *trc, type **thingp, const char *name);                            \
 void Mark##base##Unbarriered(JSTracer *trc, type **thingp, const char *name);                     \
 void Mark##base##Range(JSTracer *trc, size_t len, HeapPtr<type*> *thing, const char *name);       \
 void Mark##base##RootRange(JSTracer *trc, size_t len, type **thing, const char *name);            \
 bool Is##base##Marked(type **thingp);                                                             \
 bool Is##base##Marked(BarrieredBase<type*> *thingp);                                              \
 bool Is##base##AboutToBeFinalized(type **thingp);                                                 \
+bool Is##base##AboutToBeFinalizedFromAnyThread(type **thingp);                                    \
 bool Is##base##AboutToBeFinalized(BarrieredBase<type*> *thingp);                                  \
 type *Update##base##IfRelocated(JSRuntime *rt, BarrieredBase<type*> *thingp);                     \
 type *Update##base##IfRelocated(JSRuntime *rt, type **thingp);
 
 DeclMarker(BaseShape, BaseShape)
 DeclMarker(BaseShape, UnownedBaseShape)
 DeclMarker(JitCode, jit::JitCode)
 DeclMarker(Object, NativeObject)
@@ -214,16 +215,19 @@ void
 MarkTypeRoot(JSTracer *trc, types::Type *v, const char *name);
 
 bool
 IsValueMarked(Value *v);
 
 bool
 IsValueAboutToBeFinalized(Value *v);
 
+bool
+IsValueAboutToBeFinalizedFromAnyThread(Value *v);
+
 /*** Slot Marking ***/
 
 bool
 IsSlotMarked(HeapSlot *s);
 
 void
 MarkSlot(JSTracer *trc, HeapSlot *s, const char *name);
 
@@ -322,16 +326,19 @@ Mark(JSTracer *trc, ScopeObject **obj, c
 }
 
 bool
 IsCellMarked(Cell **thingp);
 
 bool
 IsCellAboutToBeFinalized(Cell **thing);
 
+bool
+IsCellAboutToBeFinalizedFromAnyThread(Cell **thing);
+
 inline bool
 IsMarked(BarrieredBase<Value> *v)
 {
     if (!v->isMarkable())
         return true;
     return IsValueMarked(v->unsafeGet());
 }
 
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -14,16 +14,17 @@
 #include <stdio.h>
 
 #include "jscrashreport.h"
 #include "jsprf.h"
 #include "jsutil.h"
 #include "prmjtime.h"
 
 #include "gc/Memory.h"
+#include "vm/HelperThreads.h"
 #include "vm/Runtime.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::gcstats;
 
 using mozilla::PodArrayZero;
 
@@ -832,16 +833,26 @@ Statistics::endPhase(Phase phase)
     phaseNestingDepth--;
 
     int64_t t = PRMJ_Now() - phaseStartTimes[phase];
     slices.back().phaseTimes[phase] += t;
     phaseTimes[phase] += t;
     phaseStartTimes[phase] = 0;
 }
 
+void
+Statistics::endParallelPhase(Phase phase, const GCParallelTask *task)
+{
+    phaseNestingDepth--;
+
+    slices.back().phaseTimes[phase] += task->duration();
+    phaseTimes[phase] += task->duration();
+    phaseStartTimes[phase] = 0;
+}
+
 int64_t
 Statistics::beginSCC()
 {
     return PRMJ_Now();
 }
 
 void
 Statistics::endSCC(unsigned scc, int64_t start)
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -15,16 +15,19 @@
 #include "jspubtd.h"
 
 #include "js/GCAPI.h"
 #include "js/Vector.h"
 
 struct JSCompartment;
 
 namespace js {
+
+class GCParallelTask;
+
 namespace gcstats {
 
 enum Phase {
     PHASE_GC_BEGIN,
     PHASE_WAIT_BACKGROUND_THREAD,
     PHASE_MARK_DISCARD_CODE,
     PHASE_PURGE,
     PHASE_MARK,
@@ -104,16 +107,17 @@ struct ZoneGCStats
 
 struct Statistics
 {
     explicit Statistics(JSRuntime *rt);
     ~Statistics();
 
     void beginPhase(Phase phase);
     void endPhase(Phase phase);
+    void endParallelPhase(Phase phase, const GCParallelTask *task);
 
     void beginSlice(const ZoneGCStats &zoneStats, JS::gcreason::Reason reason);
     void endSlice();
 
     void reset(const char *reason) { slices.back().resetReason = reason; }
     void nonincremental(const char *reason) { nonincrementalReason = reason; }
 
     void count(Stat s) {
@@ -229,35 +233,51 @@ struct AutoGCSlice
     Statistics &stats;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 struct AutoPhase
 {
     AutoPhase(Statistics &stats, Phase phase
               MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : stats(stats), phase(phase), enabled(true)
+      : stats(stats), task(nullptr), phase(phase), enabled(true)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         stats.beginPhase(phase);
     }
+
     AutoPhase(Statistics &stats, bool condition, Phase phase
               MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : stats(stats), phase(phase), enabled(condition)
+      : stats(stats), task(nullptr), phase(phase), enabled(condition)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         if (enabled)
             stats.beginPhase(phase);
     }
-    ~AutoPhase() {
+
+    AutoPhase(Statistics &stats, const GCParallelTask &task, Phase phase
+              MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : stats(stats), task(&task), phase(phase), enabled(true)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         if (enabled)
-            stats.endPhase(phase);
+            stats.beginPhase(phase);
+    }
+
+    ~AutoPhase() {
+        if (enabled) {
+            if (task)
+                stats.endParallelPhase(phase, task);
+            else
+                stats.endPhase(phase);
+        }
     }
 
     Statistics &stats;
+    const GCParallelTask *task;
     Phase phase;
     bool enabled;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 struct AutoSCC
 {
     AutoSCC(Statistics &stats, unsigned scc
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -1211,16 +1211,20 @@ Instruction *
 BOffImm::getDest(Instruction *src)
 {
     // TODO: It is probably worthwhile to verify that src is actually a branch.
     // NOTE: This does not explicitly shift the offset of the destination left by 2,
     // since it is indexing into an array of instruction sized objects.
     return &src[(((int32_t)data << 8) >> 8) + 2];
 }
 
+const js::jit::DoubleEncoder::DoubleEntry js::jit::DoubleEncoder::table[256] = {
+#include "jit/arm/DoubleEntryTable.tbl"
+};
+
 // VFPRegister implementation
 VFPRegister
 VFPRegister::doubleOverlay(unsigned int which) const
 {
     MOZ_ASSERT(!_isInvalid);
     MOZ_ASSERT(which == 0);
     if (kind != Double)
         return VFPRegister(code_ >> 1, Double);
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -445,36 +445,33 @@ struct Imm8VFPOffData
         MOZ_ASSERT((imm & ~(0xff)) == 0);
     }
 };
 
 // ARM can magically encode 256 very special immediates to be moved into a
 // register.
 struct Imm8VFPImmData
 {
-  private:
+    // This structure's members are public and it has no constructor to
+    // initialize them, for a very special reason. Were this structure to
+    // have a constructor, the initialization for DoubleEncoder's internal
+    // table (see below) would require a rather large static constructor on
+    // some of our supported compilers. The known solution to this is to mark
+    // the constructor MOZ_CONSTEXPR, but, again, some of our supported
+    // compilers don't support MOZ_CONSTEXPR! So we are reduced to public
+    // members and eschewing a constructor in hopes that the initialization
+    // of DoubleEncoder's table is correct.
     uint32_t imm4L : 4;
-    uint32_t pad : 12;
     uint32_t imm4H : 4;
-    int32_t isInvalid : 12;
-
-  public:
-    Imm8VFPImmData()
-      : imm4L(-1U & 0xf), imm4H(-1U & 0xf), isInvalid(-1)
-    { }
-
-    Imm8VFPImmData(uint32_t imm)
-      : imm4L(imm&0xf), imm4H(imm >> 4), isInvalid(0)
-    {
-        MOZ_ASSERT(imm <= 0xff);
-    }
+    int32_t isInvalid : 24;
 
     uint32_t encode() {
-        if (isInvalid != 0)
-            return -1;
+        // This assert is an attempting at ensuring that we don't create random
+        // instances of this structure and then asking to encode() it.
+        MOZ_ASSERT(isInvalid == 0);
         return imm4L | (imm4H << 16);
     };
 };
 
 struct Imm12Data
 {
     uint32_t data : 12;
     uint32_t encode() {
@@ -2185,60 +2182,25 @@ GetDoubleArgStackDisp(uint32_t usedIntAr
     return (intSlots + doubleSlots + *padding) * sizeof(intptr_t);
 }
 
 #endif
 
 
 
 class DoubleEncoder {
-    uint32_t rep(bool b, uint32_t count) {
-        uint32_t ret = 0;
-        for (uint32_t i = 0; i < count; i++)
-            ret = (ret << 1) | b;
-        return ret;
-    }
-
-    uint32_t encode(uint8_t value) {
-        // ARM ARM "VFP modified immediate constants"
-        //  aBbbbbbb bbcdefgh 000...
-        // We want to return the top 32 bits of the double the rest are 0.
-        bool a = value >> 7;
-        bool b = value >> 6 & 1;
-        bool B = !b;
-        uint32_t cdefgh = value & 0x3f;
-        return         a << 31 |
-                       B << 30 |
-               rep(b, 8) << 22 |
-                  cdefgh << 16;
-    }
-
     struct DoubleEntry
     {
         uint32_t dblTop;
         datastore::Imm8VFPImmData data;
-
-        DoubleEntry()
-          : dblTop(-1)
-        { }
-        DoubleEntry(uint32_t dblTop_, datastore::Imm8VFPImmData data_)
-          : dblTop(dblTop_), data(data_)
-        { }
     };
 
-    mozilla::Array<DoubleEntry, 256> table;
+    static const DoubleEntry table[256];
 
   public:
-    DoubleEncoder()
-    {
-        for (int i = 0; i < 256; i++) {
-            table[i] = DoubleEntry(encode(i), datastore::Imm8VFPImmData(i));
-        }
-    }
-
     bool lookup(uint32_t top, datastore::Imm8VFPImmData *ret) {
         for (int i = 0; i < 256; i++) {
             if (table[i].dblTop == top) {
                 *ret = table[i].data;
                 return true;
             }
         }
         return false;
new file mode 100644
--- /dev/null
+++ b/js/src/jit/arm/DoubleEntryTable.tbl
@@ -0,0 +1,257 @@
+/* THIS FILE IS AUTOMATICALLY GENERATED BY gen-double-encode-table.py.  */
+  { 0x40000000, { 0, 0, 0 } },
+  { 0x40010000, { 1, 0, 0 } },
+  { 0x40020000, { 2, 0, 0 } },
+  { 0x40030000, { 3, 0, 0 } },
+  { 0x40040000, { 4, 0, 0 } },
+  { 0x40050000, { 5, 0, 0 } },
+  { 0x40060000, { 6, 0, 0 } },
+  { 0x40070000, { 7, 0, 0 } },
+  { 0x40080000, { 8, 0, 0 } },
+  { 0x40090000, { 9, 0, 0 } },
+  { 0x400a0000, { 10, 0, 0 } },
+  { 0x400b0000, { 11, 0, 0 } },
+  { 0x400c0000, { 12, 0, 0 } },
+  { 0x400d0000, { 13, 0, 0 } },
+  { 0x400e0000, { 14, 0, 0 } },
+  { 0x400f0000, { 15, 0, 0 } },
+  { 0x40100000, { 0, 1, 0 } },
+  { 0x40110000, { 1, 1, 0 } },
+  { 0x40120000, { 2, 1, 0 } },
+  { 0x40130000, { 3, 1, 0 } },
+  { 0x40140000, { 4, 1, 0 } },
+  { 0x40150000, { 5, 1, 0 } },
+  { 0x40160000, { 6, 1, 0 } },
+  { 0x40170000, { 7, 1, 0 } },
+  { 0x40180000, { 8, 1, 0 } },
+  { 0x40190000, { 9, 1, 0 } },
+  { 0x401a0000, { 10, 1, 0 } },
+  { 0x401b0000, { 11, 1, 0 } },
+  { 0x401c0000, { 12, 1, 0 } },
+  { 0x401d0000, { 13, 1, 0 } },
+  { 0x401e0000, { 14, 1, 0 } },
+  { 0x401f0000, { 15, 1, 0 } },
+  { 0x40200000, { 0, 2, 0 } },
+  { 0x40210000, { 1, 2, 0 } },
+  { 0x40220000, { 2, 2, 0 } },
+  { 0x40230000, { 3, 2, 0 } },
+  { 0x40240000, { 4, 2, 0 } },
+  { 0x40250000, { 5, 2, 0 } },
+  { 0x40260000, { 6, 2, 0 } },
+  { 0x40270000, { 7, 2, 0 } },
+  { 0x40280000, { 8, 2, 0 } },
+  { 0x40290000, { 9, 2, 0 } },
+  { 0x402a0000, { 10, 2, 0 } },
+  { 0x402b0000, { 11, 2, 0 } },
+  { 0x402c0000, { 12, 2, 0 } },
+  { 0x402d0000, { 13, 2, 0 } },
+  { 0x402e0000, { 14, 2, 0 } },
+  { 0x402f0000, { 15, 2, 0 } },
+  { 0x40300000, { 0, 3, 0 } },
+  { 0x40310000, { 1, 3, 0 } },
+  { 0x40320000, { 2, 3, 0 } },
+  { 0x40330000, { 3, 3, 0 } },
+  { 0x40340000, { 4, 3, 0 } },
+  { 0x40350000, { 5, 3, 0 } },
+  { 0x40360000, { 6, 3, 0 } },
+  { 0x40370000, { 7, 3, 0 } },
+  { 0x40380000, { 8, 3, 0 } },
+  { 0x40390000, { 9, 3, 0 } },
+  { 0x403a0000, { 10, 3, 0 } },
+  { 0x403b0000, { 11, 3, 0 } },
+  { 0x403c0000, { 12, 3, 0 } },
+  { 0x403d0000, { 13, 3, 0 } },
+  { 0x403e0000, { 14, 3, 0 } },
+  { 0x403f0000, { 15, 3, 0 } },
+  { 0x3fc00000, { 0, 4, 0 } },
+  { 0x3fc10000, { 1, 4, 0 } },
+  { 0x3fc20000, { 2, 4, 0 } },
+  { 0x3fc30000, { 3, 4, 0 } },
+  { 0x3fc40000, { 4, 4, 0 } },
+  { 0x3fc50000, { 5, 4, 0 } },
+  { 0x3fc60000, { 6, 4, 0 } },
+  { 0x3fc70000, { 7, 4, 0 } },
+  { 0x3fc80000, { 8, 4, 0 } },
+  { 0x3fc90000, { 9, 4, 0 } },
+  { 0x3fca0000, { 10, 4, 0 } },
+  { 0x3fcb0000, { 11, 4, 0 } },
+  { 0x3fcc0000, { 12, 4, 0 } },
+  { 0x3fcd0000, { 13, 4, 0 } },
+  { 0x3fce0000, { 14, 4, 0 } },
+  { 0x3fcf0000, { 15, 4, 0 } },
+  { 0x3fd00000, { 0, 5, 0 } },
+  { 0x3fd10000, { 1, 5, 0 } },
+  { 0x3fd20000, { 2, 5, 0 } },
+  { 0x3fd30000, { 3, 5, 0 } },
+  { 0x3fd40000, { 4, 5, 0 } },
+  { 0x3fd50000, { 5, 5, 0 } },
+  { 0x3fd60000, { 6, 5, 0 } },
+  { 0x3fd70000, { 7, 5, 0 } },
+  { 0x3fd80000, { 8, 5, 0 } },
+  { 0x3fd90000, { 9, 5, 0 } },
+  { 0x3fda0000, { 10, 5, 0 } },
+  { 0x3fdb0000, { 11, 5, 0 } },
+  { 0x3fdc0000, { 12, 5, 0 } },
+  { 0x3fdd0000, { 13, 5, 0 } },
+  { 0x3fde0000, { 14, 5, 0 } },
+  { 0x3fdf0000, { 15, 5, 0 } },
+  { 0x3fe00000, { 0, 6, 0 } },
+  { 0x3fe10000, { 1, 6, 0 } },
+  { 0x3fe20000, { 2, 6, 0 } },
+  { 0x3fe30000, { 3, 6, 0 } },
+  { 0x3fe40000, { 4, 6, 0 } },
+  { 0x3fe50000, { 5, 6, 0 } },
+  { 0x3fe60000, { 6, 6, 0 } },
+  { 0x3fe70000, { 7, 6, 0 } },
+  { 0x3fe80000, { 8, 6, 0 } },
+  { 0x3fe90000, { 9, 6, 0 } },
+  { 0x3fea0000, { 10, 6, 0 } },
+  { 0x3feb0000, { 11, 6, 0 } },
+  { 0x3fec0000, { 12, 6, 0 } },
+  { 0x3fed0000, { 13, 6, 0 } },
+  { 0x3fee0000, { 14, 6, 0 } },
+  { 0x3fef0000, { 15, 6, 0 } },
+  { 0x3ff00000, { 0, 7, 0 } },
+  { 0x3ff10000, { 1, 7, 0 } },
+  { 0x3ff20000, { 2, 7, 0 } },
+  { 0x3ff30000, { 3, 7, 0 } },
+  { 0x3ff40000, { 4, 7, 0 } },
+  { 0x3ff50000, { 5, 7, 0 } },
+  { 0x3ff60000, { 6, 7, 0 } },
+  { 0x3ff70000, { 7, 7, 0 } },
+  { 0x3ff80000, { 8, 7, 0 } },
+  { 0x3ff90000, { 9, 7, 0 } },
+  { 0x3ffa0000, { 10, 7, 0 } },
+  { 0x3ffb0000, { 11, 7, 0 } },
+  { 0x3ffc0000, { 12, 7, 0 } },
+  { 0x3ffd0000, { 13, 7, 0 } },
+  { 0x3ffe0000, { 14, 7, 0 } },
+  { 0x3fff0000, { 15, 7, 0 } },
+  { 0xc0000000, { 0, 8, 0 } },
+  { 0xc0010000, { 1, 8, 0 } },
+  { 0xc0020000, { 2, 8, 0 } },
+  { 0xc0030000, { 3, 8, 0 } },
+  { 0xc0040000, { 4, 8, 0 } },
+  { 0xc0050000, { 5, 8, 0 } },
+  { 0xc0060000, { 6, 8, 0 } },
+  { 0xc0070000, { 7, 8, 0 } },
+  { 0xc0080000, { 8, 8, 0 } },
+  { 0xc0090000, { 9, 8, 0 } },
+  { 0xc00a0000, { 10, 8, 0 } },
+  { 0xc00b0000, { 11, 8, 0 } },
+  { 0xc00c0000, { 12, 8, 0 } },
+  { 0xc00d0000, { 13, 8, 0 } },
+  { 0xc00e0000, { 14, 8, 0 } },
+  { 0xc00f0000, { 15, 8, 0 } },
+  { 0xc0100000, { 0, 9, 0 } },
+  { 0xc0110000, { 1, 9, 0 } },
+  { 0xc0120000, { 2, 9, 0 } },
+  { 0xc0130000, { 3, 9, 0 } },
+  { 0xc0140000, { 4, 9, 0 } },
+  { 0xc0150000, { 5, 9, 0 } },
+  { 0xc0160000, { 6, 9, 0 } },
+  { 0xc0170000, { 7, 9, 0 } },
+  { 0xc0180000, { 8, 9, 0 } },
+  { 0xc0190000, { 9, 9, 0 } },
+  { 0xc01a0000, { 10, 9, 0 } },
+  { 0xc01b0000, { 11, 9, 0 } },
+  { 0xc01c0000, { 12, 9, 0 } },
+  { 0xc01d0000, { 13, 9, 0 } },
+  { 0xc01e0000, { 14, 9, 0 } },
+  { 0xc01f0000, { 15, 9, 0 } },
+  { 0xc0200000, { 0, 10, 0 } },
+  { 0xc0210000, { 1, 10, 0 } },
+  { 0xc0220000, { 2, 10, 0 } },
+  { 0xc0230000, { 3, 10, 0 } },
+  { 0xc0240000, { 4, 10, 0 } },
+  { 0xc0250000, { 5, 10, 0 } },
+  { 0xc0260000, { 6, 10, 0 } },
+  { 0xc0270000, { 7, 10, 0 } },
+  { 0xc0280000, { 8, 10, 0 } },
+  { 0xc0290000, { 9, 10, 0 } },
+  { 0xc02a0000, { 10, 10, 0 } },
+  { 0xc02b0000, { 11, 10, 0 } },
+  { 0xc02c0000, { 12, 10, 0 } },
+  { 0xc02d0000, { 13, 10, 0 } },
+  { 0xc02e0000, { 14, 10, 0 } },
+  { 0xc02f0000, { 15, 10, 0 } },
+  { 0xc0300000, { 0, 11, 0 } },
+  { 0xc0310000, { 1, 11, 0 } },
+  { 0xc0320000, { 2, 11, 0 } },
+  { 0xc0330000, { 3, 11, 0 } },
+  { 0xc0340000, { 4, 11, 0 } },
+  { 0xc0350000, { 5, 11, 0 } },
+  { 0xc0360000, { 6, 11, 0 } },
+  { 0xc0370000, { 7, 11, 0 } },
+  { 0xc0380000, { 8, 11, 0 } },
+  { 0xc0390000, { 9, 11, 0 } },
+  { 0xc03a0000, { 10, 11, 0 } },
+  { 0xc03b0000, { 11, 11, 0 } },
+  { 0xc03c0000, { 12, 11, 0 } },
+  { 0xc03d0000, { 13, 11, 0 } },
+  { 0xc03e0000, { 14, 11, 0 } },
+  { 0xc03f0000, { 15, 11, 0 } },
+  { 0xbfc00000, { 0, 12, 0 } },
+  { 0xbfc10000, { 1, 12, 0 } },
+  { 0xbfc20000, { 2, 12, 0 } },
+  { 0xbfc30000, { 3, 12, 0 } },
+  { 0xbfc40000, { 4, 12, 0 } },
+  { 0xbfc50000, { 5, 12, 0 } },
+  { 0xbfc60000, { 6, 12, 0 } },
+  { 0xbfc70000, { 7, 12, 0 } },
+  { 0xbfc80000, { 8, 12, 0 } },
+  { 0xbfc90000, { 9, 12, 0 } },
+  { 0xbfca0000, { 10, 12, 0 } },
+  { 0xbfcb0000, { 11, 12, 0 } },
+  { 0xbfcc0000, { 12, 12, 0 } },
+  { 0xbfcd0000, { 13, 12, 0 } },
+  { 0xbfce0000, { 14, 12, 0 } },
+  { 0xbfcf0000, { 15, 12, 0 } },
+  { 0xbfd00000, { 0, 13, 0 } },
+  { 0xbfd10000, { 1, 13, 0 } },
+  { 0xbfd20000, { 2, 13, 0 } },
+  { 0xbfd30000, { 3, 13, 0 } },
+  { 0xbfd40000, { 4, 13, 0 } },
+  { 0xbfd50000, { 5, 13, 0 } },
+  { 0xbfd60000, { 6, 13, 0 } },
+  { 0xbfd70000, { 7, 13, 0 } },
+  { 0xbfd80000, { 8, 13, 0 } },
+  { 0xbfd90000, { 9, 13, 0 } },
+  { 0xbfda0000, { 10, 13, 0 } },
+  { 0xbfdb0000, { 11, 13, 0 } },
+  { 0xbfdc0000, { 12, 13, 0 } },
+  { 0xbfdd0000, { 13, 13, 0 } },
+  { 0xbfde0000, { 14, 13, 0 } },
+  { 0xbfdf0000, { 15, 13, 0 } },
+  { 0xbfe00000, { 0, 14, 0 } },
+  { 0xbfe10000, { 1, 14, 0 } },
+  { 0xbfe20000, { 2, 14, 0 } },
+  { 0xbfe30000, { 3, 14, 0 } },
+  { 0xbfe40000, { 4, 14, 0 } },
+  { 0xbfe50000, { 5, 14, 0 } },
+  { 0xbfe60000, { 6, 14, 0 } },
+  { 0xbfe70000, { 7, 14, 0 } },
+  { 0xbfe80000, { 8, 14, 0 } },
+  { 0xbfe90000, { 9, 14, 0 } },
+  { 0xbfea0000, { 10, 14, 0 } },
+  { 0xbfeb0000, { 11, 14, 0 } },
+  { 0xbfec0000, { 12, 14, 0 } },
+  { 0xbfed0000, { 13, 14, 0 } },
+  { 0xbfee0000, { 14, 14, 0 } },
+  { 0xbfef0000, { 15, 14, 0 } },
+  { 0xbff00000, { 0, 15, 0 } },
+  { 0xbff10000, { 1, 15, 0 } },
+  { 0xbff20000, { 2, 15, 0 } },
+  { 0xbff30000, { 3, 15, 0 } },
+  { 0xbff40000, { 4, 15, 0 } },
+  { 0xbff50000, { 5, 15, 0 } },
+  { 0xbff60000, { 6, 15, 0 } },
+  { 0xbff70000, { 7, 15, 0 } },
+  { 0xbff80000, { 8, 15, 0 } },
+  { 0xbff90000, { 9, 15, 0 } },
+  { 0xbffa0000, { 10, 15, 0 } },
+  { 0xbffb0000, { 11, 15, 0 } },
+  { 0xbffc0000, { 12, 15, 0 } },
+  { 0xbffd0000, { 13, 15, 0 } },
+  { 0xbffe0000, { 14, 15, 0 } },
+  { 0xbfff0000, { 15, 15, 0 } },
new file mode 100644
--- /dev/null
+++ b/js/src/jit/arm/gen-double-encoder-table.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+"""Generate tables of immediately-encodable VFP doubles.
+
+DOES NOT get automatically run during the build process.  If you need to
+modify this file (which is unlikely), you must re-run this script:
+
+python gen-double-encode-table.py > $(topsrcdir)/path/to/DoubleEntryTable.tbl
+"""
+
+import operator
+
+def rep(bit, count):
+    return reduce(operator.ior, [bit << c for c in range(count)])
+
+def encodeDouble(value):
+    """Generate an ARM ARM 'VFP modified immediate constant' with format:
+    aBbbbbbb bbcdefgh 000...
+
+    We will return the top 32 bits of the double; the rest are 0."""
+    assert (0 <= value) and (value <= 255)
+    a = value >> 7
+    b = (value >> 6) & 1
+    B = int(b == 0)
+    cdefgh = value & 0x3f
+    return (a << 31) | (B << 30) | (rep(b, 8) << 22) | cdefgh << 16
+
+print '/* THIS FILE IS AUTOMATICALLY GENERATED BY gen-double-encode-table.py.  */'
+for i in range(256):
+    print '  { 0x%08x, { %d, %d, 0 } },' % (encodeDouble(i), i & 0xf, i >> 4)
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -249,17 +249,17 @@ void
 JSRuntime::sweepAtoms()
 {
     if (!atoms_)
         return;
 
     for (AtomSet::Enum e(*atoms_); !e.empty(); e.popFront()) {
         AtomStateEntry entry = e.front();
         JSAtom *atom = entry.asPtr();
-        bool isDying = IsStringAboutToBeFinalized(&atom);
+        bool isDying = IsStringAboutToBeFinalizedFromAnyThread(&atom);
 
         /* Pinned or interned key cannot be finalized. */
         MOZ_ASSERT_IF(hasContexts() && entry.isTagged(), !isDying);
 
         if (isDying)
             e.removeFront();
     }
 }
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -96,18 +96,19 @@ js::TraceCycleDetectionSet(JSTracer *trc
 }
 
 void
 JSCompartment::sweepCallsiteClones()
 {
     if (callsiteClones.initialized()) {
         for (CallsiteCloneTable::Enum e(callsiteClones); !e.empty(); e.popFront()) {
             CallsiteCloneKey key = e.front().key();
-            if (IsObjectAboutToBeFinalized(&key.original) || IsScriptAboutToBeFinalized(&key.script) ||
-                IsObjectAboutToBeFinalized(e.front().value().unsafeGet()))
+            if (IsObjectAboutToBeFinalizedFromAnyThread(&key.original) ||
+                IsScriptAboutToBeFinalizedFromAnyThread(&key.script) ||
+                IsObjectAboutToBeFinalizedFromAnyThread(e.front().value().unsafeGet()))
             {
                 e.removeFront();
             } else if (key != e.front().key()) {
                 e.rekeyFront(key);
             }
         }
     }
 }
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -530,54 +530,54 @@ JSCompartment::markRoots(JSTracer *trc)
 
     if (jitCompartment_)
         jitCompartment_->mark(trc, this);
 
     /*
      * If a compartment is on-stack, we mark its global so that
      * JSContext::global() remains valid.
      */
-    if (enterCompartmentDepth && global_)
+    if (enterCompartmentDepth && global_.unbarrieredGet())
         MarkObjectRoot(trc, global_.unsafeGet(), "on-stack compartment global");
 }
 
 void
 JSCompartment::sweepInnerViews()
 {
-    innerViews.sweep(runtimeFromMainThread());
+    innerViews.sweep(runtimeFromAnyThread());
 }
 
 void
 JSCompartment::sweepTypeObjectTables()
 {
     sweepNewTypeObjectTable(newTypeObjects);
     sweepNewTypeObjectTable(lazyTypeObjects);
 }
 
 void
 JSCompartment::sweepSavedStacks()
 {
-    savedStacks_.sweep(runtimeFromMainThread());
+    savedStacks_.sweep(runtimeFromAnyThread());
 }
 
 void
 JSCompartment::sweepGlobalObject(FreeOp *fop)
 {
-    if (global_ && IsObjectAboutToBeFinalized(global_.unsafeGet())) {
+    if (global_.unbarrieredGet() && IsObjectAboutToBeFinalizedFromAnyThread(global_.unsafeGet())) {
         if (debugMode())
             Debugger::detachAllDebuggersFromGlobal(fop, global_);
         global_.set(nullptr);
     }
 }
 
 void
 JSCompartment::sweepSelfHostingScriptSource()
 {
-    if (selfHostingScriptSource &&
-        IsObjectAboutToBeFinalized((JSObject **) selfHostingScriptSource.unsafeGet()))
+    if (selfHostingScriptSource.unbarrieredGet() &&
+        IsObjectAboutToBeFinalizedFromAnyThread((JSObject **) selfHostingScriptSource.unsafeGet()))
     {
         selfHostingScriptSource.set(nullptr);
     }
 }
 
 void
 JSCompartment::sweepJitCompartment(FreeOp *fop)
 {
@@ -588,23 +588,23 @@ JSCompartment::sweepJitCompartment(FreeO
 void
 JSCompartment::sweepRegExps()
 {
     /*
      * JIT code increments activeWarmUpCounter for any RegExpShared used by jit
      * code for the lifetime of the JIT script. Thus, we must perform
      * sweeping after clearing jit code.
      */
-    regExps.sweep(runtimeFromMainThread());
+    regExps.sweep(runtimeFromAnyThread());
 }
 
 void
 JSCompartment::sweepDebugScopes()
 {
-    JSRuntime *rt = runtimeFromMainThread();
+    JSRuntime *rt = runtimeFromAnyThread();
     if (debugScopes)
         debugScopes->sweep(rt);
 }
 
 void
 JSCompartment::sweepWeakMaps()
 {
     /* Finalize unreachable (key,value) pairs in all weak maps. */
@@ -614,36 +614,36 @@ JSCompartment::sweepWeakMaps()
 void
 JSCompartment::sweepNativeIterators()
 {
     /* Sweep list of native iterators. */
     NativeIterator *ni = enumerators->next();
     while (ni != enumerators) {
         JSObject *iterObj = ni->iterObj();
         NativeIterator *next = ni->next();
-        if (gc::IsObjectAboutToBeFinalized(&iterObj))
+        if (gc::IsObjectAboutToBeFinalizedFromAnyThread(&iterObj))
             ni->unlink();
         ni = next;
     }
 }
 
 /*
  * Remove dead wrappers from the table. We must sweep all compartments, since
  * string entries in the crossCompartmentWrappers table are not marked during
  * markCrossCompartmentWrappers.
  */
 void
 JSCompartment::sweepCrossCompartmentWrappers()
 {
     /* Remove dead wrappers from the table. */
     for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
         CrossCompartmentKey key = e.front().key();
-        bool keyDying = IsCellAboutToBeFinalized(&key.wrapped);
-        bool valDying = IsValueAboutToBeFinalized(e.front().value().unsafeGet());
-        bool dbgDying = key.debugger && IsObjectAboutToBeFinalized(&key.debugger);
+        bool keyDying = IsCellAboutToBeFinalizedFromAnyThread(&key.wrapped);
+        bool valDying = IsValueAboutToBeFinalizedFromAnyThread(e.front().value().unsafeGet());
+        bool dbgDying = key.debugger && IsObjectAboutToBeFinalizedFromAnyThread(&key.debugger);
         if (keyDying || valDying || dbgDying) {
             MOZ_ASSERT(key.kind != CrossCompartmentKey::StringWrapper);
             e.removeFront();
         } else if (key.wrapped != e.front().key().wrapped ||
                    key.debugger != e.front().key().debugger)
         {
             e.rekeyFront(key);
         }
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -249,16 +249,17 @@ struct JSCompartment
     /* Set of initial shapes in the compartment. */
     js::InitialShapeSet          initialShapes;
     void sweepInitialShapeTable();
 
     /* Set of default 'new' or lazy types in the compartment. */
     js::types::TypeObjectWithNewScriptSet newTypeObjects;
     js::types::TypeObjectWithNewScriptSet lazyTypeObjects;
     void sweepNewTypeObjectTable(js::types::TypeObjectWithNewScriptSet &table);
+
 #ifdef JSGC_HASH_TABLE_CHECKS
     void checkTypeObjectTablesAfterMovingGC();
     void checkTypeObjectTableAfterMovingGC(js::types::TypeObjectWithNewScriptSet &table);
     void checkInitialShapesTableAfterMovingGC();
     void checkWrapperMapAfterMovingGC();
 #endif
 
     /*
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -2145,39 +2145,16 @@ js::ReportIncompatible(JSContext *cx, Ca
         JSAutoByteString funNameBytes;
         if (const char *funName = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_METHOD,
                                  funName, "method", InformalValueTypeName(call.thisv()));
         }
     }
 }
 
-bool
-JSObject::hasIdempotentProtoChain() const
-{
-    // Return false if obj (or an object on its proto chain) is non-native or
-    // has a resolve or lookup hook.
-    JSObject *obj = const_cast<JSObject *>(this);
-    while (true) {
-        if (!obj->isNative())
-            return false;
-
-        JSResolveOp resolve = obj->getClass()->resolve;
-        if (resolve != JS_ResolveStub && resolve != (JSResolveOp) js::fun_resolve)
-            return false;
-
-        if (obj->getOps()->lookupProperty || obj->getOps()->lookupGeneric || obj->getOps()->lookupElement)
-            return false;
-
-        obj = obj->getProto();
-        if (!obj)
-            return true;
-    }
-}
-
 namespace JS {
 namespace detail {
 
 JS_PUBLIC_API(void)
 CheckIsValidConstructible(Value calleev)
 {
     JSObject *callee = &calleev.toObject();
     if (callee->is<JSFunction>())
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -658,74 +658,74 @@ FreeChunk(JSRuntime *rt, Chunk *p)
 {
     UnmapPages(static_cast<void *>(p), ChunkSize);
 }
 
 /* Must be called with the GC lock taken. */
 inline Chunk *
 ChunkPool::get(JSRuntime *rt)
 {
-    Chunk *chunk = emptyChunkListHead;
+    Chunk *chunk = head_;
     if (!chunk) {
-        MOZ_ASSERT(!emptyCount);
+        MOZ_ASSERT(!count_);
         return nullptr;
     }
 
-    MOZ_ASSERT(emptyCount);
-    emptyChunkListHead = chunk->info.next;
-    --emptyCount;
+    MOZ_ASSERT(count_);
+    head_ = chunk->info.next;
+    --count_;
     return chunk;
 }
 
 /* Must be called either during the GC or with the GC lock taken. */
 inline void
 ChunkPool::put(Chunk *chunk)
 {
     chunk->info.age = 0;
-    chunk->info.next = emptyChunkListHead;
-    emptyChunkListHead = chunk;
-    emptyCount++;
+    chunk->info.next = head_;
+    head_ = chunk;
+    count_++;
 }
 
 inline Chunk *
 ChunkPool::Enum::front()
 {
     Chunk *chunk = *chunkp;
-    MOZ_ASSERT_IF(chunk, pool.getEmptyCount() != 0);
+    MOZ_ASSERT_IF(chunk, pool.count() != 0);
     return chunk;
 }
 
 inline void
 ChunkPool::Enum::popFront()
 {
     MOZ_ASSERT(!empty());
     chunkp = &front()->info.next;
 }
 
 inline void
 ChunkPool::Enum::removeAndPopFront()
 {
     MOZ_ASSERT(!empty());
     *chunkp = front()->info.next;
-    --pool.emptyCount;
+    --pool.count_;
 }
 
 /* Must be called either during the GC or with the GC lock taken. */
 Chunk *
 GCRuntime::expireChunkPool(bool shrinkBuffers, bool releaseAll)
 {
     /*
      * Return old empty chunks to the system while preserving the order of
      * other chunks in the list. This way, if the GC runs several times
      * without emptying the list, the older chunks will stay at the tail
      * and are more likely to reach the max age.
      */
     Chunk *freeList = nullptr;
     unsigned freeChunkCount = 0;
-    for (ChunkPool::Enum e(chunkPool); !e.empty(); ) {
+    for (ChunkPool::Enum e(emptyChunks); !e.empty(); ) {
         Chunk *chunk = e.front();
         MOZ_ASSERT(chunk->unused());
         MOZ_ASSERT(!chunkSet.has(chunk));
         if (releaseAll || freeChunkCount >= tunables.maxEmptyChunkCount() ||
             (freeChunkCount >= tunables.minEmptyChunkCount() &&
              (shrinkBuffers || chunk->info.age == MAX_EMPTY_CHUNK_AGE)))
         {
             e.removeAndPopFront();
@@ -734,19 +734,19 @@ GCRuntime::expireChunkPool(bool shrinkBu
             freeList = chunk;
         } else {
             /* Keep the chunk but increase its age. */
             ++freeChunkCount;
             ++chunk->info.age;
             e.popFront();
         }
     }
-    MOZ_ASSERT(chunkPool.getEmptyCount() <= tunables.maxEmptyChunkCount());
-    MOZ_ASSERT_IF(shrinkBuffers, chunkPool.getEmptyCount() <= tunables.minEmptyChunkCount());
-    MOZ_ASSERT_IF(releaseAll, chunkPool.getEmptyCount() == 0);
+    MOZ_ASSERT(emptyChunks.count() <= tunables.maxEmptyChunkCount());
+    MOZ_ASSERT_IF(shrinkBuffers, emptyChunks.count() <= tunables.minEmptyChunkCount());
+    MOZ_ASSERT_IF(releaseAll, emptyChunks.count() == 0);
     return freeList;
 }
 
 void
 GCRuntime::freeChunkList(Chunk *chunkListHead)
 {
     while (Chunk *chunk = chunkListHead) {
         MOZ_ASSERT(!chunk->info.numArenasFreeCommitted);
@@ -1032,29 +1032,29 @@ Chunk::releaseArena(ArenaHeader *aheader
 }
 
 void
 GCRuntime::moveChunkToFreePool(Chunk *chunk)
 {
     MOZ_ASSERT(chunk->unused());
     MOZ_ASSERT(chunkSet.has(chunk));
     chunkSet.remove(chunk);
-    chunkPool.put(chunk);
+    emptyChunks.put(chunk);
 }
 
 inline bool
 GCRuntime::wantBackgroundAllocation() const
 {
     /*
      * To minimize memory waste we do not want to run the background chunk
      * allocation if we have empty chunks or when the runtime needs just few
      * of them.
      */
     return helperState.canBackgroundAllocate() &&
-           chunkPool.getEmptyCount() < tunables.minEmptyChunkCount() &&
+           emptyChunks.count() < tunables.minEmptyChunkCount() &&
            chunkSet.count() >= 4;
 }
 
 class js::gc::AutoMaybeStartBackgroundAllocation
 {
   private:
     JSRuntime *runtime;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
@@ -1083,17 +1083,17 @@ class js::gc::AutoMaybeStartBackgroundAl
 Chunk *
 GCRuntime::pickChunk(Zone *zone, AutoMaybeStartBackgroundAllocation &maybeStartBackgroundAllocation)
 {
     Chunk **listHeadp = getAvailableChunkList(zone);
     Chunk *chunk = *listHeadp;
     if (chunk)
         return chunk;
 
-    chunk = chunkPool.get(rt);
+    chunk = emptyChunks.get(rt);
     if (!chunk) {
         chunk = Chunk::allocate(rt);
         if (!chunk)
             return nullptr;
         MOZ_ASSERT(chunk->info.numArenasFreeCommitted == 0);
     }
 
     MOZ_ASSERT(chunk->unused());
@@ -1507,19 +1507,19 @@ GCRuntime::getParameter(JSGCParamKey key
         return uint32_t(tunables.gcMaxBytes());
       case JSGC_MAX_MALLOC_BYTES:
         return maxMallocBytes;
       case JSGC_BYTES:
         return uint32_t(usage.gcBytes());
       case JSGC_MODE:
         return uint32_t(mode);
       case JSGC_UNUSED_CHUNKS:
-        return uint32_t(chunkPool.getEmptyCount());
+        return uint32_t(emptyChunks.count());
       case JSGC_TOTAL_CHUNKS:
-        return uint32_t(chunkSet.count() + chunkPool.getEmptyCount());
+        return uint32_t(chunkSet.count() + emptyChunks.count());
       case JSGC_SLICE_TIME_BUDGET:
         return uint32_t(sliceBudget > 0 ? sliceBudget / PRMJ_USEC_PER_MSEC : 0);
       case JSGC_MARK_STACK_LIMIT:
         return marker.maxCapacity();
       case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
         return tunables.highFrequencyThresholdUsec();
       case JSGC_HIGH_FREQUENCY_LOW_LIMIT:
         return tunables.highFrequencyLowLimitBytes() / 1024 / 1024;
@@ -2193,25 +2193,26 @@ RelocateCell(Zone *zone, TenuredCell *sr
     // Copy source cell contents to destination.
     memcpy(dst, src, thingSize);
 
     if (thingKind <= FINALIZE_OBJECT_LAST) {
         JSObject *srcObj = static_cast<JSObject *>(static_cast<Cell *>(src));
         JSObject *dstObj = static_cast<JSObject *>(static_cast<Cell *>(dst));
 
         // Fixup the pointer to inline object elements if necessary.
-        if (srcObj->hasFixedElements())
-            dstObj->setFixedElements();
+        if (srcObj->isNative() && srcObj->as<NativeObject>().hasFixedElements())
+            dstObj->as<NativeObject>().setFixedElements();
 
         // Call object moved hook if present.
         if (JSObjectMovedOp op = srcObj->getClass()->ext.objectMovedOp)
             op(dstObj, srcObj);
 
         MOZ_ASSERT_IF(dstObj->isNative(),
-                      !PtrIsInRange((const Value*)dstObj->getDenseElements(), src, thingSize));
+                      !PtrIsInRange((const Value*)dstObj->as<NativeObject>().getDenseElements(),
+                                    src, thingSize));
     }
 
     // Copy the mark bits.
     dst->copyMarkBitsFrom(src);
 
     // Mark source cell as forwarded and leave a pointer to the destination.
     RelocationOverlay* overlay = RelocationOverlay::fromCell(src);
     overlay->forwardTo(dst);
@@ -3249,17 +3250,19 @@ GCHelperState::startBackgroundThread(Sta
     HelperThreadState().notifyAll(GlobalHelperThreadState::PRODUCER);
 }
 
 void
 GCHelperState::waitForBackgroundThread()
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
 
+#ifdef DEBUG
     rt->gc.lockOwner = nullptr;
+#endif
     PR_WaitCondVar(done, PR_INTERVAL_NO_TIMEOUT);
 #ifdef DEBUG
     rt->gc.lockOwner = PR_GetCurrentThread();
 #endif
 }
 
 void
 GCHelperState::work()
@@ -3294,17 +3297,17 @@ GCHelperState::work()
                 AutoUnlockGC unlock(rt);
                 chunk = Chunk::allocate(rt);
             }
 
             /* OOM stops the background allocation. */
             if (!chunk)
                 break;
             MOZ_ASSERT(chunk->info.numArenasFreeCommitted == 0);
-            rt->gc.chunkPool.put(chunk);
+            rt->gc.emptyChunks.put(chunk);
         } while (state() == ALLOCATING && rt->gc.wantBackgroundAllocation());
 
         MOZ_ASSERT(state() == ALLOCATING || state() == CANCEL_ALLOCATION);
         break;
       }
 
       case CANCEL_ALLOCATION:
         break;
@@ -4604,16 +4607,107 @@ GCRuntime::endMarkingZoneGroup()
     for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
         MOZ_ASSERT(zone->isGCMarkingGray());
         zone->setGCState(Zone::Mark);
     }
     MOZ_ASSERT(marker.isDrained());
     marker.setMarkColorBlack();
 }
 
+#define MAKE_GC_PARALLEL_TASK(name) \
+    class name : public GCParallelTask {\
+        JSRuntime *runtime;\
+        virtual void run() MOZ_OVERRIDE;\
+      public:\
+        name (JSRuntime *rt) : runtime(rt) {}\
+    }
+MAKE_GC_PARALLEL_TASK(SweepAtomsTask);
+MAKE_GC_PARALLEL_TASK(SweepInnerViewsTask);
+MAKE_GC_PARALLEL_TASK(SweepCCWrappersTask);
+MAKE_GC_PARALLEL_TASK(SweepBaseShapesTask);
+MAKE_GC_PARALLEL_TASK(SweepInitialShapesTask);
+MAKE_GC_PARALLEL_TASK(SweepTypeObjectsTask);
+MAKE_GC_PARALLEL_TASK(SweepRegExpsTask);
+MAKE_GC_PARALLEL_TASK(SweepMiscTask);
+
+/* virtual */ void
+SweepAtomsTask::run()
+{
+    runtime->sweepAtoms();
+}
+
+/* virtual */ void
+SweepInnerViewsTask::run()
+{
+    for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
+        c->sweepInnerViews();
+}
+
+/* virtual */ void
+SweepCCWrappersTask::run()
+{
+    for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
+        c->sweepCrossCompartmentWrappers();
+}
+
+/* virtual */ void
+SweepBaseShapesTask::run()
+{
+    for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
+        c->sweepBaseShapeTable();
+}
+
+/* virtual */ void
+SweepInitialShapesTask::run()
+{
+    for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
+        c->sweepInitialShapeTable();
+}
+
+/* virtual */ void
+SweepTypeObjectsTask::run()
+{
+    for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
+        c->sweepTypeObjectTables();
+}
+
+/* virtual */ void
+SweepRegExpsTask::run()
+{
+    for (GCCompartmentGroupIter c(runtime); !c.done(); c.next())
+        c->sweepRegExps();
+}
+
+/* virtual */ void
+SweepMiscTask::run()
+{
+    for (GCCompartmentGroupIter c(runtime); !c.done(); c.next()) {
+        c->sweepCallsiteClones();
+        c->sweepSavedStacks();
+        c->sweepSelfHostingScriptSource();
+        c->sweepNativeIterators();
+    }
+}
+
+void
+GCRuntime::startTask(GCParallelTask &task, gcstats::Phase phase)
+{
+    if (!task.startWithLockHeld()) {
+        gcstats::AutoPhase ap(stats, phase);
+        task.runFromMainThread(rt);
+    }
+}
+
+void
+GCRuntime::joinTask(GCParallelTask &task, gcstats::Phase phase)
+{
+    gcstats::AutoPhase ap(stats, task, phase);
+    task.joinWithLockHeld();
+}
+
 void
 GCRuntime::beginSweepingZoneGroup()
 {
     /*
      * Begin sweeping the group of zones in gcCurrentZoneGroup,
      * performing actions that must be done before yielding to caller.
      */
 
@@ -4633,85 +4727,61 @@ GCRuntime::beginSweepingZoneGroup()
             rt->sweepZoneCallback(zone);
 
         zone->gcLastZoneGroupIndex = zoneGroupIndex;
     }
 
     validateIncrementalMarking();
 
     FreeOp fop(rt);
+    SweepAtomsTask sweepAtomsTask(rt);
+    SweepInnerViewsTask sweepInnerViewsTask(rt);
+    SweepCCWrappersTask sweepCCWrappersTask(rt);
+    SweepBaseShapesTask sweepBaseShapesTask(rt);
+    SweepInitialShapesTask sweepInitialShapesTask(rt);
+    SweepTypeObjectsTask sweepTypeObjectsTask(rt);
+    SweepRegExpsTask sweepRegExpsTask(rt);
+    SweepMiscTask sweepMiscTask(rt);
 
     {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_FINALIZE_START);
         callFinalizeCallbacks(&fop, JSFINALIZE_GROUP_START);
         callWeakPointerCallbacks();
     }
 
     if (sweepingAtoms) {
-        gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_ATOMS);
-        rt->sweepAtoms();
+        AutoLockHelperThreadState helperLock;
+        startTask(sweepAtomsTask, gcstats::PHASE_SWEEP_ATOMS);
     }
 
     {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS);
         gcstats::AutoSCC scc(stats, zoneGroupIndex);
 
         {
-            gcstats::AutoPhase apiv(stats, gcstats::PHASE_SWEEP_INNER_VIEWS);
-            for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
-                c->sweepInnerViews();
-            }
+            AutoLockHelperThreadState helperLock;
+            startTask(sweepInnerViewsTask, gcstats::PHASE_SWEEP_INNER_VIEWS);
+            startTask(sweepCCWrappersTask, gcstats::PHASE_SWEEP_CC_WRAPPER);
+            startTask(sweepBaseShapesTask, gcstats::PHASE_SWEEP_BASE_SHAPE);
+            startTask(sweepInitialShapesTask, gcstats::PHASE_SWEEP_INITIAL_SHAPE);
+            startTask(sweepTypeObjectsTask, gcstats::PHASE_SWEEP_TYPE_OBJECT);
+            startTask(sweepRegExpsTask, gcstats::PHASE_SWEEP_REGEXP);
+            startTask(sweepMiscTask, gcstats::PHASE_SWEEP_MISC);
         }
 
+        // The remainder of the of the tasks run in parallel on the main
+        // thread until we join, below.
         {
-            gcstats::AutoPhase apccw(stats, gcstats::PHASE_SWEEP_CC_WRAPPER);
-            for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
-                c->sweepCrossCompartmentWrappers();
-            }
-        }
-
-        {
-            gcstats::AutoPhase apbs(stats, gcstats::PHASE_SWEEP_BASE_SHAPE);
-            for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
-                c->sweepBaseShapeTable();
-            }
-        }
-
-        {
-            gcstats::AutoPhase apis(stats, gcstats::PHASE_SWEEP_INITIAL_SHAPE);
+            gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_MISC);
+
             for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
-                c->sweepInitialShapeTable();
-            }
-        }
-
-        {
-            gcstats::AutoPhase apto(stats, gcstats::PHASE_SWEEP_TYPE_OBJECT);
-            for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
-                c->sweepTypeObjectTables();
-            }
-        }
-
-        {
-            gcstats::AutoPhase apre(stats, gcstats::PHASE_SWEEP_REGEXP);
-            for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
-                c->sweepRegExps();
-            }
-        }
-
-        {
-            gcstats::AutoPhase apmisc(stats, gcstats::PHASE_SWEEP_MISC);
-            for (GCCompartmentGroupIter c(rt); !c.done(); c.next()) {
-                c->sweepCallsiteClones();
-                c->sweepSavedStacks();
                 c->sweepGlobalObject(&fop);
-                c->sweepSelfHostingScriptSource();
                 c->sweepDebugScopes();
                 c->sweepJitCompartment(&fop);
                 c->sweepWeakMaps();
-                c->sweepNativeIterators();
             }
 
             // Bug 1071218: the following two methods have not yet been
             // refactored to work on a single zone-group at once.
 
             // Collect watch points associated with unreachable objects.
             WatchpointMap::sweepAll(rt);
 
@@ -4741,16 +4811,36 @@ GCRuntime::beginSweepingZoneGroup()
         }
     }
 
     if (sweepingAtoms) {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_SYMBOL_REGISTRY);
         rt->symbolRegistry().sweep();
     }
 
+    // Rejoin our off-main-thread tasks.
+    if (sweepingAtoms) {
+        AutoLockHelperThreadState helperLock;
+        joinTask(sweepAtomsTask, gcstats::PHASE_SWEEP_ATOMS);
+    }
+
+    {
+        gcstats::AutoPhase ap(stats, gcstats::PHASE_SWEEP_COMPARTMENTS);
+        gcstats::AutoSCC scc(stats, zoneGroupIndex);
+
+        AutoLockHelperThreadState helperLock;
+        joinTask(sweepInnerViewsTask, gcstats::PHASE_SWEEP_INNER_VIEWS);
+        joinTask(sweepCCWrappersTask, gcstats::PHASE_SWEEP_CC_WRAPPER);
+        joinTask(sweepBaseShapesTask, gcstats::PHASE_SWEEP_BASE_SHAPE);
+        joinTask(sweepInitialShapesTask, gcstats::PHASE_SWEEP_INITIAL_SHAPE);
+        joinTask(sweepTypeObjectsTask, gcstats::PHASE_SWEEP_TYPE_OBJECT);
+        joinTask(sweepRegExpsTask, gcstats::PHASE_SWEEP_REGEXP);
+        joinTask(sweepMiscTask, gcstats::PHASE_SWEEP_MISC);
+    }
+
     /*
      * Queue all GC things in all zones for sweeping, either in the
      * foreground or on the background thread.
      *
      * Note that order is important here for the background case.
      *
      * Objects are finalized immediately but this may change in the future.
      */
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1065,16 +1065,57 @@ class GCHelperState
     }
 
     bool shouldShrink() const {
         MOZ_ASSERT(isBackgroundSweeping());
         return shrinkFlag;
     }
 };
 
+// A generic task used to dispatch work to the helper thread system.
+// Users should derive from GCParallelTask add what data they need and
+// override |run|.
+class GCParallelTask
+{
+    // The state of the parallel computation.
+    enum TaskState {
+        NotStarted,
+        Dispatched,
+        Finished,
+    } state;
+
+    // Amount of time this task took to execute.
+    uint64_t duration_;
+
+  protected:
+    virtual void run() = 0;
+
+  public:
+    GCParallelTask() : state(NotStarted), duration_(0) {}
+
+    int64_t duration() const { return duration_; }
+
+    // The simple interface to a parallel task works exactly like pthreads.
+    bool start();
+    void join();
+
+    // If multiple tasks are to be started or joined at once, it is more
+    // efficient to take the helper thread lock once and use these methods.
+    bool startWithLockHeld();
+    void joinWithLockHeld();
+
+    // Instead of dispatching to a helper, run the task on the main thread.
+    void runFromMainThread(JSRuntime *rt);
+
+    // This should be friended to HelperThread, but cannot be because it
+    // would introduce several circular dependencies.
+  public:
+    void runFromHelperThread();
+};
+
 struct GCChunkHasher {
     typedef gc::Chunk *Lookup;
 
     /*
      * Strip zeros for better distribution after multiplying by the golden
      * ratio.
      */
     static HashNumber hash(gc::Chunk *chunk) {
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -4779,27 +4779,27 @@ TypeCompartment::sweep(FreeOp *fop)
                 e.rekeyFront(key);
         }
     }
 }
 
 void
 JSCompartment::sweepNewTypeObjectTable(TypeObjectWithNewScriptSet &table)
 {
-    MOZ_ASSERT(zone()->isCollecting());
+    MOZ_ASSERT(zone()->runtimeFromAnyThread()->isHeapCollecting());
     if (table.initialized()) {
         for (TypeObjectWithNewScriptSet::Enum e(table); !e.empty(); e.popFront()) {
             TypeObjectWithNewScriptEntry entry = e.front();
-            if (IsTypeObjectAboutToBeFinalized(entry.object.unsafeGet()) ||
-                (entry.newFunction && IsObjectAboutToBeFinalized(&entry.newFunction)))
+            if (IsTypeObjectAboutToBeFinalizedFromAnyThread(entry.object.unsafeGet()) ||
+                (entry.newFunction && IsObjectAboutToBeFinalizedFromAnyThread(&entry.newFunction)))
             {
                 e.removeFront();
             } else {
                 /* Any rekeying necessary is handled by fixupNewTypeObjectTable() below. */
-                MOZ_ASSERT(entry.object == e.front().object);
+                MOZ_ASSERT(entry.object.unbarrieredGet() == e.front().object.unbarrieredGet());
                 MOZ_ASSERT(entry.newFunction == e.front().newFunction);
             }
         }
     }
 }
 
 #ifdef JSGC_COMPACTING
 
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -30,16 +30,17 @@
 
 #include "vm/GlobalObject.h"
 #include "vm/NumericConversions.h"
 #include "vm/StringBuffer.h"
 
 #include "jsatominlines.h"
 
 #include "vm/NumberObject-inl.h"
+#include "vm/ObjectImpl-inl.h"
 #include "vm/String-inl.h"
 
 using namespace js;
 using namespace js::types;
 
 using mozilla::Abs;
 using mozilla::ArrayLength;
 using mozilla::MinNumberValue;
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -6,17 +6,16 @@
 
 /*
  * JS object implementation.
  */
 
 #include "jsobjinlines.h"
 
 #include "mozilla/ArrayUtils.h"
-#include "mozilla/CheckedInt.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/TemplateLib.h"
 
 #include <string.h>
 
 #include "jsapi.h"
 #include "jsarray.h"
@@ -68,20 +67,16 @@
 #include "vm/StringObject-inl.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 using mozilla::DebugOnly;
 using mozilla::Maybe;
-using mozilla::RoundUpPow2;
-
-JS_STATIC_ASSERT(int32_t((NativeObject::NELEMENTS_LIMIT - 1) * sizeof(Value)) ==
-                 int64_t((NativeObject::NELEMENTS_LIMIT - 1) * sizeof(Value)));
 
 static JSObject *
 CreateObjectConstructor(JSContext *cx, JSProtoKey key)
 {
     Rooted<GlobalObject*> self(cx, cx->global());
     if (!GlobalObject::ensureConstructor(cx, self, JSProto_Function))
         return nullptr;
 
@@ -651,59 +646,16 @@ Reject(JSContext *cx, HandleId id, unsig
     if (throwError)
         return Throw(cx, id, errorNumber);
 
     *rval = false;
     return true;
 }
 
 static unsigned
-ApplyAttributes(unsigned attrs, bool enumerable, bool writable, bool configurable)
-{
-    /*
-     * Respect the fact that some callers may want to preserve existing attributes as much as
-     * possible, or install defaults otherwise.
-     */
-    if (attrs & JSPROP_IGNORE_ENUMERATE) {
-        attrs &= ~JSPROP_IGNORE_ENUMERATE;
-        if (enumerable)
-            attrs |= JSPROP_ENUMERATE;
-        else
-            attrs &= ~JSPROP_ENUMERATE;
-    }
-    if (attrs & JSPROP_IGNORE_READONLY) {
-        attrs &= ~JSPROP_IGNORE_READONLY;
-        // Only update the writability if it's relevant
-        if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
-            if (!writable)
-                attrs |= JSPROP_READONLY;
-            else
-                attrs &= ~JSPROP_READONLY;
-        }
-    }
-    if (attrs & JSPROP_IGNORE_PERMANENT) {
-        attrs &= ~JSPROP_IGNORE_PERMANENT;
-        if (!configurable)
-            attrs |= JSPROP_PERMANENT;
-        else
-            attrs &= ~JSPROP_PERMANENT;
-    }
-    return attrs;
-}
-
-static unsigned
-ApplyOrDefaultAttributes(unsigned attrs, const Shape *shape = nullptr)
-{
-    bool enumerable = shape ? shape->enumerable() : false;
-    bool writable = shape ? shape->writable() : false;
-    bool configurable = shape ? shape->configurable() : false;
-    return ApplyAttributes(attrs, enumerable, writable, configurable);
-}
-
-static unsigned
 ApplyOrDefaultAttributes(unsigned attrs, Handle<PropertyDescriptor> desc)
 {
     bool present = !!desc.object();
     bool enumerable = present ? desc.isEnumerable() : false;
     bool writable = present ? !desc.isReadonly() : false;
     bool configurable = present ? !desc.isPermanent() : false;
     return ApplyAttributes(attrs, enumerable, writable, configurable);
 }
@@ -1860,62 +1812,16 @@ js::CreateThisForFunction(JSContext *cx,
         TypeScript::SetThis(cx, calleeScript, types::Type::ObjectType(nobj));
 
         return nobj;
     }
 
     return obj;
 }
 
-/*
- * Given pc pointing after a property accessing bytecode, return true if the
- * access is "object-detecting" in the sense used by web scripts, e.g., when
- * checking whether document.all is defined.
- */
-static bool
-Detecting(JSContext *cx, JSScript *script, jsbytecode *pc)
-{
-    MOZ_ASSERT(script->containsPC(pc));
-
-    /* General case: a branch or equality op follows the access. */
-    JSOp op = JSOp(*pc);
-    if (js_CodeSpec[op].format & JOF_DETECTING)
-        return true;
-
-    jsbytecode *endpc = script->codeEnd();
-
-    if (op == JSOP_NULL) {
-        /*
-         * Special case #1: handle (document.all == null).  Don't sweat
-         * about JS1.2's revision of the equality operators here.
-         */
-        if (++pc < endpc) {
-            op = JSOp(*pc);
-            return op == JSOP_EQ || op == JSOP_NE;
-        }
-        return false;
-    }
-
-    if (op == JSOP_GETGNAME || op == JSOP_NAME) {
-        /*
-         * Special case #2: handle (document.all == undefined).  Don't worry
-         * about a local variable named |undefined| shadowing the immutable
-         * global binding...because, really?
-         */
-        JSAtom *atom = script->getAtom(GET_UINT32_INDEX(pc));
-        if (atom == cx->names().undefined &&
-            (pc += js_CodeSpec[op].length) < endpc) {
-            op = JSOp(*pc);
-            return op == JSOP_EQ || op == JSOP_NE || op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
-        }
-    }
-
-    return false;
-}
-
 /* static */ bool
 JSObject::nonNativeSetProperty(JSContext *cx, HandleObject obj,
                                HandleId id, MutableHandleValue vp, bool strict)
 {
     if (MOZ_UNLIKELY(obj->watched())) {
         WatchpointMap *wpmap = cx->compartment()->watchpointMap;
         if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp))
             return false;
@@ -2936,673 +2842,16 @@ js_InitClass(JSContext *cx, HandleObject
     {
         return nullptr;
     }
 
     return DefineConstructorAndPrototype(cx, obj, key, atom, protoProto, clasp, constructor, nargs,
                                          ps, fs, static_ps, static_fs, ctorp, ctorKind);
 }
 
-/* static */ inline bool
-NativeObject::updateSlotsForSpan(ThreadSafeContext *cx,
-                                 HandleNativeObject obj, size_t oldSpan, size_t newSpan)
-{
-    MOZ_ASSERT(cx->isThreadLocal(obj));
-    MOZ_ASSERT(oldSpan != newSpan);
-
-    size_t oldCount = dynamicSlotsCount(obj->numFixedSlots(), oldSpan, obj->getClass());
-    size_t newCount = dynamicSlotsCount(obj->numFixedSlots(), newSpan, obj->getClass());
-
-    if (oldSpan < newSpan) {
-        if (oldCount < newCount && !growSlots(cx, obj, oldCount, newCount))
-            return false;
-
-        if (newSpan == oldSpan + 1)
-            obj->initSlotUnchecked(oldSpan, UndefinedValue());
-        else
-            obj->initializeSlotRange(oldSpan, newSpan - oldSpan);
-    } else {
-        /* Trigger write barriers on the old slots before reallocating. */
-        obj->prepareSlotRangeForOverwrite(newSpan, oldSpan);
-        obj->invalidateSlotRange(newSpan, oldSpan - newSpan);
-
-        if (oldCount > newCount)
-            shrinkSlots(cx, obj, oldCount, newCount);
-    }
-
-    return true;
-}
-
-/* static */ bool
-NativeObject::setLastProperty(ThreadSafeContext *cx, HandleNativeObject obj, HandleShape shape)
-{
-    MOZ_ASSERT(cx->isThreadLocal(obj));
-    MOZ_ASSERT(!obj->inDictionaryMode());
-    MOZ_ASSERT(!shape->inDictionary());
-    MOZ_ASSERT(shape->compartment() == obj->compartment());
-    MOZ_ASSERT(shape->numFixedSlots() == obj->numFixedSlots());
-
-    size_t oldSpan = obj->lastProperty()->slotSpan();
-    size_t newSpan = shape->slotSpan();
-
-    if (oldSpan == newSpan) {
-        obj->shape_ = shape;
-        return true;
-    }
-
-    if (!updateSlotsForSpan(cx, obj, oldSpan, newSpan))
-        return false;
-
-    obj->shape_ = shape;
-    return true;
-}
-
-void
-NativeObject::setLastPropertyShrinkFixedSlots(Shape *shape)
-{
-    MOZ_ASSERT(!inDictionaryMode());
-    MOZ_ASSERT(!shape->inDictionary());
-    MOZ_ASSERT(shape->compartment() == compartment());
-    MOZ_ASSERT(lastProperty()->slotSpan() == shape->slotSpan());
-
-    DebugOnly<size_t> oldFixed = numFixedSlots();
-    DebugOnly<size_t> newFixed = shape->numFixedSlots();
-    MOZ_ASSERT(newFixed < oldFixed);
-    MOZ_ASSERT(shape->slotSpan() <= oldFixed);
-    MOZ_ASSERT(shape->slotSpan() <= newFixed);
-    MOZ_ASSERT(dynamicSlotsCount(oldFixed, shape->slotSpan(), getClass()) == 0);
-    MOZ_ASSERT(dynamicSlotsCount(newFixed, shape->slotSpan(), getClass()) == 0);
-
-    shape_ = shape;
-}
-
-/* static */ bool
-NativeObject::setSlotSpan(ThreadSafeContext *cx, HandleNativeObject obj, uint32_t span)
-{
-    MOZ_ASSERT(cx->isThreadLocal(obj));
-    MOZ_ASSERT(obj->inDictionaryMode());
-
-    size_t oldSpan = obj->lastProperty()->base()->slotSpan();
-    if (oldSpan == span)
-        return true;
-
-    if (!updateSlotsForSpan(cx, obj, oldSpan, span))
-        return false;
-
-    obj->lastProperty()->base()->setSlotSpan(span);
-    return true;
-}
-
-// This will not run the garbage collector.  If a nursery cannot accomodate the slot array
-// an attempt will be made to place the array in the tenured area.
-static HeapSlot *
-AllocateSlots(ThreadSafeContext *cx, JSObject *obj, uint32_t nslots)
-{
-#ifdef JSGC_GENERATIONAL
-    if (cx->isJSContext())
-        return cx->asJSContext()->runtime()->gc.nursery.allocateSlots(obj, nslots);
-#endif
-#ifdef JSGC_FJGENERATIONAL
-    if (cx->isForkJoinContext())
-        return cx->asForkJoinContext()->nursery().allocateSlots(obj, nslots);
-#endif
-    return obj->zone()->pod_malloc<HeapSlot>(nslots);
-}
-
-// This will not run the garbage collector.  If a nursery cannot accomodate the slot array
-// an attempt will be made to place the array in the tenured area.
-//
-// If this returns null then the old slots will be left alone.
-static HeapSlot *
-ReallocateSlots(ThreadSafeContext *cx, JSObject *obj, HeapSlot *oldSlots,
-                uint32_t oldCount, uint32_t newCount)
-{
-#ifdef JSGC_GENERATIONAL
-    if (cx->isJSContext()) {
-        return cx->asJSContext()->runtime()->gc.nursery.reallocateSlots(obj, oldSlots,
-                                                                        oldCount, newCount);
-    }
-#endif
-#ifdef JSGC_FJGENERATIONAL
-    if (cx->isForkJoinContext()) {
-        return cx->asForkJoinContext()->nursery().reallocateSlots(obj, oldSlots,
-                                                                  oldCount, newCount);
-    }
-#endif
-    return obj->zone()->pod_realloc<HeapSlot>(oldSlots, oldCount, newCount);
-}
-
-/* static */ bool
-NativeObject::growSlots(ThreadSafeContext *cx, HandleNativeObject obj, uint32_t oldCount, uint32_t newCount)
-{
-    MOZ_ASSERT(cx->isThreadLocal(obj));
-    MOZ_ASSERT(newCount > oldCount);
-    MOZ_ASSERT_IF(!obj->is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
-
-    /*
-     * Slot capacities are determined by the span of allocated objects. Due to
-     * the limited number of bits to store shape slots, object growth is
-     * throttled well before the slot capacity can overflow.
-     */
-    MOZ_ASSERT(newCount < NELEMENTS_LIMIT);
-
-    if (!oldCount) {
-        obj->slots = AllocateSlots(cx, obj, newCount);
-        if (!obj->slots)
-            return false;
-        Debug_SetSlotRangeToCrashOnTouch(obj->slots, newCount);
-        return true;
-    }
-
-    HeapSlot *newslots = ReallocateSlots(cx, obj, obj->slots, oldCount, newCount);
-    if (!newslots)
-        return false;  /* Leave slots at its old size. */
-
-    obj->slots = newslots;
-
-    Debug_SetSlotRangeToCrashOnTouch(obj->slots + oldCount, newCount - oldCount);
-
-    return true;
-}
-
-static void
-FreeSlots(ThreadSafeContext *cx, HeapSlot *slots)
-{
-#ifdef JSGC_GENERATIONAL
-    // Note: threads without a JSContext do not have access to GGC nursery allocated things.
-    if (cx->isJSContext())
-        return cx->asJSContext()->runtime()->gc.nursery.freeSlots(slots);
-#endif
-#ifdef JSGC_FJGENERATIONAL
-    if (cx->isForkJoinContext())
-        return cx->asForkJoinContext()->nursery().freeSlots(slots);
-#endif
-    js_free(slots);
-}
-
-/* static */ void
-NativeObject::shrinkSlots(ThreadSafeContext *cx, HandleNativeObject obj,
-                          uint32_t oldCount, uint32_t newCount)
-{
-    MOZ_ASSERT(cx->isThreadLocal(obj));
-    MOZ_ASSERT(newCount < oldCount);
-
-    if (newCount == 0) {
-        FreeSlots(cx, obj->slots);
-        obj->slots = nullptr;
-        return;
-    }
-
-    MOZ_ASSERT_IF(!obj->is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
-
-    HeapSlot *newslots = ReallocateSlots(cx, obj, obj->slots, oldCount, newCount);
-    if (!newslots)
-        return;  /* Leave slots at its old size. */
-
-    obj->slots = newslots;
-}
-
-/* static */ bool
-NativeObject::sparsifyDenseElement(ExclusiveContext *cx, HandleNativeObject obj, uint32_t index)
-{
-    if (!obj->maybeCopyElementsForWrite(cx))
-        return false;
-
-    RootedValue value(cx, obj->getDenseElement(index));
-    MOZ_ASSERT(!value.isMagic(JS_ELEMENTS_HOLE));
-
-    removeDenseElementForSparseIndex(cx, obj, index);
-
-    uint32_t slot = obj->slotSpan();
-    if (!obj->addDataProperty(cx, INT_TO_JSID(index), slot, JSPROP_ENUMERATE)) {
-        obj->setDenseElement(index, value);
-        return false;
-    }
-
-    MOZ_ASSERT(slot == obj->slotSpan() - 1);
-    obj->initSlot(slot, value);
-
-    return true;
-}
-
-/* static */ bool
-NativeObject::sparsifyDenseElements(js::ExclusiveContext *cx, HandleNativeObject obj)
-{
-    if (!obj->maybeCopyElementsForWrite(cx))
-        return false;
-
-    uint32_t initialized = obj->getDenseInitializedLength();
-
-    /* Create new properties with the value of non-hole dense elements. */
-    for (uint32_t i = 0; i < initialized; i++) {
-        if (obj->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE))
-            continue;
-
-        if (!sparsifyDenseElement(cx, obj, i))
-            return false;
-    }
-
-    if (initialized)
-        obj->setDenseInitializedLength(0);
-
-    /*
-     * Reduce storage for dense elements which are now holes. Explicitly mark
-     * the elements capacity as zero, so that any attempts to add dense
-     * elements will be caught in ensureDenseElements.
-     */
-    if (obj->getDenseCapacity()) {
-        obj->shrinkElements(cx, 0);
-        obj->getElementsHeader()->capacity = 0;
-    }
-
-    return true;
-}
-
-bool
-NativeObject::willBeSparseElements(uint32_t requiredCapacity, uint32_t newElementsHint)
-{
-    MOZ_ASSERT(isNative());
-    MOZ_ASSERT(requiredCapacity > MIN_SPARSE_INDEX);
-
-    uint32_t cap = getDenseCapacity();
-    MOZ_ASSERT(requiredCapacity >= cap);
-
-    if (requiredCapacity >= NELEMENTS_LIMIT)
-        return true;
-
-    uint32_t minimalDenseCount = requiredCapacity / SPARSE_DENSITY_RATIO;
-    if (newElementsHint >= minimalDenseCount)
-        return false;
-    minimalDenseCount -= newElementsHint;
-
-    if (minimalDenseCount > cap)
-        return true;
-
-    uint32_t len = getDenseInitializedLength();
-    const Value *elems = getDenseElements();
-    for (uint32_t i = 0; i < len; i++) {
-        if (!elems[i].isMagic(JS_ELEMENTS_HOLE) && !--minimalDenseCount)
-            return false;
-    }
-    return true;
-}
-
-/* static */ NativeObject::EnsureDenseResult
-NativeObject::maybeDensifySparseElements(js::ExclusiveContext *cx, HandleNativeObject obj)
-{
-    /*
-     * Wait until after the object goes into dictionary mode, which must happen
-     * when sparsely packing any array with more than MIN_SPARSE_INDEX elements
-     * (see PropertyTree::MAX_HEIGHT).
-     */
-    if (!obj->inDictionaryMode())
-        return ED_SPARSE;
-
-    /*
-     * Only measure the number of indexed properties every log(n) times when
-     * populating the object.
-     */
-    uint32_t slotSpan = obj->slotSpan();
-    if (slotSpan != RoundUpPow2(slotSpan))
-        return ED_SPARSE;
-
-    /* Watch for conditions under which an object's elements cannot be dense. */
-    if (!obj->nonProxyIsExtensible() || obj->watched())
-        return ED_SPARSE;
-
-    /*
-     * The indexes in the object need to be sufficiently dense before they can
-     * be converted to dense mode.
-     */
-    uint32_t numDenseElements = 0;
-    uint32_t newInitializedLength = 0;
-
-    RootedShape shape(cx, obj->lastProperty());
-    while (!shape->isEmptyShape()) {
-        uint32_t index;
-        if (js_IdIsIndex(shape->propid(), &index)) {
-            if (shape->attributes() == JSPROP_ENUMERATE &&
-                shape->hasDefaultGetter() &&
-                shape->hasDefaultSetter())
-            {
-                numDenseElements++;
-                newInitializedLength = Max(newInitializedLength, index + 1);
-            } else {
-                /*
-                 * For simplicity, only densify the object if all indexed
-                 * properties can be converted to dense elements.
-                 */
-                return ED_SPARSE;
-            }
-        }
-        shape = shape->previous();
-    }
-
-    if (numDenseElements * SPARSE_DENSITY_RATIO < newInitializedLength)
-        return ED_SPARSE;
-
-    if (newInitializedLength >= NELEMENTS_LIMIT)
-        return ED_SPARSE;
-
-    /*
-     * This object meets all necessary restrictions, convert all indexed
-     * properties into dense elements.
-     */
-
-    if (!obj->maybeCopyElementsForWrite(cx))
-        return ED_FAILED;
-
-    if (newInitializedLength > obj->getDenseCapacity()) {
-        if (!obj->growElements(cx, newInitializedLength))
-            return ED_FAILED;
-    }
-
-    obj->ensureDenseInitializedLength(cx, newInitializedLength, 0);
-
-    RootedValue value(cx);
-
-    shape = obj->lastProperty();
-    while (!shape->isEmptyShape()) {
-        jsid id = shape->propid();
-        uint32_t index;
-        if (js_IdIsIndex(id, &index)) {
-            value = obj->getSlot(shape->slot());
-
-            /*
-             * When removing a property from a dictionary, the specified
-             * property will be removed from the dictionary list and the
-             * last property will then be changed due to reshaping the object.
-             * Compute the next shape in the traverse, watching for such
-             * removals from the list.
-             */
-            if (shape != obj->lastProperty()) {
-                shape = shape->previous();
-                if (!obj->removeProperty(cx, id))
-                    return ED_FAILED;
-            } else {
-                if (!obj->removeProperty(cx, id))
-                    return ED_FAILED;
-                shape = obj->lastProperty();
-            }
-
-            obj->setDenseElement(index, value);
-        } else {
-            shape = shape->previous();
-        }
-    }
-
-    /*
-     * All indexed properties on the object are now dense, clear the indexed
-     * flag so that we will not start using sparse indexes again if we need
-     * to grow the object.
-     */
-    if (!obj->clearFlag(cx, BaseShape::INDEXED))
-        return ED_FAILED;
-
-    return ED_OK;
-}
-
-// This will not run the garbage collector.  If a nursery cannot accomodate the element array
-// an attempt will be made to place the array in the tenured area.
-static ObjectElements *
-AllocateElements(ThreadSafeContext *cx, JSObject *obj, uint32_t nelems)
-{
-#ifdef JSGC_GENERATIONAL
-    if (cx->isJSContext())
-        return cx->asJSContext()->runtime()->gc.nursery.allocateElements(obj, nelems);
-#endif
-#ifdef JSGC_FJGENERATIONAL
-    if (cx->isForkJoinContext())
-        return cx->asForkJoinContext()->nursery().allocateElements(obj, nelems);
-#endif
-
-    return reinterpret_cast<js::ObjectElements *>(obj->zone()->pod_malloc<HeapSlot>(nelems));
-}
-
-// This will not run the garbage collector.  If a nursery cannot accomodate the element array
-// an attempt will be made to place the array in the tenured area.
-static ObjectElements *
-ReallocateElements(ThreadSafeContext *cx, JSObject *obj, ObjectElements *oldHeader,
-                   uint32_t oldCount, uint32_t newCount)
-{
-#ifdef JSGC_GENERATIONAL
-    if (cx->isJSContext()) {
-        return cx->asJSContext()->runtime()->gc.nursery.reallocateElements(obj, oldHeader,
-                                                                           oldCount, newCount);
-    }
-#endif
-#ifdef JSGC_FJGENERATIONAL
-    if (cx->isForkJoinContext()) {
-        return cx->asForkJoinContext()->nursery().reallocateElements(obj, oldHeader,
-                                                                     oldCount, newCount);
-    }
-#endif
-
-    return reinterpret_cast<js::ObjectElements *>(
-            obj->zone()->pod_realloc<HeapSlot>(reinterpret_cast<HeapSlot *>(oldHeader),
-                                               oldCount, newCount));
-}
-
-// Round up |reqAllocated| to a good size. Up to 1 Mebi (i.e. 1,048,576) the
-// slot count is usually a power-of-two:
-//
-//   8, 16, 32, 64, ..., 256 Ki, 512 Ki, 1 Mi
-//
-// Beyond that, we use this formula:
-//
-//   count(n+1) = Math.ceil(count(n) * 1.125)
-//
-// where |count(n)| is the size of the nth bucket measured in MiSlots.
-//
-// These counts lets us add N elements to an array in amortized O(N) time.
-// Having the second class means that for bigger arrays the constant factor is
-// higher, but we waste less space.
-//
-// There is one exception to the above rule: for the power-of-two cases, if the
-// chosen capacity would be 2/3 or more of the array's length, the chosen
-// capacity is adjusted (up or down) to be equal to the array's length
-// (assuming length is at least as large as the required capacity). This avoids
-// the allocation of excess elements which are unlikely to be needed, either in
-// this resizing or a subsequent one. The 2/3 factor is chosen so that
-// exceptional resizings will at most triple the capacity, as opposed to the
-// usual doubling.
-//
-/* static */ uint32_t
-NativeObject::goodAllocated(uint32_t reqAllocated, uint32_t length = 0)
-{
-    static const uint32_t Mebi = 1024 * 1024;
-
-    // This table was generated with this JavaScript code and a small amount
-    // subsequent reformatting:
-    //
-    //   for (let n = 1, i = 0; i < 57; i++) {
-    //     print((n * 1024 * 1024) + ', ');
-    //     n = Math.ceil(n * 1.125);
-    //   }
-    //   print('0');
-    //
-    // The final element is a sentinel value.
-    static const uint32_t BigBuckets[] = {
-        1048576, 2097152, 3145728, 4194304, 5242880, 6291456, 7340032, 8388608,
-        9437184, 11534336, 13631488, 15728640, 17825792, 20971520, 24117248,
-        27262976, 31457280, 35651584, 40894464, 46137344, 52428800, 59768832,
-        68157440, 77594624, 88080384, 99614720, 112197632, 126877696,
-        143654912, 162529280, 183500800, 206569472, 232783872, 262144000,
-        295698432, 333447168, 375390208, 422576128, 476053504, 535822336,
-        602931200, 678428672, 763363328, 858783744, 966787072, 1088421888,
-        1224736768, 1377828864, 1550843904, 1744830464, 1962934272, 2208301056,
-        2485125120, 2796552192, 3146776576, 3541041152, 3984588800, 0
-    };
-
-    // This code relies very much on |goodAllocated| being a uint32_t.
-    uint32_t goodAllocated = reqAllocated;
-    if (goodAllocated < Mebi) {
-        goodAllocated = RoundUpPow2(goodAllocated);
-
-        // Look for the abovementioned exception.
-        uint32_t goodCapacity = goodAllocated - ObjectElements::VALUES_PER_HEADER;
-        uint32_t reqCapacity = reqAllocated - ObjectElements::VALUES_PER_HEADER;
-        if (length >= reqCapacity && goodCapacity > (length / 3) * 2)
-            goodAllocated = length + ObjectElements::VALUES_PER_HEADER;
-
-        if (goodAllocated < SLOT_CAPACITY_MIN)
-            goodAllocated = SLOT_CAPACITY_MIN;
-
-    } else {
-        uint32_t i = 0;
-        while (true) {
-            uint32_t b = BigBuckets[i++];
-            if (b >= goodAllocated) {
-                // Found the first bucket greater than or equal to
-                // |goodAllocated|.
-                goodAllocated = b;
-                break;
-            } else if (b == 0) {
-                // Hit the end; return the maximum possible goodAllocated.
-                goodAllocated = 0xffffffff;
-                break;
-            }
-        }
-    }
-
-    return goodAllocated;
-}
-
-bool
-NativeObject::growElements(ThreadSafeContext *cx, uint32_t reqCapacity)
-{
-    MOZ_ASSERT(nonProxyIsExtensible());
-    MOZ_ASSERT(canHaveNonEmptyElements());
-    if (denseElementsAreCopyOnWrite())
-        MOZ_CRASH();
-
-    uint32_t oldCapacity = getDenseCapacity();
-    MOZ_ASSERT(oldCapacity < reqCapacity);
-
-    using mozilla::CheckedInt;
-
-    CheckedInt<uint32_t> checkedOldAllocated =
-        CheckedInt<uint32_t>(oldCapacity) + ObjectElements::VALUES_PER_HEADER;
-    CheckedInt<uint32_t> checkedReqAllocated =
-        CheckedInt<uint32_t>(reqCapacity) + ObjectElements::VALUES_PER_HEADER;
-    if (!checkedOldAllocated.isValid() || !checkedReqAllocated.isValid())
-        return false;
-
-    uint32_t reqAllocated = checkedReqAllocated.value();
-    uint32_t oldAllocated = checkedOldAllocated.value();
-
-    uint32_t newAllocated;
-    if (is<ArrayObject>() && !as<ArrayObject>().lengthIsWritable()) {
-        MOZ_ASSERT(reqCapacity <= as<ArrayObject>().length());
-        // Preserve the |capacity <= length| invariant for arrays with
-        // non-writable length.  See also js::ArraySetLength which initially
-        // enforces this requirement.
-        newAllocated = reqAllocated;
-    } else {
-        newAllocated = goodAllocated(reqAllocated, getElementsHeader()->length);
-    }
-
-    uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER;
-    MOZ_ASSERT(newCapacity > oldCapacity && newCapacity >= reqCapacity);
-
-    // Don't let nelements get close to wrapping around uint32_t.
-    if (newCapacity >= NELEMENTS_LIMIT)
-        return false;
-
-    uint32_t initlen = getDenseInitializedLength();
-
-    ObjectElements *newheader;
-    if (hasDynamicElements()) {
-        newheader = ReallocateElements(cx, this, getElementsHeader(), oldAllocated, newAllocated);
-        if (!newheader)
-            return false;   // Leave elements at its old size.
-    } else {
-        newheader = AllocateElements(cx, this, newAllocated);
-        if (!newheader)
-            return false;   // Leave elements at its old size.
-        js_memcpy(newheader, getElementsHeader(),
-                  (ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value));
-    }
-
-    newheader->capacity = newCapacity;
-    elements = newheader->elements();
-
-    Debug_SetSlotRangeToCrashOnTouch(elements + initlen, newCapacity - initlen);
-
-    return true;
-}
-
-void
-NativeObject::shrinkElements(ThreadSafeContext *cx, uint32_t reqCapacity)
-{
-    MOZ_ASSERT(cx->isThreadLocal(this));
-    MOZ_ASSERT(canHaveNonEmptyElements());
-    if (denseElementsAreCopyOnWrite())
-        MOZ_CRASH();
-
-    if (!hasDynamicElements())
-        return;
-
-    uint32_t oldCapacity = getDenseCapacity();
-    MOZ_ASSERT(reqCapacity < oldCapacity);
-
-    uint32_t oldAllocated = oldCapacity + ObjectElements::VALUES_PER_HEADER;
-    uint32_t reqAllocated = reqCapacity + ObjectElements::VALUES_PER_HEADER;
-    uint32_t newAllocated = goodAllocated(reqAllocated);
-    if (newAllocated == oldAllocated)
-        return;  // Leave elements at its old size.
-
-    MOZ_ASSERT(newAllocated > ObjectElements::VALUES_PER_HEADER);
-    uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER;
-
-    ObjectElements *newheader = ReallocateElements(cx, this, getElementsHeader(),
-                                                   oldAllocated, newAllocated);
-    if (!newheader) {
-        cx->recoverFromOutOfMemory();
-        return;  // Leave elements at its old size.
-    }
-
-    newheader->capacity = newCapacity;
-    elements = newheader->elements();
-}
-
-/* static */ bool
-NativeObject::CopyElementsForWrite(ThreadSafeContext *cx, NativeObject *obj)
-{
-    MOZ_ASSERT(obj->denseElementsAreCopyOnWrite());
-
-    // The original owner of a COW elements array should never be modified.
-    MOZ_ASSERT(obj->getElementsHeader()->ownerObject() != obj);
-
-    uint32_t initlen = obj->getDenseInitializedLength();
-    uint32_t allocated = initlen + ObjectElements::VALUES_PER_HEADER;
-    uint32_t newAllocated = goodAllocated(allocated);
-
-    uint32_t newCapacity = newAllocated - ObjectElements::VALUES_PER_HEADER;
-
-    if (newCapacity >= NELEMENTS_LIMIT)
-        return false;
-
-    JSObject::writeBarrierPre(obj->getElementsHeader()->ownerObject());
-
-    ObjectElements *newheader = AllocateElements(cx, obj, newAllocated);
-    if (!newheader)
-        return false;
-    js_memcpy(newheader, obj->getElementsHeader(),
-              (ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value));
-
-    newheader->capacity = newCapacity;
-    newheader->clearCopyOnWrite();
-    obj->elements = newheader->elements();
-
-    Debug_SetSlotRangeToCrashOnTouch(obj->elements + initlen, newCapacity - initlen);
-
-    return true;
-}
-
 void
 JSObject::fixupAfterMovingGC()
 {
     /*
      * If this is a copy-on-write elements we may need to fix up both the
      * elements' pointer back to the owner object, and the elements pointer
      * itself if it points to inline elements in another object.
      */
@@ -3872,159 +3121,16 @@ JSObject::constructHook() const
         const js::ProxyObject &p = as<js::ProxyObject>();
         if (p.handler()->isConstructor(const_cast<JSObject*>(this)))
             return js::proxy_Construct;
     }
     return nullptr;
 }
 
 /* static */ bool
-NativeObject::allocSlot(ThreadSafeContext *cx, HandleNativeObject obj, uint32_t *slotp)
-{
-    MOZ_ASSERT(cx->isThreadLocal(obj));
-
-    uint32_t slot = obj->slotSpan();
-    MOZ_ASSERT(slot >= JSSLOT_FREE(obj->getClass()));
-
-    /*
-     * If this object is in dictionary mode, try to pull a free slot from the
-     * shape table's slot-number freelist.
-     */
-    if (obj->inDictionaryMode()) {
-        ShapeTable &table = obj->lastProperty()->table();
-        uint32_t last = table.freelist;
-        if (last != SHAPE_INVALID_SLOT) {
-#ifdef DEBUG
-            MOZ_ASSERT(last < slot);
-            uint32_t next = obj->getSlot(last).toPrivateUint32();
-            MOZ_ASSERT_IF(next != SHAPE_INVALID_SLOT, next < slot);
-#endif
-
-            *slotp = last;
-
-            const Value &vref = obj->getSlot(last);
-            table.freelist = vref.toPrivateUint32();
-            obj->setSlot(last, UndefinedValue());
-            return true;
-        }
-    }
-
-    if (slot >= SHAPE_MAXIMUM_SLOT) {
-        js_ReportOutOfMemory(cx);
-        return false;
-    }
-
-    *slotp = slot;
-
-    if (obj->inDictionaryMode() && !setSlotSpan(cx, obj, slot + 1))
-        return false;
-
-    return true;
-}
-
-void
-NativeObject::freeSlot(uint32_t slot)
-{
-    MOZ_ASSERT(slot < slotSpan());
-
-    if (inDictionaryMode()) {
-        uint32_t &last = lastProperty()->table().freelist;
-
-        /* Can't afford to check the whole freelist, but let's check the head. */
-        MOZ_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan() && last != slot);
-
-        /*
-         * Place all freed slots other than reserved slots (bug 595230) on the
-         * dictionary's free list.
-         */
-        if (JSSLOT_FREE(getClass()) <= slot) {
-            MOZ_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan());
-            setSlot(slot, PrivateUint32Value(last));
-            last = slot;
-            return;
-        }
-    }
-    setSlot(slot, UndefinedValue());
-}
-
-static bool
-PurgeProtoChain(ExclusiveContext *cx, JSObject *objArg, HandleId id)
-{
-    /* Root locally so we can re-assign. */
-    RootedObject obj(cx, objArg);
-
-    RootedShape shape(cx);
-    while (obj) {
-        /* Lookups will not be cached through non-native protos. */
-        if (!obj->isNative())
-            break;
-
-        shape = obj->as<NativeObject>().lookup(cx, id);
-        if (shape)
-            return obj->as<NativeObject>().shadowingShapeChange(cx, *shape);
-
-        obj = obj->getProto();
-    }
-
-    return true;
-}
-
-static bool
-PurgeScopeChainHelper(ExclusiveContext *cx, HandleObject objArg, HandleId id)
-{
-    /* Re-root locally so we can re-assign. */
-    RootedObject obj(cx, objArg);
-
-    MOZ_ASSERT(obj->isNative());
-    MOZ_ASSERT(obj->isDelegate());
-
-    /* Lookups on integer ids cannot be cached through prototypes. */
-    if (JSID_IS_INT(id))
-        return true;
-
-    PurgeProtoChain(cx, obj->getProto(), id);
-
-    /*
-     * We must purge the scope chain only for Call objects as they are the only
-     * kind of cacheable non-global object that can gain properties after outer
-     * properties with the same names have been cached or traced. Call objects
-     * may gain such properties via eval introducing new vars; see bug 490364.
-     */
-    if (obj->is<CallObject>()) {
-        while ((obj = obj->enclosingScope()) != nullptr) {
-            if (!PurgeProtoChain(cx, obj, id))
-                return false;
-        }
-    }
-
-    return true;
-}
-
-/*
- * PurgeScopeChain does nothing if obj is not itself a prototype or parent
- * scope, else it reshapes the scope and prototype chains it links. It calls
- * PurgeScopeChainHelper, which asserts that obj is flagged as a delegate
- * (i.e., obj has ever been on a prototype or parent chain).
- */
-static inline bool
-PurgeScopeChain(ExclusiveContext *cx, JS::HandleObject obj, JS::HandleId id)
-{
-    if (obj->isDelegate())
-        return PurgeScopeChainHelper(cx, obj, id);
-    return true;
-}
-
-bool
-baseops::DefineGeneric(ExclusiveContext *cx, HandleNativeObject obj, HandleId id, HandleValue value,
-                       PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
-{
-    return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs);
-}
-
-/* static */ bool
 JSObject::defineGeneric(ExclusiveContext *cx, HandleObject obj,
                         HandleId id, HandleValue value,
                         JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
 {
     MOZ_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS));
     js::DefineGenericOp op = obj->getOps()->defineGeneric;
     if (op) {
         if (!cx->shouldBeJSContext())
@@ -4038,724 +3144,45 @@ JSObject::defineGeneric(ExclusiveContext
 JSObject::defineProperty(ExclusiveContext *cx, HandleObject obj,
                          PropertyName *name, HandleValue value,
                          JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
 {
     RootedId id(cx, NameToId(name));
     return defineGeneric(cx, obj, id, value, getter, setter, attrs);
 }
 
-bool
-baseops::DefineElement(ExclusiveContext *cx, HandleNativeObject obj, uint32_t index, HandleValue value,
-                       PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
-{
-    RootedId id(cx);
-    if (index <= JSID_INT_MAX) {
-        id = INT_TO_JSID(index);
-        return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs);
-    }
-
-    AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
-
-    if (!IndexToId(cx, index, &id))
-        return false;
-
-    return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs);
-}
-
 /* static */ bool
 JSObject::defineElement(ExclusiveContext *cx, HandleObject obj,
                         uint32_t index, HandleValue value,
                         JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
 {
     js::DefineElementOp op = obj->getOps()->defineElement;
     if (op) {
         if (!cx->shouldBeJSContext())
             return false;
         return op(cx->asJSContext(), obj, index, value, getter, setter, attrs);
     }
     return baseops::DefineElement(cx, obj.as<NativeObject>(), index, value, getter, setter, attrs);
 }
 
-Shape *
-NativeObject::addDataProperty(ExclusiveContext *cx, jsid idArg, uint32_t slot, unsigned attrs)
-{
-    MOZ_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
-    RootedNativeObject self(cx, this);
-    RootedId id(cx, idArg);
-    return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0);
-}
-
-Shape *
-NativeObject::addDataProperty(ExclusiveContext *cx, HandlePropertyName name,
-                          uint32_t slot, unsigned attrs)
-{
-    MOZ_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
-    RootedNativeObject self(cx, this);
-    RootedId id(cx, NameToId(name));
-    return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0);
-}
-
-/*
- * Backward compatibility requires allowing addProperty hooks to mutate the
- * nominal initial value of a slotful property, while GC safety wants that
- * value to be stored before the call-out through the hook.  Optimize to do
- * both while saving cycles for classes that stub their addProperty hook.
- */
-template <ExecutionMode mode>
-static inline bool
-CallAddPropertyHook(typename ExecutionModeTraits<mode>::ExclusiveContextType cxArg,
-                    const Class *clasp, HandleNativeObject obj, HandleShape shape,
-                    HandleValue nominal)
-{
-    if (clasp->addProperty != JS_PropertyStub) {
-        if (mode == ParallelExecution)
-            return false;
-
-        ExclusiveContext *cx = cxArg->asExclusiveContext();
-        if (!cx->shouldBeJSContext())
-            return false;
-
-        /* Make a local copy of value so addProperty can mutate its inout parameter. */
-        RootedValue value(cx, nominal);
-
-        Rooted<jsid> id(cx, shape->propid());
-        if (!CallJSPropertyOp(cx->asJSContext(), clasp->addProperty, obj, id, &value)) {
-            obj->removeProperty(cx, shape->propid());
-            return false;
-        }
-        if (value.get() != nominal) {
-            if (shape->hasSlot())
-                obj->setSlotWithType(cx, shape, value);
-        }
-    }
-    return true;
-}
-
-template <ExecutionMode mode>
-static inline bool
-CallAddPropertyHookDense(typename ExecutionModeTraits<mode>::ExclusiveContextType cxArg,
-                         const Class *clasp, HandleNativeObject obj, uint32_t index,
-                         HandleValue nominal)
-{
-    /* Inline addProperty for array objects. */
-    if (obj->is<ArrayObject>()) {
-        ArrayObject *arr = &obj->as<ArrayObject>();
-        uint32_t length = arr->length();
-        if (index >= length) {
-            if (mode == ParallelExecution) {
-                /* We cannot deal with overflows in parallel. */
-                if (length > INT32_MAX)
-                    return false;
-                arr->setLengthInt32(index + 1);
-            } else {
-                arr->setLength(cxArg->asExclusiveContext(), index + 1);
-            }
-        }
-        return true;
-    }
-
-    if (clasp->addProperty != JS_PropertyStub) {
-        if (mode == ParallelExecution)
-            return false;
-
-        ExclusiveContext *cx = cxArg->asExclusiveContext();
-        if (!cx->shouldBeJSContext())
-            return false;
-
-        if (!obj->maybeCopyElementsForWrite(cx))
-            return false;
-
-        /* Make a local copy of value so addProperty can mutate its inout parameter. */
-        RootedValue value(cx, nominal);
-
-        Rooted<jsid> id(cx, INT_TO_JSID(index));
-        if (!CallJSPropertyOp(cx->asJSContext(), clasp->addProperty, obj, id, &value)) {
-            obj->setDenseElementHole(cx, index);
-            return false;
-        }
-        if (value.get() != nominal)
-            obj->setDenseElementWithType(cx, index, value);
-    }
-
-    return true;
-}
-
-template <ExecutionMode mode>
-static bool
-UpdateShapeTypeAndValue(typename ExecutionModeTraits<mode>::ExclusiveContextType cx,
-                        NativeObject *obj, Shape *shape, const Value &value)
-{
-    jsid id = shape->propid();
-    if (shape->hasSlot()) {
-        if (mode == ParallelExecution) {
-            if (!obj->setSlotIfHasType(shape, value, /* overwriting = */ false))
-                return false;
-        } else {
-            obj->setSlotWithType(cx->asExclusiveContext(), shape, value, /* overwriting = */ false);
-        }
-
-        // Per the acquired properties analysis, when the shape of a partially
-        // initialized object is changed to its fully initialized shape, its
-        // type can be updated as well.
-        if (types::TypeNewScript *newScript = obj->typeRaw()->newScript()) {
-            if (newScript->initializedShape() == shape)
-                obj->setType(newScript->initializedType());
-        }
-    }
-    if (!shape->hasSlot() || !shape->hasDefaultGetter() || !shape->hasDefaultSetter()) {
-        if (mode == ParallelExecution) {
-            if (!IsTypePropertyIdMarkedNonData(obj, id))
-                return false;
-        } else {
-            MarkTypePropertyNonData(cx->asExclusiveContext(), obj, id);
-        }
-    }
-    if (!shape->writable()) {
-        if (mode == ParallelExecution) {
-            if (!IsTypePropertyIdMarkedNonWritable(obj, id))
-                return false;
-        } else {
-            MarkTypePropertyNonWritable(cx->asExclusiveContext(), obj, id);
-        }
-    }
-    return true;
-}
-
-template <ExecutionMode mode>
-static inline bool
-DefinePropertyOrElement(typename ExecutionModeTraits<mode>::ExclusiveContextType cx,
-                        HandleNativeObject obj, HandleId id,
-                        PropertyOp getter, StrictPropertyOp setter,
-                        unsigned attrs, HandleValue value,
-                        bool callSetterAfterwards, bool setterIsStrict)
-{
-    /* Use dense storage for new indexed properties where possible. */
-    if (JSID_IS_INT(id) &&
-        getter == JS_PropertyStub &&
-        setter == JS_StrictPropertyStub &&
-        attrs == JSPROP_ENUMERATE &&
-        (!obj->isIndexed() || !obj->containsPure(id)) &&
-        !IsAnyTypedArray(obj))
-    {
-        uint32_t index = JSID_TO_INT(id);
-        bool definesPast;
-        if (!WouldDefinePastNonwritableLength(cx, obj, index, setterIsStrict, &definesPast))
-            return false;
-        if (definesPast)
-            return true;
-
-        NativeObject::EnsureDenseResult result;
-        if (mode == ParallelExecution) {
-            if (obj->writeToIndexWouldMarkNotPacked(index))
-                return false;
-            result = obj->ensureDenseElementsPreservePackedFlag(cx, index, 1);
-        } else {
-            result = obj->ensureDenseElements(cx->asExclusiveContext(), index, 1);
-        }
-
-        if (result == NativeObject::ED_FAILED)
-            return false;
-        if (result == NativeObject::ED_OK) {
-            if (mode == ParallelExecution) {
-                if (!obj->setDenseElementIfHasType(index, value))
-                    return false;
-            } else {
-                obj->setDenseElementWithType(cx->asExclusiveContext(), index, value);
-            }
-            return CallAddPropertyHookDense<mode>(cx, obj->getClass(), obj, index, value);
-        }
-    }
-
-    if (obj->is<ArrayObject>()) {
-        Rooted<ArrayObject*> arr(cx, &obj->as<ArrayObject>());
-        if (id == NameToId(cx->names().length)) {
-            if (mode == SequentialExecution && !cx->shouldBeJSContext())
-                return false;
-            return ArraySetLength<mode>(ExecutionModeTraits<mode>::toContextType(cx), arr, id,
-                                        attrs, value, setterIsStrict);
-        }
-
-        uint32_t index;
-        if (js_IdIsIndex(id, &index)) {
-            bool definesPast;
-            if (!WouldDefinePastNonwritableLength(cx, arr, index, setterIsStrict, &definesPast))
-                return false;
-            if (definesPast)
-                return true;
-        }
-    }
-
-    // Don't define new indexed properties on typed arrays.
-    if (IsAnyTypedArray(obj)) {
-        uint64_t index;
-        if (IsTypedArrayIndex(id, &index))
-            return true;
-    }
-
-    AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
-
-    RootedShape shape(cx, NativeObject::putProperty<mode>(cx, obj, id, getter, setter,
-                                                          SHAPE_INVALID_SLOT, attrs, 0));
-    if (!shape)
-        return false;
-
-    if (!UpdateShapeTypeAndValue<mode>(cx, obj, shape, value))
-        return false;
-
-    /*
-     * Clear any existing dense index after adding a sparse indexed property,
-     * and investigate converting the object to dense indexes.
-     */
-    if (JSID_IS_INT(id)) {
-        if (mode == ParallelExecution)
-            return false;
-
-        if (!obj->maybeCopyElementsForWrite(cx))
-            return false;
-
-        ExclusiveContext *ncx = cx->asExclusiveContext();
-        uint32_t index = JSID_TO_INT(id);
-        NativeObject::removeDenseElementForSparseIndex(ncx, obj, index);
-        NativeObject::EnsureDenseResult result = NativeObject::maybeDensifySparseElements(ncx, obj);
-        if (result == NativeObject::ED_FAILED)
-            return false;
-        if (result == NativeObject::ED_OK) {
-            MOZ_ASSERT(setter == JS_StrictPropertyStub);
-            return CallAddPropertyHookDense<mode>(cx, obj->getClass(), obj, index, value);
-        }
-    }
-
-    if (!CallAddPropertyHook<mode>(cx, obj->getClass(), obj, shape, value))
-        return false;
-
-    if (callSetterAfterwards && setter != JS_StrictPropertyStub) {
-        if (!cx->shouldBeJSContext())
-            return false;
-        RootedValue nvalue(cx, value);
-        return NativeSet<mode>(ExecutionModeTraits<mode>::toContextType(cx),
-                               obj, obj, shape, setterIsStrict, &nvalue);
-    }
-    return true;
-}
-
-static bool
-NativeLookupOwnProperty(ExclusiveContext *cx, HandleNativeObject obj, HandleId id,
-                        MutableHandle<Shape*> shapep);
-
-bool
-js::DefineNativeProperty(ExclusiveContext *cx, HandleNativeObject obj, HandleId id, HandleValue value,
-                         PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
-{
-    MOZ_ASSERT(!(attrs & JSPROP_NATIVE_ACCESSORS));
-
-    AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
-
-    RootedShape shape(cx);
-    RootedValue updateValue(cx, value);
-    bool shouldDefine = true;
-
-    /*
-     * If defining a getter or setter, we must check for its counterpart and
-     * update the attributes and property ops.  A getter or setter is really
-     * only half of a property.
-     */
-    if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
-        if (!NativeLookupOwnProperty(cx, obj, id, &shape))
-            return false;
-        if (shape) {
-            /*
-             * If we are defining a getter whose setter was already defined, or
-             * vice versa, finish the job via obj->changeProperty.
-             */
-            if (IsImplicitDenseOrTypedArrayElement(shape)) {
-                if (IsAnyTypedArray(obj)) {
-                    /* Ignore getter/setter properties added to typed arrays. */
-                    return true;
-                }
-                if (!NativeObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id)))
-                    return false;
-                shape = obj->lookup(cx, id);
-            }
-            if (shape->isAccessorDescriptor()) {
-                attrs = ApplyOrDefaultAttributes(attrs, shape);
-                shape = NativeObject::changeProperty<SequentialExecution>(cx, obj, shape, attrs,
-                                                                          JSPROP_GETTER | JSPROP_SETTER,
-                                                                          (attrs & JSPROP_GETTER)
-                                                                          ? getter
-                                                                          : shape->getter(),
-                                                                          (attrs & JSPROP_SETTER)
-                                                                          ? setter
-                                                                          : shape->setter());
-                if (!shape)
-                    return false;
-                shouldDefine = false;
-            }
-        }
-    } else if (!(attrs & JSPROP_IGNORE_VALUE)) {
-        /*
-         * We might still want to ignore redefining some of our attributes, if the
-         * request came through a proxy after Object.defineProperty(), but only if we're redefining
-         * a data property.
-         * FIXME: All this logic should be removed when Proxies use PropDesc, but we need to
-         *        remove JSPropertyOp getters and setters first.
-         * FIXME: This is still wrong for various array types, and will set the wrong attributes
-         *        by accident, but we can't use NativeLookupOwnProperty in this case, because of resolve
-         *        loops.
-         */
-        shape = obj->lookup(cx, id);
-        if (shape && shape->isDataDescriptor())
-            attrs = ApplyOrDefaultAttributes(attrs, shape);
-    } else {
-        /*
-         * We have been asked merely to update some attributes by a caller of
-         * Object.defineProperty, laundered through the proxy system, and returning here. We can
-         * get away with just using JSObject::changeProperty here.
-         */
-        if (!NativeLookupOwnProperty(cx, obj, id, &shape))
-            return false;
-
-        if (shape) {
-            // Don't forget about arrays.
-            if (IsImplicitDenseOrTypedArrayElement(shape)) {
-                if (obj->is<TypedArrayObject>()) {
-                    /*
-                     * Silently ignore attempts to change individial index attributes.
-                     * FIXME: Uses the same broken behavior as for accessors. This should
-                     *        probably throw.
-                     */
-                    return true;
-                }
-                if (!NativeObject::sparsifyDenseElement(cx, obj, JSID_TO_INT(id)))
-                    return false;
-                shape = obj->lookup(cx, id);
-            }
-
-            attrs = ApplyOrDefaultAttributes(attrs, shape);
-
-            /* Keep everything from the shape that isn't the things we're changing */
-            unsigned attrMask = ~(JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
-            shape = NativeObject::changeProperty<SequentialExecution>(cx, obj, shape, attrs, attrMask,
-                                                                      shape->getter(), shape->setter());
-            if (!shape)
-                return false;
-            if (shape->hasSlot())
-                updateValue = obj->getSlot(shape->slot());
-            shouldDefine = false;
-        }
-    }
-
-    /*
-     * Purge the property cache of any properties named by id that are about
-     * to be shadowed in obj's scope chain.
-     */
-    if (!PurgeScopeChain(cx, obj, id))
-        return false;
-
-    /* Use the object's class getter and setter by default. */
-    const Class *clasp = obj->getClass();
-    if (!getter && !(attrs & JSPROP_GETTER))
-        getter = clasp->getProperty;
-    if (!setter && !(attrs & JSPROP_SETTER))
-        setter = clasp->setProperty;
-
-    if (shouldDefine) {
-        // Handle the default cases here. Anyone that wanted to set non-default attributes has
-        // cleared the IGNORE flags by now. Since we can never get here with JSPROP_IGNORE_VALUE
-        // relevant, just clear it.
-        attrs = ApplyOrDefaultAttributes(attrs) & ~JSPROP_IGNORE_VALUE;
-        return DefinePropertyOrElement<SequentialExecution>(cx, obj, id, getter, setter,
-                                                            attrs, value, false, false);
-    }
-
-    MOZ_ASSERT(shape);
-
-    JS_ALWAYS_TRUE(UpdateShapeTypeAndValue<SequentialExecution>(cx, obj, shape, updateValue));
-
-    return CallAddPropertyHook<SequentialExecution>(cx, clasp, obj, shape, updateValue);
-}
-
-/*
- * Call obj's resolve hook.
- *
- * cx, id, and flags are the parameters initially passed to the ongoing lookup;
- * objp and propp are its out parameters. obj is an object along the prototype
- * chain from where the lookup started.
- *
- * There are four possible outcomes:
- *
- *   - On failure, report an error or exception and return false.
- *
- *   - If we are already resolving a property of *curobjp, set *recursedp = true,
- *     and return true.
- *
- *   - If the resolve hook finds or defines the sought property, set *objp and
- *     *propp appropriately, set *recursedp = false, and return true.
- *
- *   - Otherwise no property was resolved. Set *propp = nullptr and
- *     *recursedp = false and return true.
- */
-static MOZ_ALWAYS_INLINE bool
-CallResolveOp(JSContext *cx, HandleNativeObject obj, HandleId id, MutableHandleObject objp,
-              MutableHandleShape propp, bool *recursedp)
-{
-    const Class *clasp = obj->getClass();
-    JSResolveOp resolve = clasp->resolve;
-
-    /*
-     * Avoid recursion on (obj, id) already being resolved on cx.
-     *
-     * Once we have successfully added an entry for (obj, key) to
-     * cx->resolvingTable, control must go through cleanup: before
-     * returning.  But note that JS_DHASH_ADD may find an existing
-     * entry, in which case we bail to suppress runaway recursion.
-     */
-    AutoResolving resolving(cx, obj, id);
-    if (resolving.alreadyStarted()) {
-        /* Already resolving id in obj -- suppress recursion. */
-        *recursedp = true;
-        return true;
-    }
-    *recursedp = false;
-
-    propp.set(nullptr);
-
-    if (clasp->flags & JSCLASS_NEW_RESOLVE) {
-        JSNewResolveOp newresolve = reinterpret_cast<JSNewResolveOp>(resolve);
-        RootedObject obj2(cx, nullptr);
-        if (!newresolve(cx, obj, id, &obj2))
-            return false;
-
-        /*
-         * We trust the new style resolve hook to set obj2 to nullptr when
-         * the id cannot be resolved. But, when obj2 is not null, we do
-         * not assume that id must exist and do full nativeLookup for
-         * compatibility.
-         */
-        if (!obj2)
-            return true;
-
-        if (!obj2->isNative()) {
-            /* Whoops, newresolve handed back a foreign obj2. */
-            MOZ_ASSERT(obj2 != obj);
-            return JSObject::lookupGeneric(cx, obj2, id, objp, propp);
-        }
-
-        objp.set(obj2);
-    } else {
-        if (!resolve(cx, obj, id))
-            return false;
-
-        objp.set(obj);
-    }
-
-    NativeObject *nobjp = &objp->as<NativeObject>();
-
-    if (JSID_IS_INT(id) && nobjp->containsDenseElement(JSID_TO_INT(id))) {
-        MarkDenseOrTypedArrayElementFound<CanGC>(propp);
-        return true;
-    }
-
-    Shape *shape;
-    if (!nobjp->empty() && (shape = nobjp->lookup(cx, id)))
-        propp.set(shape);
-    else
-        objp.set(nullptr);
-
-    return true;
-}
-
-template <AllowGC allowGC>
-static MOZ_ALWAYS_INLINE bool
-LookupOwnPropertyInline(ExclusiveContext *cx,
-                        typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
-                        typename MaybeRooted<jsid, allowGC>::HandleType id,
-                        typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
-                        typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp,
-                        bool *donep)
-{
-    // Check for a native dense element.
-    if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
-        objp.set(obj);
-        MarkDenseOrTypedArrayElementFound<allowGC>(propp);
-        *donep = true;
-        return true;
-    }
-
-    // Check for a typed array element. Integer lookups always finish here
-    // so that integer properties on the prototype are ignored even for out
-    // of bounds accesses.
-    if (IsAnyTypedArray(obj)) {
-        uint64_t index;
-        if (IsTypedArrayIndex(id, &index)) {
-            if (index < AnyTypedArrayLength(obj)) {
-                objp.set(obj);
-                MarkDenseOrTypedArrayElementFound<allowGC>(propp);
-            } else {
-                objp.set(nullptr);
-                propp.set(nullptr);
-            }
-            *donep = true;
-            return true;
-        }
-    }
-
-    // Check for a native property.
-    if (Shape *shape = obj->lookup(cx, id)) {
-        objp.set(obj);
-        propp.set(shape);
-        *donep = true;
-        return true;
-    }
-
-    // id was not found in obj. Try obj's resolve hook, if any.
-    if (obj->getClass()->resolve != JS_ResolveStub) {
-        if (!cx->shouldBeJSContext() || !allowGC)
-            return false;
-
-        bool recursed;
-        if (!CallResolveOp(cx->asJSContext(),
-                           MaybeRooted<NativeObject*, allowGC>::toHandle(obj),
-                           MaybeRooted<jsid, allowGC>::toHandle(id),
-                           MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp),
-                           MaybeRooted<Shape*, allowGC>::toMutableHandle(propp),
-                           &recursed))
-        {
-            return false;
-        }
-
-        if (recursed) {
-            objp.set(nullptr);
-            propp.set(nullptr);
-            *donep = true;
-            return true;
-        }
-
-        if (propp) {
-            *donep = true;
-            return true;
-        }
-    }
-
-    *donep = false;
-    return true;
-}
-
-static bool
-NativeLookupOwnProperty(ExclusiveContext *cx, HandleNativeObject obj, HandleId id,
-                        MutableHandle<Shape*> shapep)
-{
-    RootedObject pobj(cx);
-    bool done;
-
-    if (!LookupOwnPropertyInline<CanGC>(cx, obj, id, &pobj, shapep, &done))
-        return false;
-    if (!done || pobj != obj)
-        shapep.set(nullptr);
-    return true;
-}
-
-template <AllowGC allowGC>
-static MOZ_ALWAYS_INLINE bool
-LookupPropertyInline(ExclusiveContext *cx,
-                     typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
-                     typename MaybeRooted<jsid, allowGC>::HandleType id,
-                     typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
-                     typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
-{
-    /* NB: The logic of this procedure is implicitly reflected in
-     *     BaselineIC.cpp's |EffectlesslyLookupProperty| logic.
-     *     If this changes, please remember to update the logic there as well.
-     */
-
-    /* Search scopes starting with obj and following the prototype link. */
-    typename MaybeRooted<NativeObject*, allowGC>::RootType current(cx, obj);
-
-    while (true) {
-        bool done;
-        if (!LookupOwnPropertyInline<allowGC>(cx, current, id, objp, propp, &done))
-            return false;
-        if (done)
-            return true;
-
-        typename MaybeRooted<JSObject*, allowGC>::RootType proto(cx, current->getProto());
-
-        if (!proto)
-            break;
-        if (!proto->isNative()) {
-            if (!cx->shouldBeJSContext() || !allowGC)
-                return false;
-            return JSObject::lookupGeneric(cx->asJSContext(),
-                                           MaybeRooted<JSObject*, allowGC>::toHandle(proto),
-                                           MaybeRooted<jsid, allowGC>::toHandle(id),
-                                           MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp),
-                                           MaybeRooted<Shape*, allowGC>::toMutableHandle(propp));
-        }
-
-        current = &proto->template as<NativeObject>();
-    }
-
-    objp.set(nullptr);
-    propp.set(nullptr);
-    return true;
-}
-
-template <AllowGC allowGC>
-bool
-baseops::LookupProperty(ExclusiveContext *cx,
-                        typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
-                        typename MaybeRooted<jsid, allowGC>::HandleType id,
-                        typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
-                        typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
-{
-    return LookupPropertyInline<allowGC>(cx, obj, id, objp, propp);
-}
-
-template bool
-baseops::LookupProperty<CanGC>(ExclusiveContext *cx, HandleNativeObject obj, HandleId id,
-                               MutableHandleObject objp, MutableHandleShape propp);
-
-template bool
-baseops::LookupProperty<NoGC>(ExclusiveContext *cx, NativeObject *obj, jsid id,
-                              FakeMutableHandle<JSObject*> objp,
-                              FakeMutableHandle<Shape*> propp);
-
 /* static */ bool
 JSObject::lookupGeneric(JSContext *cx, HandleObject obj, js::HandleId id,
                         MutableHandleObject objp, MutableHandleShape propp)
 {
     /* NB: The logic of lookupGeneric is implicitly reflected in
      *     BaselineIC.cpp's |EffectlesslyLookupProperty| logic.
      *     If this changes, please remember to update the logic there as well.
      */
     LookupGenericOp op = obj->getOps()->lookupGeneric;
     if (op)
         return op(cx, obj, id, objp, propp);
     return baseops::LookupProperty<CanGC>(cx, obj.as<NativeObject>(), id, objp, propp);
 }
 
 bool
-baseops::LookupElement(JSContext *cx, HandleNativeObject obj, uint32_t index,
-                       MutableHandleObject objp, MutableHandleShape propp)
-{
-    RootedId id(cx);
-    if (!IndexToId(cx, index, &id))
-        return false;
-
-    return LookupPropertyInline<CanGC>(cx, obj, id, objp, propp);
-}
-
-bool
-js::LookupNativeProperty(ExclusiveContext *cx, HandleNativeObject obj, HandleId id,
-                         MutableHandleObject objp, MutableHandleShape propp)
-{
-    return LookupPropertyInline<CanGC>(cx, obj, id, objp, propp);
-}
-
-bool
 js::LookupName(JSContext *cx, HandlePropertyName name, HandleObject scopeChain,
                MutableHandleObject objp, MutableHandleObject pobjp, MutableHandleShape propp)
 {
     RootedId id(cx, NameToId(name));
 
     for (RootedObject scope(cx, scopeChain); scope; scope = scope->enclosingScope()) {
         if (!JSObject::lookupGeneric(cx, scope, id, pobjp, propp))
             return false;
@@ -4912,279 +3339,16 @@ js::HasOwnProperty(JSContext *cx, Handle
     RootedObject pobj(cx);
     RootedShape shape(cx);
     if (!HasOwnProperty<CanGC>(cx, obj->getOps()->lookupGeneric, obj, id, &pobj, &shape))
         return false;
     *resultp = (shape != nullptr);
     return true;
 }
 
-template <AllowGC allowGC>
-static MOZ_ALWAYS_INLINE bool
-NativeGetInline(JSContext *cx,
-                typename MaybeRooted<JSObject*, allowGC>::HandleType obj,
-                typename MaybeRooted<JSObject*, allowGC>::HandleType receiver,
-                typename MaybeRooted<NativeObject*, allowGC>::HandleType pobj,
-                typename MaybeRooted<Shape*, allowGC>::HandleType shape,
-                typename MaybeRooted<Value, allowGC>::MutableHandleType vp)
-{
-    if (shape->hasSlot()) {
-        vp.set(pobj->getSlot(shape->slot()));
-        MOZ_ASSERT_IF(!vp.isMagic(JS_UNINITIALIZED_LEXICAL) &&
-                      !pobj->hasSingletonType() &&
-                      !pobj->template is<ScopeObject>() &&
-                      shape->hasDefaultGetter(),
-                      js::types::TypeHasProperty(cx, pobj->type(), shape->propid(), vp));
-    } else {
-        vp.setUndefined();
-    }
-    if (shape->hasDefaultGetter())
-        return true;
-
-    {
-        jsbytecode *pc;
-        JSScript *script = cx->currentScript(&pc);
-        if (script && script->hasBaselineScript()) {
-            switch (JSOp(*pc)) {
-              case JSOP_GETPROP:
-              case JSOP_CALLPROP:
-              case JSOP_LENGTH:
-                script->baselineScript()->noteAccessedGetter(script->pcToOffset(pc));
-                break;
-              default:
-                break;
-            }
-        }
-    }
-
-    if (!allowGC)
-        return false;
-
-    if (!shape->get(cx,
-                    MaybeRooted<JSObject*, allowGC>::toHandle(receiver),
-                    MaybeRooted<JSObject*, allowGC>::toHandle(obj),
-                    MaybeRooted<JSObject*, allowGC>::toHandle(pobj),
-                    MaybeRooted<Value, allowGC>::toMutableHandle(vp)))
-    {
-        return false;
-    }
-
-    /* Update slotful shapes according to the value produced by the getter. */
-    if (shape->hasSlot() && pobj->contains(cx, shape))
-        pobj->setSlot(shape->slot(), vp);
-
-    return true;
-}
-
-bool
-js::NativeGet(JSContext *cx, HandleObject obj, HandleNativeObject pobj, HandleShape shape,
-              MutableHandleValue vp)
-{
-    return NativeGetInline<CanGC>(cx, obj, obj, pobj, shape, vp);
-}
-
-template <ExecutionMode mode>
-bool
-js::NativeSet(typename ExecutionModeTraits<mode>::ContextType cxArg,
-              HandleNativeObject obj, Handle<JSObject*> receiver,
-              HandleShape shape, bool strict, MutableHandleValue vp)
-{
-    MOZ_ASSERT(cxArg->isThreadLocal(obj));
-    MOZ_ASSERT(obj->isNative());
-
-    if (shape->hasSlot()) {
-        /* If shape has a stub setter, just store vp. */
-        if (shape->hasDefaultSetter()) {
-            if (mode == ParallelExecution) {
-                if (!obj->setSlotIfHasType(shape, vp))
-                    return false;
-            } else {
-                // Global properties declared with 'var' will be initially
-                // defined with an undefined value, so don't treat the initial
-                // assignments to such properties as overwrites.
-                bool overwriting = !obj->is<GlobalObject>() || !obj->getSlot(shape->slot()).isUndefined();
-                obj->setSlotWithType(cxArg->asExclusiveContext(), shape, vp, overwriting);
-            }
-
-            return true;
-        }
-    }
-
-    if (mode == ParallelExecution)
-        return false;
-    JSContext *cx = cxArg->asJSContext();
-
-    if (!shape->hasSlot()) {
-        /*
-         * Allow API consumers to create shared properties with stub setters.
-         * Such properties effectively function as data descriptors which are
-         * not writable, so attempting to set such a property should do nothing
-         * or throw if we're in strict mode.
-         */
-        if (!shape->hasGetterValue() && shape->hasDefaultSetter())
-            return js_ReportGetterOnlyAssignment(cx, strict);
-    }
-
-    RootedValue ovp(cx, vp);
-
-    uint32_t sample = cx->runtime()->propertyRemovals;
-    if (!shape->set(cx, obj, receiver, strict, vp))
-        return false;
-
-    /*
-     * Update any slot for the shape with the value produced by the setter,
-     * unless the setter deleted the shape.
-     */
-    if (shape->hasSlot() &&
-        (MOZ_LIKELY(cx->runtime()->propertyRemovals == sample) ||
-         obj->contains(cx, shape)))
-    {
-        obj->setSlot(shape->slot(), vp);
-    }
-
-    return true;
-}
-
-template bool
-js::NativeSet<SequentialExecution>(JSContext *cx,
-                                   HandleNativeObject obj, HandleObject receiver,
-                                   HandleShape shape, bool strict, MutableHandleValue vp);
-template bool
-js::NativeSet<ParallelExecution>(ForkJoinContext *cx,
-                                 HandleNativeObject obj, HandleObject receiver,
-                                 HandleShape shape, bool strict, MutableHandleValue vp);
-
-template <AllowGC allowGC>
-static MOZ_ALWAYS_INLINE bool
-GetPropertyHelperInline(JSContext *cx,
-                        typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
-                        typename MaybeRooted<JSObject*, allowGC>::HandleType receiver,
-                        typename MaybeRooted<jsid, allowGC>::HandleType id,
-                        typename MaybeRooted<Value, allowGC>::MutableHandleType vp)
-{
-    /* This call site is hot -- use the always-inlined variant of LookupNativeProperty(). */
-    typename MaybeRooted<JSObject*, allowGC>::RootType obj2(cx);
-    typename MaybeRooted<Shape*, allowGC>::RootType shape(cx);
-    if (!LookupPropertyInline<allowGC>(cx, obj, id, &obj2, &shape))
-        return false;
-
-    if (!shape) {
-        if (!allowGC)
-            return false;
-
-        vp.setUndefined();
-
-        if (!CallJSPropertyOp(cx, obj->getClass()->getProperty,
-                              MaybeRooted<JSObject*, allowGC>::toHandle(obj),
-                              MaybeRooted<jsid, allowGC>::toHandle(id),
-                              MaybeRooted<Value, allowGC>::toMutableHandle(vp)))
-        {
-            return false;
-        }
-
-        /*
-         * Give a strict warning if foo.bar is evaluated by a script for an
-         * object foo with no property named 'bar'.
-         */
-        if (vp.isUndefined()) {
-            jsbytecode *pc = nullptr;
-            RootedScript script(cx, cx->currentScript(&pc));
-            if (!pc)
-                return true;
-            JSOp op = (JSOp) *pc;
-
-            if (op == JSOP_GETXPROP) {
-                /* Undefined property during a name lookup, report an error. */
-                JSAutoByteString printable;
-                if (js_ValueToPrintable(cx, IdToValue(id), &printable))
-                    js_ReportIsNotDefined(cx, printable.ptr());
-                return false;
-            }
-
-            /* Don't warn if extra warnings not enabled or for random getprop operations. */
-            if (!cx->compartment()->options().extraWarnings(cx) || (op != JSOP_GETPROP && op != JSOP_GETELEM))
-                return true;
-
-            /* Don't warn repeatedly for the same script. */
-            if (!script || script->warnedAboutUndefinedProp())
-                return true;
-
-            /*
-             * Don't warn in self-hosted code (where the further presence of
-             * JS::RuntimeOptions::werror() would result in impossible-to-avoid
-             * errors to entirely-innocent client code).
-             */
-            if (script->selfHosted())
-                return true;
-
-            /* We may just be checking if that object has an iterator. */
-            if (JSID_IS_ATOM(id, cx->names().iteratorIntrinsic))
-                return true;
-
-            /* Do not warn about tests like (obj[prop] == undefined). */
-            pc += js_CodeSpec[op].length;
-            if (Detecting(cx, script, pc))
-                return true;
-
-            unsigned flags = JSREPORT_WARNING | JSREPORT_STRICT;
-            script->setWarnedAboutUndefinedProp();
-
-            /* Ok, bad undefined property reference: whine about it. */
-            RootedValue val(cx, IdToValue(id));
-            if (!js_ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP,
-                                          JSDVG_IGNORE_STACK, val, js::NullPtr(),
-                                          nullptr, nullptr))
-            {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    if (!obj2->isNative()) {
-        if (!allowGC)
-            return false;
-        HandleObject obj2Handle = MaybeRooted<JSObject*, allowGC>::toHandle(obj2);
-        HandleObject receiverHandle = MaybeRooted<JSObject*, allowGC>::toHandle(receiver);
-        HandleId idHandle = MaybeRooted<jsid, allowGC>::toHandle(id);
-        MutableHandleValue vpHandle = MaybeRooted<Value, allowGC>::toMutableHandle(vp);
-        return obj2->template is<ProxyObject>()
-               ? Proxy::get(cx, obj2Handle, receiverHandle, idHandle, vpHandle)
-               : JSObject::getGeneric(cx, obj2Handle, obj2Handle, idHandle, vpHandle);
-    }
-
-    typename MaybeRooted<NativeObject*, allowGC>::HandleType nobj2 =
-        MaybeRooted<JSObject*, allowGC>::template downcastHandle<NativeObject>(obj2);
-
-    if (IsImplicitDenseOrTypedArrayElement(shape)) {
-        vp.set(nobj2->getDenseOrTypedArrayElement(JSID_TO_INT(id)));
-        return true;
-    }
-
-    /* This call site is hot -- use the always-inlined variant of NativeGet(). */
-    if (!NativeGetInline<allowGC>(cx, obj, receiver, nobj2, shape, vp))
-        return false;
-
-    return true;
-}
-
-bool
-baseops::GetProperty(JSContext *cx, HandleNativeObject obj, HandleObject receiver, HandleId id, MutableHandleValue vp)
-{
-    /* This call site is hot -- use the always-inlined variant of GetPropertyHelper(). */
-    return GetPropertyHelperInline<CanGC>(cx, obj, receiver, id, vp);
-}
-
-bool
-baseops::GetPropertyNoGC(JSContext *cx, NativeObject *obj, JSObject *receiver, jsid id, Value *vp)
-{
-    AutoAssertNoException nogc(cx);
-    return GetPropertyHelperInline<NoGC>(cx, obj, receiver, id, vp);
-}
-
 static MOZ_ALWAYS_INLINE bool
 LookupPropertyPureInline(JSObject *obj, jsid id, NativeObject **objp, Shape **propp)
 {
     if (!obj->isNative())
         return false;
 
     NativeObject *current = &obj->as<NativeObject>();
     while (true) {
@@ -5337,51 +3501,16 @@ js::GetObjectElementOperationPure(Thread
     JSAtom *name = &prop.toString()->asAtom();
     if (name->isIndex(&index))
         return GetElementPure(cx, obj, index, vp);
 
     return GetPropertyPure(cx, obj, NameToId(name->asPropertyName()), vp);
 }
 
 bool
-baseops::GetElement(JSContext *cx, HandleNativeObject obj, HandleObject receiver, uint32_t index,
-                    MutableHandleValue vp)
-{
-    RootedId id(cx);
-    if (!IndexToId(cx, index, &id))
-        return false;
-
-    /* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */
-    return GetPropertyHelperInline<CanGC>(cx, obj, receiver, id, vp);
-}
-
-static bool
-MaybeReportUndeclaredVarAssignment(JSContext *cx, JSString *propname)
-{
-    {
-        JSScript *script = cx->currentScript(nullptr, JSContext::ALLOW_CROSS_COMPARTMENT);
-        if (!script)
-            return true;
-
-        // If the code is not strict and extra warnings aren't enabled, then no
-        // check is needed.
-        if (!script->strict() && !cx->compartment()->options().extraWarnings(cx))
-            return true;
-    }
-
-    JSAutoByteString bytes(cx, propname);
-    return !!bytes &&
-           JS_ReportErrorFlagsAndNumber(cx,
-                                        (JSREPORT_WARNING | JSREPORT_STRICT
-                                         | JSREPORT_STRICT_MODE_ERROR),
-                                        js_GetErrorMessage, nullptr,
-                                        JSMSG_UNDECLARED_VAR, bytes.ptr());
-}
-
-bool
 JSObject::reportReadOnly(ThreadSafeContext *cxArg, jsid id, unsigned report)
 {
     if (cxArg->isForkJoinContext())
         return cxArg->asForkJoinContext()->reportError(report);
 
     if (!cxArg->isJSContext())
         return true;
 
@@ -5429,394 +3558,16 @@ JSObject::callMethod(JSContext *cx, Hand
 {
     RootedValue fval(cx);
     RootedObject obj(cx, this);
     if (!JSObject::getGeneric(cx, obj, obj, id, &fval))
         return false;
     return Invoke(cx, ObjectValue(*obj), fval, argc, argv, vp);
 }
 
-template <ExecutionMode mode>
-bool
-baseops::SetPropertyHelper(typename ExecutionModeTraits<mode>::ContextType cxArg,
-                           HandleNativeObject obj, HandleObject receiver, HandleId id,
-                           QualifiedBool qualified, MutableHandleValue vp, bool strict)
-{
-    MOZ_ASSERT(cxArg->isThreadLocal(obj));
-
-    if (MOZ_UNLIKELY(obj->watched())) {
-        if (mode == ParallelExecution)
-            return false;
-
-        /* Fire watchpoints, if any. */
-        JSContext *cx = cxArg->asJSContext();
-        WatchpointMap *wpmap = cx->compartment()->watchpointMap;
-        if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp))
-            return false;
-    }
-
-    RootedObject pobj(cxArg);
-    RootedShape shape(cxArg);
-    if (mode == ParallelExecution) {
-        NativeObject *npobj;
-        if (!LookupPropertyPure(obj, id, &npobj, shape.address()))
-            return false;
-        pobj = npobj;
-    } else {
-        JSContext *cx = cxArg->asJSContext();
-        if (!LookupNativeProperty(cx, obj, id, &pobj, &shape))
-            return false;
-    }
-    if (shape) {
-        if (!pobj->isNative()) {
-            if (pobj->is<ProxyObject>()) {
-                if (mode == ParallelExecution)
-                    return false;
-
-                JSContext *cx = cxArg->asJSContext();
-                Rooted<PropertyDescriptor> pd(cx);
-                if (!Proxy::getPropertyDescriptor(cx, pobj, id, &pd))
-                    return false;
-
-                if ((pd.attributes() & (JSPROP_SHARED | JSPROP_SHADOWABLE)) == JSPROP_SHARED) {
-                    return !pd.setter() ||
-                           CallSetter(cx, receiver, id, pd.setter(), pd.attributes(), strict, vp);
-                }
-
-                if (pd.isReadonly()) {
-                    if (strict)
-                        return JSObject::reportReadOnly(cx, id, JSREPORT_ERROR);
-                    if (cx->compartment()->options().extraWarnings(cx))
-                        return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
-                    return true;
-                }
-            }
-
-            shape = nullptr;
-        }
-    } else {
-        /* We should never add properties to lexical blocks. */
-        MOZ_ASSERT(!obj->is<BlockObject>());
-
-        if (obj->isUnqualifiedVarObj() && !qualified) {
-            if (mode == ParallelExecution)
-                return false;
-
-            if (!MaybeReportUndeclaredVarAssignment(cxArg->asJSContext(), JSID_TO_STRING(id)))
-                return false;
-        }
-    }
-
-    /*
-     * Now either shape is null, meaning id was not found in obj or one of its
-     * prototypes; or shape is non-null, meaning id was found directly in pobj.
-     */
-    unsigned attrs = JSPROP_ENUMERATE;
-    const Class *clasp = obj->getClass();
-    PropertyOp getter = clasp->getProperty;
-    StrictPropertyOp setter = clasp->setProperty;
-
-    if (IsImplicitDenseOrTypedArrayElement(shape)) {
-        /* ES5 8.12.4 [[Put]] step 2, for a dense data property on pobj. */
-        if (pobj != obj)
-            shape = nullptr;
-    } else if (shape) {
-        /* ES5 8.12.4 [[Put]] step 2. */
-        if (shape->isAccessorDescriptor()) {
-            if (shape->hasDefaultSetter()) {
-                /* Bail out of parallel execution if we are strict to throw. */
-                if (mode == ParallelExecution)
-                    return !strict;
-
-                return js_ReportGetterOnlyAssignment(cxArg->asJSContext(), strict);
-            }
-        } else {
-            MOZ_ASSERT(shape->isDataDescriptor());
-
-            if (!shape->writable()) {
-                /*
-                 * Error in strict mode code, warn with extra warnings
-                 * options, otherwise do nothing.
-                 *
-                 * Bail out of parallel execution if we are strict to throw.
-                 */
-                if (mode == ParallelExecution)
-                    return !strict;
-
-                JSContext *cx = cxArg->asJSContext();
-                if (strict)
-                    return JSObject::reportReadOnly(cx, id, JSREPORT_ERROR);
-                if (cx->compartment()->options().extraWarnings(cx))
-                    return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
-                return true;
-            }
-        }
-
-        attrs = shape->attributes();
-        if (pobj != obj) {
-            /*
-             * We found id in a prototype object: prepare to share or shadow.
-             */
-            if (!shape->shadowable()) {
-                if (shape->hasDefaultSetter() && !shape->hasGetterValue())
-                    return true;
-
-                if (mode == ParallelExecution)
-                    return false;
-
-                return shape->set(cxArg->asJSContext(), obj, receiver, strict, vp);
-            }
-
-            /*
-             * Preserve attrs except JSPROP_SHARED, getter, and setter when
-             * shadowing any property that has no slot (is shared). We must
-             * clear the shared attribute for the shadowing shape so that the
-             * property in obj that it defines has a slot to retain the value
-             * being set, in case the setter simply cannot operate on instances
-             * of obj's class by storing the value in some class-specific
-             * location.
-             */
-            if (!shape->hasSlot()) {
-                attrs &= ~JSPROP_SHARED;
-                getter = shape->getter();
-                setter = shape->setter();
-            } else {
-                /* Restore attrs to the ECMA default for new properties. */
-                attrs = JSPROP_ENUMERATE;
-            }
-
-            /*
-             * Forget we found the proto-property now that we've copied any
-             * needed member values.
-             */
-            shape = nullptr;
-        }
-    }
-
-    if (IsImplicitDenseOrTypedArrayElement(shape)) {
-        uint32_t index = JSID_TO_INT(id);
-
-        if (IsAnyTypedArray(obj)) {
-            double d;
-            if (mode == ParallelExecution) {
-                // Bail if converting the value might invoke user-defined
-                // conversions.
-                if (vp.isObject())
-                    return false;
-                if (!NonObjectToNumber(cxArg, vp, &d))
-                    return false;
-            } else {
-                if (!ToNumber(cxArg->asJSContext(), vp, &d))
-                    return false;
-            }
-
-            // Silently do nothing for out-of-bounds sets, for consistency with
-            // current behavior.  (ES6 currently says to throw for this in
-            // strict mode code, so we may eventually need to change.)
-            uint32_t len = AnyTypedArrayLength(obj);
-            if (index < len) {
-                if (obj->is<TypedArrayObject>())
-                    TypedArrayObject::setElement(obj->as<TypedArrayObject>(), index, d);
-                else
-                    SharedTypedArrayObject::setElement(obj->as<SharedTypedArrayObject>(), index, d);
-            }
-            return true;
-        }
-
-        bool definesPast;
-        if (!WouldDefinePastNonwritableLength(cxArg, obj, index, strict, &definesPast))
-            return false;
-        if (definesPast) {
-            /* Bail out of parallel execution if we are strict to throw. */
-            if (mode == ParallelExecution)
-                return !strict;
-            return true;
-        }
-
-        if (!obj->maybeCopyElementsForWrite(cxArg))
-            return false;
-
-        if (mode == ParallelExecution)
-            return obj->setDenseElementIfHasType(index, vp);
-
-        obj->setDenseElementWithType(cxArg->asJSContext(), index, vp);
-        return true;
-    }
-
-    if (obj->is<ArrayObject>() && id == NameToId(cxArg->names().length)) {
-        Rooted<ArrayObject*> arr(cxArg, &obj->as<ArrayObject>());
-        return ArraySetLength<mode>(cxArg, arr, id, attrs, vp, strict);
-    }
-
-    if (!shape) {
-        bool extensible;
-        if (mode == ParallelExecution) {
-            if (obj->is<ProxyObject>())
-                return false;
-            extensible = obj->nonProxyIsExtensible();
-        } else {
-            if (!JSObject::isExtensible(cxArg->asJSContext(), obj, &extensible))
-                return false;
-        }
-
-        if (!extensible) {
-            /* Error in strict mode code, warn with extra warnings option, otherwise do nothing. */
-            if (strict)
-                return obj->reportNotExtensible(cxArg);
-            if (mode == SequentialExecution &&
-                cxArg->asJSContext()->compartment()->options().extraWarnings(cxArg->asJSContext()))
-            {
-                return obj->reportNotExtensible(cxArg, JSREPORT_STRICT | JSREPORT_WARNING);
-            }
-            return true;
-        }
-
-        if (mode == ParallelExecution) {
-            if (obj->isDelegate())
-                return false;
-
-            if (getter != JS_PropertyStub || !HasTypePropertyId(obj, id, vp))
-                return false;
-        } else {
-            JSContext *cx = cxArg->asJSContext();
-
-            /* Purge the property cache of now-shadowed id in obj's scope chain. */
-            if (!PurgeScopeChain(cx, obj, id))
-                return false;
-        }
-
-        return DefinePropertyOrElement<mode>(cxArg, obj, id, getter, setter,
-                                             attrs, vp, true, strict);
-    }
-
-    return NativeSet<mode>(cxArg, obj, receiver, shape, strict, vp);
-}
-
-template bool
-baseops::SetPropertyHelper<SequentialExecution>(JSContext *cx, HandleNativeObject obj,
-                                                HandleObject receiver, HandleId id,
-                                                QualifiedBool qualified,
-                                                MutableHandleValue vp, bool strict);
-template bool
-baseops::SetPropertyHelper<ParallelExecution>(ForkJoinContext *cx, HandleNativeObject obj,
-                                              HandleObject receiver, HandleId id,
-                                              QualifiedBool qualified,
-                                              MutableHandleValue vp, bool strict);
-
-bool
-baseops::SetElementHelper(JSContext *cx, HandleNativeObject obj, HandleObject receiver, uint32_t index,
-                          MutableHandleValue vp, bool strict)
-{
-    RootedId id(cx);
-    if (!IndexToId(cx, index, &id))
-        return false;
-    return baseops::SetPropertyHelper<SequentialExecution>(cx, obj, receiver, id, Qualified, vp,
-                                                           strict);
-}
-
-bool
-baseops::GetAttributes(JSContext *cx, HandleNativeObject obj, HandleId id, unsigned *attrsp)
-{
-    RootedObject nobj(cx);
-    RootedShape shape(cx);
-    if (!baseops::LookupProperty<CanGC>(cx, obj, id, &nobj, &shape))
-        return false;
-    if (!shape) {
-        *attrsp = 0;
-        return true;
-    }
-    if (!nobj->isNative())
-        return JSObject::getGenericAttributes(cx, nobj, id, attrsp);
-
-    *attrsp = GetShapeAttributes(nobj, shape);
-    return true;
-}
-
-bool
-baseops::SetAttributes(JSContext *cx, HandleNativeObject obj, HandleId id, unsigned *attrsp)
-{
-    RootedObject nobj(cx);
-    RootedShape shape(cx);
-    if (!baseops::LookupProperty<CanGC>(cx, obj, id, &nobj, &shape))
-        return false;
-    if (!shape)
-        return true;
-    if (nobj->isNative() && IsImplicitDenseOrTypedArrayElement(shape)) {
-        if (IsAnyTypedArray(nobj.get())) {
-            if (*attrsp == (JSPROP_ENUMERATE | JSPROP_PERMANENT))
-                return true;
-            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_SET_ARRAY_ATTRS);
-            return false;
-        }
-        if (!NativeObject::sparsifyDenseElement(cx, nobj.as<NativeObject>(), JSID_TO_INT(id)))
-            return false;
-        shape = nobj->as<NativeObject>().lookup(cx, id);
-    }
-    if (nobj->isNative()) {
-        if (!NativeObject::changePropertyAttributes(cx, nobj.as<NativeObject>(), shape, *attrsp))
-            return false;
-        if (*attrsp & JSPROP_READONLY)
-            MarkTypePropertyNonWritable(cx, nobj, id);
-        return true;
-    } else {
-        return JSObject::setGenericAttributes(cx, nobj, id, attrsp);
-    }
-}
-
-bool
-baseops::DeleteGeneric(JSContext *cx, HandleNativeObject obj, HandleId id, bool *succeeded)
-{
-    RootedObject proto(cx);
-    RootedShape shape(cx);
-    if (!baseops::LookupProperty<CanGC>(cx, obj, id, &proto, &shape))
-        return false;
-    if (!shape || proto != obj) {
-        /*
-         * If no property, or the property comes from a prototype, call the
-         * class's delProperty hook, passing succeeded as the result parameter.
-         */
-        return CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded);
-    }
-
-    cx->runtime()->gc.poke();
-
-    if (IsImplicitDenseOrTypedArrayElement(shape)) {
-        if (IsAnyTypedArray(obj)) {
-            // Don't delete elements from typed arrays.
-            *succeeded = false;
-            return true;
-        }
-
-        if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, id, succeeded))
-            return false;
-        if (!succeeded)
-            return true;
-
-        NativeObject *nobj = &obj->as<NativeObject>();
-        if (!nobj->maybeCopyElementsForWrite(cx))
-            return false;
-
-        nobj->setDenseElementHole(cx, JSID_TO_INT(id));
-        return SuppressDeletedProperty(cx, obj, id);
-    }
-
-    if (!shape->configurable()) {
-        *succeeded = false;
-        return true;
-    }
-
-    RootedId propid(cx, shape->propid());
-    if (!CallJSDeletePropertyOp(cx, obj->getClass()->delProperty, obj, propid, succeeded))
-        return false;
-    if (!succeeded)
-        return true;
-
-    return obj->removeProperty(cx, id) && SuppressDeletedProperty(cx, obj, id);
-}
-
 bool
 js::WatchGuts(JSContext *cx, JS::HandleObject origObj, JS::HandleId id, JS::HandleObject callable)
 {
     RootedObject obj(cx, GetInnerObject(origObj));
     if (obj->isNative()) {
         // Use sparse indexes for watched objects, as dense elements can be
         // written to without checking the watchpoint map.
         if (!NativeObject::sparsifyDenseElements(cx, obj.as<NativeObject>()))
@@ -6569,8 +4320,63 @@ JSObject::addSizeOfExcludingThis(mozilla
 #ifdef JS_HAS_CTYPES
     } else {
         // This must be the last case.
         info->objectsMallocHeapMisc +=
             js::SizeOfDataIfCDataObject(mallocSizeOf, const_cast<JSObject *>(this));
 #endif
     }
 }
+
+bool
+JSObject::hasIdempotentProtoChain() const
+{
+    // Return false if obj (or an object on its proto chain) is non-native or
+    // has a resolve or lookup hook.
+    JSObject *obj = const_cast<JSObject *>(this);
+    while (true) {
+        if (!obj->isNative())
+            return false;
+
+        JSResolveOp resolve = obj->getClass()->resolve;
+        if (resolve != JS_ResolveStub && resolve != (JSResolveOp) js::fun_resolve)
+            return false;
+
+        if (obj->getOps()->lookupProperty || obj->getOps()->lookupGeneric || obj->getOps()->lookupElement)
+            return false;
+
+        obj = obj->getProto();
+        if (!obj)
+            return true;
+    }
+}
+
+void
+JSObject::markChildren(JSTracer *trc)
+{
+    MarkTypeObject(trc, &type_, "type");
+
+    MarkShape(trc, &shape_, "shape");
+
+    const Class *clasp = type_->clasp();
+    if (clasp->trace)
+        clasp->trace(trc, this);
+
+    if (shape_->isNative()) {
+        NativeObject *nobj = &as<NativeObject>();
+        MarkObjectSlots(trc, nobj, 0, nobj->slotSpan());
+
+        do {
+            if (nobj->denseElementsAreCopyOnWrite()) {
+                HeapPtrNativeObject &owner = nobj->getElementsHeader()->ownerObject();
+                if (owner != nobj) {
+                    MarkObject(trc, &owner, "objectElementsOwner");
+                    break;
+                }
+            }
+
+            gc::MarkArraySlots(trc,
+                               nobj->getDenseInitializedLength(),
+                               nobj->getDenseElementsAllowCopyOnWrite(),
+                               "objectElements");
+        } while (false);
+    }
+}
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1058,27 +1058,16 @@ CreateThis(JSContext *cx, const js::Clas
 
 extern JSObject *
 CloneObject(JSContext *cx, HandleObject obj, Handle<js::TaggedProto> proto, HandleObject parent);
 
 extern NativeObject *
 DeepCloneObjectLiteral(JSContext *cx, HandleNativeObject obj, NewObjectKind newKind = GenericObject);
 
 /*
- * Return successfully added or changed shape or nullptr on error.
- */
-extern bool
-DefineNativeProperty(ExclusiveContext *cx, HandleNativeObject obj, HandleId id, HandleValue value,
-                     PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
-
-extern bool
-LookupNativeProperty(ExclusiveContext *cx, HandleNativeObject obj, HandleId id,
-                     js::MutableHandleObject objp, js::MutableHandleShape propp);
-
-/*
  * Call the [[DefineOwnProperty]] internal method of 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.
  */
 extern bool
@@ -1133,26 +1122,16 @@ LookupNameUnqualified(JSContext *cx, Han
 
 extern JSObject *
 js_FindVariableScope(JSContext *cx, JSFunction **funp);
 
 
 namespace js {
 
 bool
-NativeGet(JSContext *cx, HandleObject obj, HandleNativeObject pobj,
-          HandleShape shape, MutableHandle<Value> vp);
-
-template <ExecutionMode mode>
-bool
-NativeSet(typename ExecutionModeTraits<mode>::ContextType cx,
-          HandleNativeObject obj, HandleObject receiver,
-          HandleShape shape, bool strict, MutableHandleValue vp);
-
-bool
 LookupPropertyPure(JSObject *obj, jsid id, NativeObject **objp, Shape **propp);
 
 bool
 GetPropertyPure(ThreadSafeContext *cx, JSObject *obj, jsid id, Value *vp);
 
 inline bool
 GetPropertyPure(ThreadSafeContext *cx, JSObject *obj, PropertyName *name, Value *vp)
 {
@@ -1164,29 +1143,16 @@ GetOwnPropertyDescriptor(JSContext *cx, 
                          MutableHandle<PropertyDescriptor> desc);
 
 bool
 GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id, MutableHandleValue vp);
 
 bool
 NewPropertyDescriptorObject(JSContext *cx, Handle<PropertyDescriptor> desc, MutableHandleValue vp);
 
-/*
- * If obj has an already-resolved data property for id, return true and
- * store the property value in *vp.
- */
-extern bool
-HasDataProperty(JSContext *cx, NativeObject *obj, jsid id, Value *vp);
-
-inline bool
-HasDataProperty(JSContext *cx, NativeObject *obj, PropertyName *name, Value *vp)
-{
-    return HasDataProperty(cx, obj, NameToId(name), vp);
-}
-
 extern bool
 IsDelegate(JSContext *cx, HandleObject obj, const Value &v, bool *result);
 
 // obj is a JSObject*, but we root it immediately up front. We do it
 // that way because we need a Rooted temporary in this method anyway.
 extern bool
 IsDelegateOfObject(JSContext *cx, HandleObject protoObj, JSObject* obj, bool *result);
 
@@ -1257,11 +1223,21 @@ GetFirstArgumentAsObject(JSContext *cx, 
 
 /* Helpers for throwing. These always return false. */
 extern bool
 Throw(JSContext *cx, jsid id, unsigned errorNumber);
 
 extern bool
 Throw(JSContext *cx, JSObject *obj, unsigned errorNumber);
 
+namespace baseops {
+
+extern bool
+Watch(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleObject callable);
+
+extern bool
+Unwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id);
+
+} /* namespace baseops */
+
 }  /* namespace js */
 
 #endif /* jsobj_h */
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -792,25 +792,16 @@ NewObjectMetadata(ExclusiveContext *cxAr
 
             if (!cx->compartment()->callObjectMetadataCallback(cx, pmetadata))
                 return false;
         }
     }
     return true;
 }
 
-inline bool
-DefineNativeProperty(ExclusiveContext *cx, HandleNativeObject obj,
-                     PropertyName *name, HandleValue value,
-                     PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
-{
-    Rooted<jsid> id(cx, NameToId(name));
-    return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs);
-}
-
 namespace baseops {
 
 inline bool
 LookupProperty(ExclusiveContext *cx, HandleNativeObject obj, PropertyName *name,
                MutableHandleObject objp, MutableHandleShape propp)
 {
     Rooted<jsid> id(cx, NameToId(name));
     return LookupProperty<CanGC>(cx, obj, id, objp, propp);
@@ -821,16 +812,50 @@ DefineProperty(ExclusiveContext *cx, Han
                JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs)
 {
     Rooted<jsid> id(cx, NameToId(name));
     return DefineGeneric(cx, obj, id, value, getter, setter, attrs);
 }
 
 } /* namespace baseops */
 
+static inline unsigned
+ApplyAttributes(unsigned attrs, bool enumerable, bool writable, bool configurable)
+{
+    /*
+     * Respect the fact that some callers may want to preserve existing attributes as much as
+     * possible, or install defaults otherwise.
+     */
+    if (attrs & JSPROP_IGNORE_ENUMERATE) {
+        attrs &= ~JSPROP_IGNORE_ENUMERATE;
+        if (enumerable)
+            attrs |= JSPROP_ENUMERATE;
+        else
+            attrs &= ~JSPROP_ENUMERATE;
+    }
+    if (attrs & JSPROP_IGNORE_READONLY) {
+        attrs &= ~JSPROP_IGNORE_READONLY;
+        // Only update the writability if it's relevant
+        if (!(attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
+            if (!writable)
+                attrs |= JSPROP_READONLY;
+            else
+                attrs &= ~JSPROP_READONLY;
+        }
+    }
+    if (attrs & JSPROP_IGNORE_PERMANENT) {
+        attrs &= ~JSPROP_IGNORE_PERMANENT;
+        if (!configurable)
+            attrs |= JSPROP_PERMANENT;
+        else
+            attrs &= ~JSPROP_PERMANENT;
+    }
+    return attrs;
+}
+
 } /* namespace js */
 
 extern js::NativeObject *
 js_InitClass(JSContext *cx, js::HandleObject obj, JSObject *parent_proto,
              const js::Class *clasp, JSNative constructor, unsigned nargs,
              const JSPropertySpec *ps, const JSFunctionSpec *fs,
              const JSPropertySpec *static_ps, const JSFunctionSpec *static_fs,
              js::NativeObject **ctorp = nullptr,
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -883,22 +883,22 @@ InnerViewTable::removeViews(ArrayBufferO
     MOZ_ASSERT(p);
 
     map.remove(p);
 }
 
 bool
 InnerViewTable::sweepEntry(JSObject **pkey, ViewVector &views)
 {
-    if (IsObjectAboutToBeFinalized(pkey))
+    if (IsObjectAboutToBeFinalizedFromAnyThread(pkey))
         return true;
 
     MOZ_ASSERT(!views.empty());
     for (size_t i = 0; i < views.length(); i++) {
-        if (IsObjectAboutToBeFinalized(&views[i])) {
+        if (IsObjectAboutToBeFinalizedFromAnyThread(&views[i])) {
             views[i--] = views.back();
             views.popBack();
         }
     }
 
     return views.empty();
 }
 
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -709,16 +709,111 @@ GlobalHelperThreadState::canStartCompres
 }
 
 bool
 GlobalHelperThreadState::canStartGCHelperTask()
 {
     return !gcHelperWorklist().empty();
 }
 
+bool
+GlobalHelperThreadState::canStartGCParallelTask()
+{
+    return !gcParallelWorklist().empty();
+}
+
+bool
+js::GCParallelTask::startWithLockHeld()
+{
+    MOZ_ASSERT(HelperThreadState().isLocked());
+
+    // Tasks cannot be started twice.
+    MOZ_ASSERT(state == NotStarted);
+
+    // If we do the shutdown GC before running anything, we may never
+    // have initialized the helper threads. Just use the serial path
+    // since we cannot safely intialize them at this point.
+    if (!HelperThreadState().threads)
+        return false;
+
+    if (!HelperThreadState().gcParallelWorklist().append(this))
+        return false;
+    state = Dispatched;
+
+    HelperThreadState().notifyOne(GlobalHelperThreadState::PRODUCER);
+
+    return true;
+}
+
+bool
+js::GCParallelTask::start()
+{
+    AutoLockHelperThreadState helperLock;
+    return startWithLockHeld();
+}
+
+void
+js::GCParallelTask::joinWithLockHeld()
+{
+    MOZ_ASSERT(HelperThreadState().isLocked());
+
+    if (state == NotStarted)
+        return;
+
+    while (state != Finished)
+        HelperThreadState().wait(GlobalHelperThreadState::CONSUMER);
+    state = NotStarted;
+}
+
+void
+js::GCParallelTask::join()
+{
+    AutoLockHelperThreadState helperLock;
+    joinWithLockHeld();
+}
+
+void
+js::GCParallelTask::runFromMainThread(JSRuntime *rt)
+{
+    MOZ_ASSERT(state == NotStarted);
+    MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(rt));
+    uint64_t timeStart = PRMJ_Now();
+    run();
+    duration_ = PRMJ_Now() - timeStart;
+}
+
+void
+js::GCParallelTask::runFromHelperThread()
+{
+    MOZ_ASSERT(HelperThreadState().isLocked());
+
+    {
+        AutoUnlockHelperThreadState parallelSection;
+        uint64_t timeStart = PRMJ_Now();
+        run();
+        duration_ = PRMJ_Now() - timeStart;
+    }
+
+    state = Finished;
+    HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER);
+}
+
+void
+HelperThread::handleGCParallelWorkload()
+{
+    MOZ_ASSERT(HelperThreadState().isLocked());
+    MOZ_ASSERT(HelperThreadState().canStartGCParallelTask());
+    MOZ_ASSERT(idle());
+
+    MOZ_ASSERT(!gcParallelTask);
+    gcParallelTask = HelperThreadState().gcParallelWorklist().popCopy();
+    gcParallelTask->runFromHelperThread();
+    gcParallelTask = nullptr;
+}
+
 static void
 LeaveParseTaskZone(JSRuntime *rt, ParseTask *task)
 {
     // Mark the zone as no longer in use by an ExclusiveContext, and available
     // to be collected by the GC.
     task->cx->leaveCompartment(task->cx->compartment());
     rt->clearUsedByExclusiveThread(task->cx->zone());
 }
@@ -1232,17 +1327,18 @@ HelperThread::threadLoop()
         bool ionCompile = false;
         while (true) {
             if (terminate)
                 return;
             if (HelperThreadState().canStartAsmJSCompile() ||
                 (ionCompile = HelperThreadState().pendingIonCompileHasSufficientPriority()) ||
                 HelperThreadState().canStartParseTask() ||
                 HelperThreadState().canStartCompressionTask() ||
-                HelperThreadState().canStartGCHelperTask())
+                HelperThreadState().canStartGCHelperTask() ||
+                HelperThreadState().canStartGCParallelTask())
             {
                 break;
             }
             HelperThreadState().wait(GlobalHelperThreadState::PRODUCER);
         }
 
         // Dispatch tasks, prioritizing AsmJS work.
         if (HelperThreadState().canStartAsmJSCompile())
@@ -1250,12 +1346,14 @@ HelperThread::threadLoop()
         else if (ionCompile)
             handleIonWorkload();
         else if (HelperThreadState().canStartParseTask())
             handleParseWorkload();
         else if (HelperThreadState().canStartCompressionTask())
             handleCompressionWorkload();
         else if (HelperThreadState().canStartGCHelperTask())
             handleGCHelperWorkload();
+        else if (HelperThreadState().canStartGCParallelTask())
+            handleGCParallelWorkload();
         else
             MOZ_CRASH("No task to perform");
     }
 }
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -42,16 +42,17 @@ class GlobalHelperThreadState
     // Number of threads to create. May be accessed without locking.
     size_t threadCount;
 
     typedef Vector<jit::IonBuilder*, 0, SystemAllocPolicy> IonBuilderVector;
     typedef Vector<AsmJSParallelTask*, 0, SystemAllocPolicy> AsmJSParallelTaskVector;
     typedef Vector<ParseTask*, 0, SystemAllocPolicy> ParseTaskVector;
     typedef Vector<SourceCompressionTask*, 0, SystemAllocPolicy> SourceCompressionTaskVector;
     typedef Vector<GCHelperState *, 0, SystemAllocPolicy> GCHelperStateVector;
+    typedef Vector<GCParallelTask *, 0, SystemAllocPolicy> GCParallelTaskVector;
     typedef mozilla::LinkedList<jit::IonBuilder> IonBuilderList;
 
     // List of available threads, or null if the thread state has not been initialized.
     HelperThread *threads;
 
   private:
     // The lists below are all protected by |lock|.
 
@@ -82,16 +83,19 @@ class GlobalHelperThreadState
     ParseTaskVector parseWaitingOnGC_;
 
     // Source compression worklist.
     SourceCompressionTaskVector compressionWorklist_;
 
     // Runtimes which have sweeping / allocating work to do.
     GCHelperStateVector gcHelperWorklist_;
 
+    // GC tasks needing to be done in parallel.
+    GCParallelTaskVector gcParallelWorklist_;
+
   public:
     size_t maxIonCompilationThreads() const {
         return 1;
     }
     size_t maxAsmJSCompilationThreads() const {
         if (cpuCount < 2)
             return 2;
         return cpuCount;
@@ -173,21 +177,27 @@ class GlobalHelperThreadState
         return compressionWorklist_;
     }
 
     GCHelperStateVector &gcHelperWorklist() {
         MOZ_ASSERT(isLocked());
         return gcHelperWorklist_;
     }
 
+    GCParallelTaskVector &gcParallelWorklist() {
+        MOZ_ASSERT(isLocked());
+        return gcParallelWorklist_;
+    }
+
     bool canStartAsmJSCompile();
     bool canStartIonCompile();
     bool canStartParseTask();
     bool canStartCompressionTask();
     bool canStartGCHelperTask();
+    bool canStartGCParallelTask();
 
     // Unlike the methods above, the value returned by this method can change
     // over time, even if the helper thread state lock is held throughout.
     bool pendingIonCompileHasSufficientPriority();
 
     jit::IonBuilder *highestPriorityPendingIonCompile(bool remove = false);
     HelperThread *lowestPriorityUnpausedIonCompileAtThreshold();
     HelperThread *highestPriorityPausedIonCompile();
@@ -294,27 +304,36 @@ struct HelperThread
     ParseTask *parseTask;
 
     /* Any source being compressed on this thread. */
     SourceCompressionTask *compressionTask;
 
     /* Any GC state for background sweeping or allocating being performed. */
     GCHelperState *gcHelperState;
 
+    /* State required to perform a GC parallel task. */
+    GCParallelTask *gcParallelTask;
+
     bool idle() const {
-        return !ionBuilder && !asmData && !parseTask && !compressionTask && !gcHelperState;
+        return !ionBuilder &&
+               !asmData &&
+               !parseTask &&
+               !compressionTask &&
+               !gcHelperState &&
+               !gcParallelTask;
     }
 
     void destroy();
 
     void handleAsmJSWorkload();
     void handleIonWorkload();
     void handleParseWorkload();
     void handleCompressionWorkload();
     void handleGCHelperWorkload();
+    void handleGCParallelWorkload();
 
     static void ThreadMain(void *arg);
     void threadLoop();
 };
 
 /* Methods for interacting with helper threads. */
 
 // Initialize helper threads unless already initialized.
--- a/js/src/vm/ObjectImpl-inl.h
+++ b/js/src/vm/ObjectImpl-inl.h
@@ -455,16 +455,236 @@ NewNativeObjectWithType(JSContext *cx, H
 
 inline NativeObject *
 NewNativeObjectWithType(JSContext *cx, HandleTypeObject type, JSObject *parent,
                         NewObjectKind newKind = GenericObject)
 {
     return MaybeNativeObject(NewObjectWithType(cx, type, parent, newKind));
 }
 
+/*
+ * Call obj's resolve hook.
+ *
+ * cx, id, and flags are the parameters initially passed to the ongoing lookup;
+ * objp and propp are its out parameters. obj is an object along the prototype
+ * chain from where the lookup started.
+ *
+ * There are four possible outcomes:
+ *
+ *   - On failure, report an error or exception and return false.
+ *
+ *   - If we are already resolving a property of *curobjp, set *recursedp = true,
+ *     and return true.
+ *
+ *   - If the resolve hook finds or defines the sought property, set *objp and
+ *     *propp appropriately, set *recursedp = false, and return true.
+ *
+ *   - Otherwise no property was resolved. Set *propp = nullptr and
+ *     *recursedp = false and return true.
+ */
+static MOZ_ALWAYS_INLINE bool
+CallResolveOp(JSContext *cx, HandleNativeObject obj, HandleId id, MutableHandleObject objp,
+              MutableHandleShape propp, bool *recursedp)
+{
+    const Class *clasp = obj->getClass();
+    JSResolveOp resolve = clasp->resolve;
+
+    /*
+     * Avoid recursion on (obj, id) already being resolved on cx.
+     *
+     * Once we have successfully added an entry for (obj, key) to
+     * cx->resolvingTable, control must go through cleanup: before
+     * returning.  But note that JS_DHASH_ADD may find an existing
+     * entry, in which case we bail to suppress runaway recursion.
+     */
+    AutoResolving resolving(cx, obj, id);
+    if (resolving.alreadyStarted()) {
+        /* Already resolving id in obj -- suppress recursion. */
+        *recursedp = true;
+        return true;
+    }
+    *recursedp = false;
+
+    propp.set(nullptr);
+
+    if (clasp->flags & JSCLASS_NEW_RESOLVE) {
+        JSNewResolveOp newresolve = reinterpret_cast<JSNewResolveOp>(resolve);
+        RootedObject obj2(cx, nullptr);
+        if (!newresolve(cx, obj, id, &obj2))
+            return false;
+
+        /*
+         * We trust the new style resolve hook to set obj2 to nullptr when
+         * the id cannot be resolved. But, when obj2 is not null, we do
+         * not assume that id must exist and do full nativeLookup for
+         * compatibility.
+         */
+        if (!obj2)
+            return true;
+
+        if (!obj2->isNative()) {
+            /* Whoops, newresolve handed back a foreign obj2. */
+            MOZ_ASSERT(obj2 != obj);
+            return JSObject::lookupGeneric(cx, obj2, id, objp, propp);
+        }
+
+        objp.set(obj2);
+    } else {
+        if (!resolve(cx, obj, id))
+            return false;
+
+        objp.set(obj);
+    }
+
+    NativeObject *nobjp = &objp->as<NativeObject>();
+
+    if (JSID_IS_INT(id) && nobjp->containsDenseElement(JSID_TO_INT(id))) {
+        MarkDenseOrTypedArrayElementFound<CanGC>(propp);
+        return true;
+    }
+
+    Shape *shape;
+    if (!nobjp->empty() && (shape = nobjp->lookup(cx, id)))
+        propp.set(shape);
+    else
+        objp.set(nullptr);
+
+    return true;
+}
+
+template <AllowGC allowGC>
+static MOZ_ALWAYS_INLINE bool
+LookupOwnPropertyInline(ExclusiveContext *cx,
+                        typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
+                        typename MaybeRooted<jsid, allowGC>::HandleType id,
+                        typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
+                        typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp,
+                        bool *donep)
+{
+    // Check for a native dense element.
+    if (JSID_IS_INT(id) && obj->containsDenseElement(JSID_TO_INT(id))) {
+        objp.set(obj);
+        MarkDenseOrTypedArrayElementFound<allowGC>(propp);
+        *donep = true;
+        return true;
+    }
+
+    // Check for a typed array element. Integer lookups always finish here
+    // so that integer properties on the prototype are ignored even for out
+    // of bounds accesses.
+    if (IsAnyTypedArray(obj)) {
+        uint64_t index;
+        if (IsTypedArrayIndex(id, &index)) {
+            if (index < AnyTypedArrayLength(obj)) {
+                objp.set(obj);
+                MarkDenseOrTypedArrayElementFound<allowGC>(propp);
+            } else {
+                objp.set(nullptr);
+                propp.set(nullptr);
+            }
+            *donep = true;
+            return true;
+        }
+    }
+
+    // Check for a native property.
+    if (Shape *shape = obj->lookup(cx, id)) {
+        objp.set(obj);
+        propp.set(shape);
+        *donep = true;
+        return true;
+    }
+
+    // id was not found in obj. Try obj's resolve hook, if any.
+    if (obj->getClass()->resolve != JS_ResolveStub) {
+        if (!cx->shouldBeJSContext() || !allowGC)
+            return false;
+
+        bool recursed;
+        if (!CallResolveOp(cx->asJSContext(),
+                           MaybeRooted<NativeObject*, allowGC>::toHandle(obj),
+                           MaybeRooted<jsid, allowGC>::toHandle(id),
+                           MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp),
+                           MaybeRooted<Shape*, allowGC>::toMutableHandle(propp),
+                           &recursed))
+        {
+            return false;
+        }
+
+        if (recursed) {
+            objp.set(nullptr);
+            propp.set(nullptr);
+            *donep = true;
+            return true;
+        }
+
+        if (propp) {
+            *donep = true;
+            return true;
+        }
+    }
+
+    *donep = false;
+    return true;
+}
+
+template <AllowGC allowGC>
+static MOZ_ALWAYS_INLINE bool
+LookupPropertyInline(ExclusiveContext *cx,
+                     typename MaybeRooted<NativeObject*, allowGC>::HandleType obj,
+                     typename MaybeRooted<jsid, allowGC>::HandleType id,
+                     typename MaybeRooted<JSObject*, allowGC>::MutableHandleType objp,
+                     typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
+{
+    /* NB: The logic of this procedure is implicitly reflected in
+     *     BaselineIC.cpp's |EffectlesslyLookupProperty| logic.
+     *     If this changes, please remember to update the logic there as well.
+     */
+
+    /* Search scopes starting with obj and following the prototype link. */
+    typename MaybeRooted<NativeObject*, allowGC>::RootType current(cx, obj);
+
+    while (true) {
+        bool done;
+        if (!LookupOwnPropertyInline<allowGC>(cx, current, id, objp, propp, &done))
+            return false;
+        if (done)
+            return true;
+
+        typename MaybeRooted<JSObject*, allowGC>::RootType proto(cx, current->getProto());
+
+        if (!proto)
+            break;
+        if (!proto->isNative()) {
+            if (!cx->shouldBeJSContext() || !allowGC)
+                return false;
+            return JSObject::lookupGeneric(cx->asJSContext(),
+                                           MaybeRooted<JSObject*, allowGC>::toHandle(proto),
+                                           MaybeRooted<jsid, allowGC>::toHandle(id),
+                                           MaybeRooted<JSObject*, allowGC>::toMutableHandle(objp),
+                                           MaybeRooted<Shape*, allowGC>::toMutableHandle(propp));
+        }
+
+        current = &proto->template as<NativeObject>();
+    }
+
+    objp.set(nullptr);
+    propp.set(nullptr);
+    return true;
+}
+
+inline bool
+DefineNativeProperty(ExclusiveContext *cx, HandleNativeObject obj,
+                     PropertyName *name, HandleValue value,
+                     PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
+{
+    RootedId id(cx, NameToId(name));
+    return DefineNativeProperty(cx, obj, id, value, getter, setter, attrs);
+}
+
 } // namespace js
 
 inline uint8_t *
 JSObject::fakeNativeFixedData(size_t nslots) const
 {
     return static_cast<const js::NativeObject*>(this)->fixedData(nslots);
 }
 
--- a/js/src/vm/ObjectImpl.cpp
+++ b/js/src/vm/ObjectImpl.cpp
@@ -1,27 +1,38 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/ObjectImpl-inl.h"
 
+#include "mozilla/CheckedInt.h"
+
+#include "jswatchpoint.h"
+
 #include "gc/Marking.h"
 #include "js/Value.h"
 #include "vm/Debugger.h"
 #include "vm/TypedArrayCommon.h"
 
 #include "jsobjinlines.h"
+
+#include "vm/ArrayObject-inl.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
 
 using JS::GenericNaN;
+using mozilla::DebugOnly;
+using mozilla::RoundUpPow2;
+
+JS_STATIC_ASSERT(int32_t((NativeObject::NELEMENTS_LIMIT - 1) * sizeof(Value)) ==
+                 int64_t((NativeObject::NELEMENTS_LIMIT - 1) * sizeof(Value)));
 
 PropDesc::PropDesc()
 {
     setUndefined();
 }
 
 void
 PropDesc::setUndefined()
@@ -35,17 +46,16 @@ PropDesc::setUndefined()
     hasValue_ = false;
     hasWritable_ = false;
     hasEnumerable_ = false;
     hasConfigurable_ = false;
 
     isUndefined_ = true;
 }
 
-
 bool
 PropDesc::checkGetter(JSContext *cx)
 {
     if (hasGet_) {
         if (!IsCallable(get_) && !get_.isUndefined()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
                                  js_getter_str);
             return false;
@@ -283,46 +293,2013 @@ js::NativeObject::dynamicSlotsCount(uint
         return SLOT_CAPACITY_MIN;
 
     uint32_t slots = mozilla::RoundUpPow2(span);
     MOZ_ASSERT(slots >= span);
     return slots;
 }
 
 void
-JSObject::markChildren(JSTracer *trc)
-{
-    MarkTypeObject(trc, &type_, "type");
-
-    MarkShape(trc, &shape_, "shape");
-
-    const Class *clasp = type_->clasp();
-    if (clasp->trace)
-        clasp->trace(trc, this);
-
-    if (shape_->isNative()) {
-        NativeObject *nobj = &as<NativeObject>();
-        MarkObjectSlots(trc, nobj, 0, nobj->slotSpan());
-
-        do {
-            if (nobj->denseElementsAreCopyOnWrite()) {
-                HeapPtrNativeObject &owner = nobj->getElementsHeader()->ownerObject();
-                if (owner != nobj) {
-                    MarkObject(trc, &owner, "objectElementsOwner");
-                    break;
-                }
-            }
-
-            gc::MarkArraySlots(trc,
-                               nobj->getDenseInitializedLength(),
-                               nobj->getDenseElementsAllowCopyOnWrite(),
-                               "objectElements");
-        } while (false);
-    }
-}
-
-void
 PropDesc::trace(JSTracer *trc)
 {
     gc::MarkValueRoot(trc, &value_, "PropDesc value");
     gc::MarkValueRoot(trc, &get_, "PropDesc get");
     gc::MarkValueRoot(trc, &set_, "PropDesc set");
 }
+
+/* static */ inline bool
+NativeObject::updateSlotsForSpan(ThreadSafeContext *cx,
+                                 HandleNativeObject obj, size_t oldSpan, size_t newSpan)
+{
+    MOZ_ASSERT(cx->isThreadLocal(obj));
+    MOZ_ASSERT(oldSpan != newSpan);
+
+    size_t oldCount = dynamicSlotsCount(obj->numFixedSlots(), oldSpan, obj->getClass());
+    size_t newCount = dynamicSlotsCount(obj->numFixedSlots(), newSpan, obj->getClass());
+
+    if (oldSpan < newSpan) {
+        if (oldCount < newCount && !growSlots(cx, obj, oldCount, newCount))
+            return false;
+
+        if (newSpan == oldSpan + 1)
+            obj->initSlotUnchecked(oldSpan, UndefinedValue());
+        else
+            obj->initializeSlotRange(oldSpan, newSpan - oldSpan);
+    } else {
+        /* Trigger write barriers on the old slots before reallocating. */
+        obj->prepareSlotRangeForOverwrite(newSpan, oldSpan);
+        obj->invalidateSlotRange(newSpan, oldSpan - newSpan);
+
+        if (oldCount > newCount)
+            shrinkSlots(cx, obj, oldCount, newCount);
+    }
+
+    return true;
+}
+
+/* static */ bool
+NativeObject::setLastProperty(ThreadSafeContext *cx, HandleNativeObject obj, HandleShape shape)
+{
+    MOZ_ASSERT(cx->isThreadLocal(obj));
+    MOZ_ASSERT(!obj->inDictionaryMode());
+    MOZ_ASSERT(!shape->inDictionary());
+    MOZ_ASSERT(shape->compartment() == obj->compartment());
+    MOZ_ASSERT(shape->numFixedSlots() == obj->numFixedSlots());
+
+    size_t oldSpan = obj->lastProperty()->slotSpan();
+    size_t newSpan = shape->slotSpan();
+
+    if (oldSpan == newSpan) {
+        obj->shape_ = shape;
+        return true;
+    }
+
+    if (!updateSlotsForSpan(cx, obj, oldSpan, newSpan))
+        return false;
+
+    obj->shape_ = shape;
+    return true;
+}
+
+void
+NativeObject::setLastPropertyShrinkFixedSlots(Shape *shape)
+{
+    MOZ_ASSERT(!inDictionaryMode());
+    MOZ_ASSERT(!shape->inDictionary());
+    MOZ_ASSERT(shape->compartment() == compartment());
+    MOZ_ASSERT(lastProperty()->slotSpan() == shape->slotSpan());
+
+    DebugOnly<size_t> oldFixed = numFixedSlots();
+    DebugOnly<size_t> newFixed = shape->numFixedSlots();
+    MOZ_ASSERT(newFixed < oldFixed);
+    MOZ_ASSERT(shape->slotSpan() <= oldFixed);
+    MOZ_ASSERT(shape->slotSpan() <= newFixed);
+    MOZ_ASSERT(dynamicSlotsCount(oldFixed, shape->slotSpan(), getClass()) == 0);
+    MOZ_ASSERT(dynamicSlotsCount(newFixed, shape->slotSpan(), getClass()) == 0);
+
+    shape_ = shape;
+}
+
+/* static */ bool
+NativeObject::setSlotSpan(ThreadSafeContext *cx, HandleNativeObject obj, uint32_t span)
+{
+    MOZ_ASSERT(cx->isThreadLocal(obj));
+    MOZ_ASSERT(obj->inDictionaryMode());
+
+    size_t oldSpan = obj->lastProperty()->base()->slotSpan();
+    if (oldSpan == span)
+        return true;
+
+    if (!updateSlotsForSpan(cx, obj, oldSpan, span))
+        return false;
+
+    obj->lastProperty()->base()->setSlotSpan(span);
+    return true;
+}
+
+// This will not run the garbage collector.  If a nursery cannot accomodate the slot array
+// an attempt will be made to place the array in the tenured area.
+static HeapSlot *
+AllocateSlots(ThreadSafeContext *cx, JSObject *obj, uint32_t nslots)
+{
+#ifdef JSGC_GENERATIONAL
+    if (cx->isJSContext())
+        return cx->asJSContext()->runtime()->gc.nursery.allocateSlots(obj, nslots);
+#endif
+#ifdef JSGC_FJGENERATIONAL
+    if (cx->isForkJoinContext())
+        return cx->asForkJoinContext()->nursery().allocateSlots(obj, nslots);
+#endif
+    return obj->zone()->pod_malloc<HeapSlot>(nslots);
+}
+
+// This will not run the garbage collector.  If a nursery cannot accomodate the slot array
+// an attempt will be made to place the array in the tenured area.
+//
+// If this returns null then the old slots will be left alone.
+static HeapSlot *
+ReallocateSlots(ThreadSafeContext *cx, JSObject *obj, HeapSlot *oldSlots,
+                uint32_t oldCount, uint32_t newCount)
+{
+#ifdef JSGC_GENERATIONAL
+    if (cx->isJSContext()) {
+        return cx->asJSContext()->runtime()->gc.nursery.reallocateSlots(obj, oldSlots,
+                                                                        oldCount, newCount);
+    }
+#endif
+#ifdef JSGC_FJGENERATIONAL
+    if (cx->isForkJoinContext()) {
+        return cx->asForkJoinContext()->nursery().reallocateSlots(obj, oldSlots,
+                                                                  oldCount, newCount);
+    }
+#endif
+    return obj->zone()->pod_realloc<HeapSlot>(oldSlots, oldCount, newCount);
+}
+
+/* static */ bool
+NativeObject::growSlots(ThreadSafeContext *cx, HandleNativeObject obj, uint32_t oldCount, uint32_t newCount)
+{
+    MOZ_ASSERT(cx->isThreadLocal(obj));
+    MOZ_ASSERT(newCount > oldCount);
+    MOZ_ASSERT_IF(!obj->is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
+
+    /*
+     * Slot capacities are determined by the span of allocated objects. Due to
+     * the limited number of bits to store shape slots, object growth is
+     * throttled well before the slot capacity can overflow.
+     */
+    MOZ_ASSERT(newCount < NELEMENTS_LIMIT);
+
+    if (!oldCount) {
+        obj->slots = AllocateSlots(cx, obj, newCount);
+        if (!obj->slots)
+            return false;
+        Debug_SetSlotRangeToCrashOnTouch(obj->slots, newCount);
+        return true;
+    }
+
+    HeapSlot *newslots = ReallocateSlots(cx, obj, obj->slots, oldCount, newCount);
+    if (!newslots)
+        return false;  /* Leave slots at its old size. */
+
+    obj->slots = newslots;
+
+    Debug_SetSlotRangeToCrashOnTouch(obj->slots + oldCount, newCount - oldCount);
+
+    return true;
+}
+
+static void
+FreeSlots(ThreadSafeContext *cx, HeapSlot *slots)
+{
+#ifdef JSGC_GENERATIONAL
+    // Note: threads without a JSContext do not have access to GGC nursery allocated things.
+    if (cx->isJSContext())
+        return cx->asJSContext()->runtime()->gc.nursery.freeSlots(slots);
+#endif
+#ifdef JSGC_FJGENERATIONAL
+    if (cx->isForkJoinContext())
+        return cx->asForkJoinContext()->nursery().freeSlots(slots);
+#endif
+    js_free(slots);
+}
+
+/* static */ void
+NativeObject::shrinkSlots(ThreadSafeContext *cx, HandleNativeObject obj,
+                          uint32_t oldCount, uint32_t newCount)
+{
+    MOZ_ASSERT(cx->isThreadLocal(obj));
+    MOZ_ASSERT(newCount < oldCount);
+
+    if (newCount == 0) {
+        FreeSlots(cx, obj->slots);
+        obj->slots = nullptr;
+        return;
+    }
+
+    MOZ_ASSERT_IF(!obj->is<ArrayObject>(), newCount >= SLOT_CAPACITY_MIN);
+
+    HeapSlot *newslots = ReallocateSlots(cx, obj, obj->slots, oldCount, newCount);
+    if (!newslots)
+        return;  /* Leave slots at its old size. */
+
+    obj->slots = newslots;
+}
+
+/* static */ bool
+NativeObject::sparsifyDenseElement(ExclusiveContext *cx, HandleNativeObject obj, uint32_t index)
+{
+    if (!obj->maybeCopyElementsForWrite(cx))
+        return false;
+
+    RootedValue value(cx, obj->getDenseElement(index));
+    MOZ_ASSERT(!value.isMagic(JS_ELEMENTS_HOLE));
+
+    removeDenseElementForSparseIndex(cx, obj, index);
+
+    uint32_t slot = obj->slotSpan();
+    if (!obj->addDataProperty(cx, INT_TO_JSID(index), slot, JSPROP_ENUMERATE)) {
+        obj->setDenseElement(index, value);
+        return false;
+    }
+
+    MOZ_ASSERT(slot == obj->slotSpan() - 1);
+    obj->initSlot(slot, value);
+
+    return true;
+}
+
+/* static */ bool
+NativeObject::sparsifyDenseElements(js::ExclusiveContext *cx, HandleNativeObject obj)
+{
+    if (!obj->maybeCopyElementsForWrite(cx))
+        return false;
+
+    uint32_t initialized = obj->getDenseInitializedLength();
+
+    /* Create new properties with the value of non-hole dense elements. */
+    for (uint32_t i = 0; i < initialized; i++) {
+        if (obj->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE))
+            continue;
+
+        if (!sparsifyDenseElement(cx, obj, i))
+            return false;
+    }
+
+    if (initialized)
+        obj->setDenseInitializedLength(0);
+
+    /*
+     * Reduce storage for dense elements which are now holes. Explicitly mark
+     * the elements capacity as zero, so that any attempts to add dense
+     * elements will be caught in ensureDenseElements.
+     */
+    if (obj->getDenseCapacity()) {
+        obj->shrinkElements(cx, 0);
+        obj->getElementsHeader()->capacity = 0;
+    }
+
+    return true;
+}
+
+bool
+NativeObject::willBeSparseElements(uint32_t requiredCapacity, uint32_t newElementsHint)
+{
+    MOZ_ASSERT(isNative());
+    MOZ_ASSERT(requiredCapacity > MIN_SPARSE_INDEX);
+
+    uint32_t cap = getDenseCapacity();
+    MOZ_ASSERT(requiredCapacity >= cap);
+
+    if (requiredCapacity >= NELEMENTS_LIMIT)
+        return true;
+
+    uint32_t minimalDenseCount = requiredCapacity / SPARSE_DENSITY_RATIO;
+    if (newElementsHint >= minimalDenseCount)
+        return false;
+    minimalDenseCount -= newElementsHint;
+
+    if (minimalDenseCount > cap)
+        return true;
+
+    uint32_t len = getDenseInitializedLength();
+    const Value *elems = getDenseElements();
+    for (uint32_t i = 0; i < len; i++) {
+        if (!elems[i].isMagic(JS_ELEMENTS_HOLE) && !--minimalDenseCount)
+            return false;
+    }
+    return true;
+}
+
+/* static */ NativeObject::EnsureDenseResult
+NativeObject::maybeDensifySparseElements(js::ExclusiveContext *cx, HandleNativeObject obj)
+{
+    /*
+     * Wait until after the object goes into dictionary mode, which must happen
+     * when sparsely packing any array with more than MIN_SPARSE_INDEX elements
+     * (see PropertyTree::MAX_HEIGHT).
+     */
+    if (!obj->inDictionaryMode())
+        return ED_SPARSE;
+
+    /*
+     * Only measure the number of indexed properties every log(n) times when
+     * populating the object.
+     */
+    uint32_t slotSpan = obj->slotSpan();
+    if (slotSpan != RoundUpPow2(slotSpan))
+        return ED_SPARSE;
+
+    /* Watch for conditions under which an object's elements cannot be dense. */
+    if (!obj->nonProxyIsExtensible() || obj->watched())
+        return ED_SPARSE;
+
+    /*
+     * The indexes in the object need to be sufficiently dense before they can
+     * be converted to dense mode.
+     */
+    uint32_t numDenseElements = 0;
+    uint32_t newInitializedLength = 0;
+
+    RootedShape shape(cx, obj->lastProperty());
+    while (!shape->i