Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 11 Feb 2015 15:01:53 +0100
changeset 255747 db5d46d32a331545a5abfc9f9018037dca3cad59
parent 255746 3fc96df801ffd1ab5a40b632ba5cd30aa23316f2 (current diff)
parent 255719 38058cb42a0ee28016d2cc619568b45249202799 (diff)
child 255748 218de6606929e693c91010cdce65d909806f06d6
push id4610
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:32:55 +0000
treeherdermozilla-beta@4df54044d9ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to fx-team
dom/media/fmp4/MP4Reader.cpp
js/src/jsproxy.h
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -314,17 +314,17 @@ pref("media.cache_readahead_limit", 30);
 // Enable/Disable Gonk Decoder Module
 pref("media.fragmented-mp4.gonk.enabled", true);
 #endif
 // The default number of decoded video frames that are enqueued in
 // MediaDecoderReader's mVideoQueue.
 pref("media.video-queue.default-size", 3);
 
 // optimize images' memory usage
-pref("image.mem.decodeondraw", false);
+pref("image.mem.decodeondraw", true);
 pref("image.mem.allow_locking_in_content_processes", false); /* don't allow image locking */
 // Limit the surface cache to 1/8 of main memory or 128MB, whichever is smaller.
 // Almost everything that was factored into 'max_decoded_image_kb' is now stored
 // in the surface cache.  1/8 of main memory is 32MB on a 256MB device, which is
 // about the same as the old 'max_decoded_image_kb'.
 pref("image.mem.surfacecache.max_size_kb", 131072);  // 128MB
 pref("image.mem.surfacecache.size_factor", 8);  // 1/8 of main memory
 pref("image.mem.surfacecache.discard_factor", 2);  // Discard 1/2 of the surface cache at a time.
--- 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="f47964c89a9c34d9a2e77afa3608d9ec604c3d20">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8c7865486a1b11076b849bbf8f7fccbaffbfafe7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="462a2ef9e98134255c144e373c7392440e3ee03b"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="da777082e02eec11c4b7e27679bdb15f47a44f66"/>
@@ -129,16 +129,16 @@
   <project name="platform/external/icu4c" path="external/icu4c" revision="2bb01561780583cc37bc667f0ea79f48a122d8a2"/>
   <!-- dolphin specific things -->
   <project name="device/sprd" path="device/sprd" revision="8609ba49ab518822691528312b9c87b656164c58"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="4e58336019b5cbcfd134caf55b142236cf986618"/>
   <project name="platform/frameworks/av" path="frameworks/av" revision="4387fe988e5a1001f29ce05fcfda03ed2d32137b"/>
   <project name="platform/hardware/akm" path="hardware/akm" revision="6d3be412647b0eab0adff8a2768736cf4eb68039"/>
   <project groups="invensense" name="platform/hardware/invensense" path="hardware/invensense" revision="e6d9ab28b4f4e7684f6c07874ee819c9ea0002a2"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="865ce3b4a2ba0b3a31421ca671f4d6c5595f8690"/>
-  <project name="kernel/common" path="kernel" revision="b1b623fcc5c8a22f4e9cb30470378496e03c9404"/>
+  <project name="kernel/common" path="kernel" revision="0b4249f88cb8faadfbf80be207af14198dec08e3"/>
   <project name="platform/system/core" path="system/core" revision="53d584d4a4b4316e4de9ee5f210d662f89b44e7e"/>
-  <project name="u-boot" path="u-boot" revision="64b93dbec64421a05bc7ffdbc03917700d171570"/>
+  <project name="u-boot" path="u-boot" revision="6980cf8f8cf9c1d43ff92b7af13425a5ed531d12"/>
   <project name="vendor/sprd/gps" path="vendor/sprd/gps" revision="6974f8e771d4d8e910357a6739ab124768891e8f"/>
   <project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="f56ab768cb9f1ad42fb0809ffec1424b1e693369"/>
   <project name="vendor/sprd/partner" path="vendor/sprd/partner" revision="8649c7145972251af11b0639997edfecabfc7c2e"/>
   <project name="vendor/sprd/proprietories" path="vendor/sprd/proprietories" revision="d2466593022f7078aaaf69026adf3367c2adb7bb"/>
 </manifest>
--- 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="6295c4eb38de793159368aa7f745ef3faf7208aa">
     <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="8c7865486a1b11076b849bbf8f7fccbaffbfafe7"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="462a2ef9e98134255c144e373c7392440e3ee03b"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eb1795a9002eb142ac58c8d68f8f4ba094af07ca"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="2c31ac3a31a340b40ecd9c291df9b9613d3afa72"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="da777082e02eec11c4b7e27679bdb15f47a44f66"/>
   <!-- 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="ac778ae59be38aea284a04c89640b1a11c26a5de">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8c7865486a1b11076b849bbf8f7fccbaffbfafe7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="462a2ef9e98134255c144e373c7392440e3ee03b"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="da777082e02eec11c4b7e27679bdb15f47a44f66"/>
   <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="f47964c89a9c34d9a2e77afa3608d9ec604c3d20">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8c7865486a1b11076b849bbf8f7fccbaffbfafe7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="462a2ef9e98134255c144e373c7392440e3ee03b"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="da777082e02eec11c4b7e27679bdb15f47a44f66"/>
--- 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="6295c4eb38de793159368aa7f745ef3faf7208aa">
     <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="8c7865486a1b11076b849bbf8f7fccbaffbfafe7"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="462a2ef9e98134255c144e373c7392440e3ee03b"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eb1795a9002eb142ac58c8d68f8f4ba094af07ca"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="2c31ac3a31a340b40ecd9c291df9b9613d3afa72"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="da777082e02eec11c4b7e27679bdb15f47a44f66"/>
   <!-- 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="f47964c89a9c34d9a2e77afa3608d9ec604c3d20">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8c7865486a1b11076b849bbf8f7fccbaffbfafe7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="462a2ef9e98134255c144e373c7392440e3ee03b"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="da777082e02eec11c4b7e27679bdb15f47a44f66"/>
--- 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="ac778ae59be38aea284a04c89640b1a11c26a5de">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8c7865486a1b11076b849bbf8f7fccbaffbfafe7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="462a2ef9e98134255c144e373c7392440e3ee03b"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="da777082e02eec11c4b7e27679bdb15f47a44f66"/>
   <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": "8c7865486a1b11076b849bbf8f7fccbaffbfafe7", 
+        "git_revision": "462a2ef9e98134255c144e373c7392440e3ee03b", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "c97ae9ec825f591d669e8b7128b81493b88dcf3f", 
+    "revision": "e6a1cfc70305b3695b694d7e66b03c51f2a45b1f", 
     "repo_path": "integration/gaia-central"
 }
--- 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="ac778ae59be38aea284a04c89640b1a11c26a5de">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8c7865486a1b11076b849bbf8f7fccbaffbfafe7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="462a2ef9e98134255c144e373c7392440e3ee03b"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="da777082e02eec11c4b7e27679bdb15f47a44f66"/>
   <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/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/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="7f2ee9f4cb926684883fc2a2e407045fd9db2199">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8c7865486a1b11076b849bbf8f7fccbaffbfafe7"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="462a2ef9e98134255c144e373c7392440e3ee03b"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="2262d4a77d4f46ab230fd747bb91e9b77bad36cb"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="da777082e02eec11c4b7e27679bdb15f47a44f66"/>
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1646,19 +1646,25 @@ pref("pdfjs.disabled", false);
 // Used by pdf.js to know the first time firefox is run with it installed so it
 // can become the default pdf viewer.
 pref("pdfjs.firstRun", true);
 // The values of preferredAction and alwaysAskBeforeHandling before pdf.js
 // became the default.
 pref("pdfjs.previousHandler.preferredAction", 0);
 pref("pdfjs.previousHandler.alwaysAskBeforeHandling", false);
 
+// Shumway is only bundled in Nightly.
 #ifdef NIGHTLY_BUILD
-// Shumway component (SWF player) is disabled by default. Also see bug 904346.
+// By default, Shumway (SWF player) is only enabled for whitelisted SWFs on Windows + OS X.
+#ifdef UNIX_BUT_NOT_MAC
 pref("shumway.disabled", true);
+#else
+pref("shumway.disabled", false);
+pref("shumway.swf.whitelist", "http://g-ecx.images-amazon.com/*/AiryBasicRenderer*.swf");
+#endif
 #endif
 
 // The maximum amount of decoded image data we'll willingly keep around (we
 // might keep around more than this, but we'll try to get down to this value).
 // (This is intentionally on the high side; see bug 746055.)
 pref("image.mem.max_decoded_image_kb", 256000);
 
 pref("loop.enabled", true);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -872,17 +872,32 @@ function LoadInOtherProcess(browser, loa
     // history into it.
     SessionStore.setTabState(tab, JSON.stringify(tabState));
   }
 }
 
 // Called when a docshell has attempted to load a page in an incorrect process.
 // This function is responsible for loading the page in the correct process.
 function RedirectLoad({ target: browser, data }) {
-  LoadInOtherProcess(browser, data.loadOptions, data.historyIndex);
+  // We should only start the redirection if the browser window has finished
+  // starting up. Otherwise, we should wait until the startup is done.
+  if (gBrowserInit.delayedStartupFinished) {
+    LoadInOtherProcess(browser, data.loadOptions, data.historyIndex);
+  } else {
+    let delayedStartupFinished = (subject, topic) => {
+      if (topic == "browser-delayed-startup-finished" &&
+          subject == window) {
+        Services.obs.removeObserver(delayedStartupFinished, topic);
+        LoadInOtherProcess(browser, data.loadOptions, data.historyIndex);
+      }
+    };
+    Services.obs.addObserver(delayedStartupFinished,
+                             "browser-delayed-startup-finished",
+                             false);
+  }
 }
 
 var gBrowserInit = {
   delayedStartupFinished: false,
 
   onLoad: function() {
     var mustLoadSidebar = false;
 
@@ -901,16 +916,18 @@ var gBrowserInit = {
     LanguageDetectionListener.init();
     BrowserOnClick.init();
     DevEdition.init();
 
     let mm = window.getGroupMessageManager("browsers");
     mm.loadFrameScript("chrome://browser/content/content.js", true);
     mm.loadFrameScript("chrome://browser/content/content-UITour.js", true);
 
+    window.messageManager.addMessageListener("Browser:LoadURI", RedirectLoad);
+
     // initialize observers and listeners
     // and give C++ access to gBrowser
     XULBrowserWindow.init();
     window.QueryInterface(Ci.nsIInterfaceRequestor)
           .getInterface(nsIWebNavigation)
           .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
           .QueryInterface(Ci.nsIInterfaceRequestor)
           .getInterface(Ci.nsIXULWindow)
@@ -918,18 +935,16 @@ var gBrowserInit = {
     window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow =
       new nsBrowserAccess();
 
     if (!gMultiProcessBrowser) {
       // There is a Content:Click message manually sent from content.
       Cc["@mozilla.org/eventlistenerservice;1"]
         .getService(Ci.nsIEventListenerService)
         .addSystemEventListener(gBrowser, "click", contentAreaClick, true);
-    } else {
-      gBrowser.updateBrowserRemoteness(gBrowser.selectedBrowser, true);
     }
 
     // hook up UI through progress listener
     gBrowser.addProgressListener(window.XULBrowserWindow);
     gBrowser.addTabsProgressListener(window.TabsProgressListener);
 
     // setup simple gestures support
     gGestureSupport.init(true);
@@ -1176,23 +1191,39 @@ var gBrowserInit = {
         // so that we don't disrupt startup
         try {
           gBrowser.loadTabs(specs, false, true);
         } catch (e) {}
       }
       else if (uriToLoad instanceof XULElement) {
         // swap the given tab with the default about:blank tab and then close
         // the original tab in the other window.
+        let tabToOpen = uriToLoad;
 
         // Stop the about:blank load
         gBrowser.stop();
         // make sure it has a docshell
         gBrowser.docShell;
 
-        gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, uriToLoad);
+        // If the browser that we're swapping in was remote, then we'd better
+        // be able to support remote browsers, and then make our selectedTab
+        // remote.
+        try {
+          if (tabToOpen.linkedBrowser.isRemoteBrowser) {
+            if (!gMultiProcessBrowser) {
+              throw new Error("Cannot drag a remote browser into a window " +
+                              "without the remote tabs load context.");
+            }
+
+            gBrowser.updateBrowserRemoteness(gBrowser.selectedBrowser, true);
+          }
+          gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, tabToOpen);
+        } catch(e) {
+          Cu.reportError(e);
+        }
       }
       // window.arguments[2]: referrer (nsIURI)
       //                 [3]: postData (nsIInputStream)
       //                 [4]: allowThirdPartyFixup (bool)
       else if (window.arguments.length >= 3) {
         loadURI(uriToLoad, window.arguments[2], window.arguments[3] || null,
                 window.arguments[4] || false);
         window.focus();
@@ -1211,17 +1242,16 @@ var gBrowserInit = {
 
     Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
     window.messageManager.addMessageListener("Browser:URIFixup", gKeywordURIFixup);
-    window.messageManager.addMessageListener("Browser:LoadURI", RedirectLoad);
 
     BrowserOffline.init();
     OfflineApps.init();
     IndexedDBPromptHelper.init();
 #ifdef E10S_TESTING_ONLY
     gRemoteTabsUI.init();
 #endif
 
@@ -4077,16 +4107,23 @@ var XULBrowserWindow = {
     var securityUI = gBrowser.securityUI;
     this.onSecurityChange(null, null, securityUI.state);
   },
 
   setJSStatus: function () {
     // unsupported
   },
 
+  forceInitialBrowserRemote: function() {
+    let initBrowser =
+      document.getAnonymousElementByAttribute(gBrowser, "anonid", "initialBrowser");
+    gBrowser.updateBrowserRemoteness(initBrowser, true);
+    return initBrowser.frameLoader.tabParent;
+  },
+
   setDefaultStatus: function (status) {
     this.defaultStatus = status;
     this.updateStatusField();
   },
 
   setOverLink: function (url, anchorElt) {
     // Encode bidirectional formatting characters.
     // (RFC 3987 sections 3.2 and 4.1 paragraph 6)
@@ -4732,17 +4769,17 @@ var TabsProgressListener = {
   }
 }
 
 function nsBrowserAccess() { }
 
 nsBrowserAccess.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow, Ci.nsISupports]),
 
-  _openURIInNewTab: function(aURI, aReferrer, aIsPrivate, aIsExternal) {
+  _openURIInNewTab: function(aURI, aReferrer, aIsPrivate, aIsExternal, aForceNotRemote=false) {
     let win, needToFocusWin;
 
     // try the current window.  if we're in a popup, fall back on the most recent browser window
     if (window.toolbar.visible)
       win = window;
     else {
       win = RecentWindow.getMostRecentBrowserWindow({private: aIsPrivate});
       needToFocusWin = true;
@@ -4759,29 +4796,44 @@ nsBrowserAccess.prototype = {
       return win.gBrowser.selectedBrowser;
     }
 
     let loadInBackground = gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground");
 
     let tab = win.gBrowser.loadOneTab(aURI ? aURI.spec : "about:blank", {
                                       referrerURI: aReferrer,
                                       fromExternal: aIsExternal,
-                                      inBackground: loadInBackground});
+                                      inBackground: loadInBackground,
+                                      forceNotRemote: aForceNotRemote});
     let browser = win.gBrowser.getBrowserForTab(tab);
 
     if (needToFocusWin || (!loadInBackground && aIsExternal))
       win.focus();
 
     return browser;
   },
 
   openURI: function (aURI, aOpener, aWhere, aContext) {
+    // This function should only ever be called if we're opening a URI
+    // from a non-remote browser window (via nsContentTreeOwner).
+    if (aOpener && Cu.isCrossProcessWrapper(aOpener)) {
+      Cu.reportError("nsBrowserAccess.openURI was passed a CPOW for aOpener. " +
+                     "openURI should only ever be called from non-remote browsers.");
+      throw Cr.NS_ERROR_FAILURE;
+    }
+
     var newWindow = null;
     var isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
 
+    if (aOpener && isExternal) {
+      Cu.reportError("nsBrowserAccess.openURI did not expect an opener to be " +
+                     "passed if the context is OPEN_EXTERNAL.");
+      throw Cr.NS_ERROR_FAILURE;
+    }
+
     if (isExternal && aURI && aURI.schemeIs("chrome")) {
       dump("use --chrome command-line option to load external chrome urls\n");
       return null;
     }
 
     if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW) {
       if (isExternal &&
           gPrefService.prefHasUserValue("browser.link.open_newwindow.override.external"))
@@ -4800,17 +4852,24 @@ nsBrowserAccess.prototype = {
           features += ",private";
         }
         // Pass all params to openDialog to ensure that "url" isn't passed through
         // loadOneOrMoreURIs, which splits based on "|"
         newWindow = openDialog(getBrowserURL(), "_blank", features, url, null, null, null);
         break;
       case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB :
         let referrer = aOpener ? makeURI(aOpener.location.href) : null;
-        let browser = this._openURIInNewTab(aURI, referrer, isPrivate, isExternal);
+        // If we have an opener, that means that the caller is expecting access
+        // to the nsIDOMWindow of the opened tab right away. For e10s windows,
+        // this means forcing the newly opened browser to be non-remote so that
+        // we can hand back the nsIDOMWindow. The XULBrowserWindow.shouldLoadURI
+        // will do the job of shuttling off the newly opened browser to run in
+        // the right process once it starts loading a URI.
+        let forceNotRemote = !!aOpener;
+        let browser = this._openURIInNewTab(aURI, referrer, isPrivate, isExternal, forceNotRemote);
         if (browser)
           newWindow = browser.contentWindow;
         break;
       default : // OPEN_CURRENTWINDOW or an illegal value
         newWindow = content;
         if (aURI) {
           let referrer = aOpener ? makeURI(aOpener.location.href) : null;
           let loadflags = isExternal ?
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -327,19 +327,19 @@ nsContextMenu.prototype = {
     if (this.inFrame) {
       if (mimeTypeIsTextBased(this.target.ownerDocument.contentType))
         this.isFrameImage.removeAttribute('hidden');
       else
         this.isFrameImage.setAttribute('hidden', 'true');
     }
 
     // BiDi UI
-    this.showItem("context-sep-bidi", top.gBidiUI);
+    this.showItem("context-sep-bidi", !this.onNumeric && top.gBidiUI);
     this.showItem("context-bidi-text-direction-toggle",
-                  this.onTextInput && top.gBidiUI);
+                  this.onTextInput && !this.onNumeric && top.gBidiUI);
     this.showItem("context-bidi-page-direction-toggle",
                   !this.onTextInput && top.gBidiUI);
 
     // SocialMarks. Marks does not work with text selections, only links. If
     // there is more than MENU_LIMIT providers, we show a submenu for them,
     // otherwise we have a menuitem per provider (added in SocialMarks class).
     let markProviders = SocialMarks.getProviders();
     let enablePageMarks = markProviders.length > 0 && !(this.onLink || this.onImage
@@ -558,16 +558,17 @@ nsContextMenu.prototype = {
     this.onImage           = false;
     this.onLoadedImage     = false;
     this.onCompletedImage  = false;
     this.imageDescURL      = "";
     this.onCanvas          = false;
     this.onVideo           = false;
     this.onAudio           = false;
     this.onTextInput       = false;
+    this.onNumeric         = false;
     this.onKeywordField    = false;
     this.mediaURL          = "";
     this.onLink            = false;
     this.onMailtoLink      = false;
     this.onSaveableLink    = false;
     this.link              = null;
     this.linkURL           = "";
     this.linkURI           = null;
@@ -653,16 +654,17 @@ nsContextMenu.prototype = {
         this.onAudio = true;
         let mediaURL = this.target.currentSrc || this.target.src;
         if (this.isMediaURLReusable(mediaURL)) {
           this.mediaURL = mediaURL;
         }
       }
       else if (editFlags & (SpellCheckHelper.INPUT | SpellCheckHelper.TEXTAREA)) {
         this.onTextInput = (editFlags & SpellCheckHelper.TEXTINPUT) !== 0;
+        this.onNumeric = (editFlags & SpellCheckHelper.NUMERIC) !== 0;
         this.onEditableArea = (editFlags & SpellCheckHelper.EDITABLE) !== 0;
         if (this.onEditableArea) {
           if (this.isRemote) {
             InlineSpellCheckerUI.initFromRemote(gContextMenuContentData.spellInfo);
           }
           else {
             InlineSpellCheckerUI.init(this.target.QueryInterface(Ci.nsIDOMNSEditableElement).editor);
             InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1361,44 +1361,47 @@
         <parameter name="aLoadInBackground"/>
         <parameter name="aAllowThirdPartyFixup"/>
         <body>
           <![CDATA[
             var aFromExternal;
             var aRelatedToCurrent;
             var aAllowMixedContent;
             var aSkipAnimation;
+            var aForceNotRemote;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aReferrerURI          = params.referrerURI;
               aCharset              = params.charset;
               aPostData             = params.postData;
               aLoadInBackground     = params.inBackground;
               aAllowThirdPartyFixup = params.allowThirdPartyFixup;
               aFromExternal         = params.fromExternal;
               aRelatedToCurrent     = params.relatedToCurrent;
               aAllowMixedContent    = params.allowMixedContent;
               aSkipAnimation        = params.skipAnimation;
+              aForceNotRemote       = params.forceNotRemote;
             }
 
             var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
                          Services.prefs.getBoolPref("browser.tabs.loadInBackground");
             var owner = bgLoad ? null : this.selectedTab;
             var tab = this.addTab(aURI, {
                                   referrerURI: aReferrerURI,
                                   charset: aCharset,
                                   postData: aPostData,
                                   ownerTab: owner,
                                   allowThirdPartyFixup: aAllowThirdPartyFixup,
                                   fromExternal: aFromExternal,
                                   relatedToCurrent: aRelatedToCurrent,
                                   skipAnimation: aSkipAnimation,
-                                  allowMixedContent: aAllowMixedContent });
+                                  allowMixedContent: aAllowMixedContent,
+                                  forceNotRemote: aForceNotRemote });
             if (!bgLoad)
               this.selectedTab = tab;
 
             return tab;
          ]]>
         </body>
       </method>
 
@@ -1667,29 +1670,31 @@
         <parameter name="aAllowThirdPartyFixup"/>
         <body>
           <![CDATA[
             const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
             var aFromExternal;
             var aRelatedToCurrent;
             var aSkipAnimation;
             var aAllowMixedContent;
+            var aForceNotRemote;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aReferrerURI          = params.referrerURI;
               aCharset              = params.charset;
               aPostData             = params.postData;
               aOwner                = params.ownerTab;
               aAllowThirdPartyFixup = params.allowThirdPartyFixup;
               aFromExternal         = params.fromExternal;
               aRelatedToCurrent     = params.relatedToCurrent;
               aSkipAnimation        = params.skipAnimation;
               aAllowMixedContent    = params.allowMixedContent;
+              aForceNotRemote       = params.forceNotRemote;
             }
 
             // if we're adding tabs, we're past interrupt mode, ditch the owner
             if (this.mCurrentTab.owner)
               this.mCurrentTab.owner = null;
 
             var t = document.createElementNS(NS_XUL, "tab");
 
@@ -1697,16 +1702,17 @@
 
             t.setAttribute("crop", "end");
             t.setAttribute("onerror", "this.removeAttribute('image');");
             t.className = "tabbrowser-tab";
 
             // The new browser should be remote if this is an e10s window and
             // the uri to load can be loaded remotely.
             let remote = gMultiProcessBrowser &&
+                         !aForceNotRemote &&
                          E10SUtils.canLoadURIInProcess(aURI, Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT);
             if (remote)
               t.setAttribute("remote", "true");
 
             this.tabContainer._unlockTabSizing();
 
             // When overflowing, new tabs are scrolled into view smoothly, which
             // doesn't go well together with the width transition. So we skip the
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -208,17 +208,16 @@ skip-if = e10s # Bug 1056146 - zoom test
 skip-if = e10s # Bug 1093373 - relies on browser.sessionHistory
 [browser_bug556061.js]
 [browser_bug559991.js]
 [browser_bug561623.js]
 skip-if = e10s
 [browser_bug561636.js]
 skip-if = e10s # Bug 1093677 - automated form submission from the test doesn't seem to quite work yet
 [browser_bug562649.js]
-skip-if = e10s # Bug 940195 - XULBrowserWindow.isBusy is false as a remote tab starts loading
 [browser_bug563588.js]
 [browser_bug565575.js]
 skip-if = e10s
 [browser_bug565667.js]
 skip-if = toolkit != "cocoa"
 [browser_bug567306.js]
 skip-if = e10s # Bug XXX - Needs some massaging to run in e10s
 [browser_bug575561.js]
@@ -255,35 +254,34 @@ skip-if = buildapp == 'mulet' || e10s # 
 [browser_bug655584.js]
 skip-if = e10s
 [browser_bug664672.js]
 [browser_bug676619.js]
 skip-if = buildapp == 'mulet' || os == "mac" # mac: Intermittent failures, bug 925225
 [browser_bug678392.js]
 skip-if = e10s # bug 1102331 - does focus things on the content window which break in e10s mode
 [browser_bug710878.js]
-skip-if = e10s # Bug 1100653 - test uses waitForFocus on content
 [browser_bug719271.js]
 skip-if = e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
 [browser_bug724239.js]
 [browser_bug734076.js]
 skip-if = e10s # Bug 1093155 - tries to use context menu from browser-chrome and gets in a mess when in e10s mode
 [browser_bug735471.js]
 [browser_bug749738.js]
-skip-if = e10s # Bug 921935 - focusmanager issues with e10s
 [browser_bug763468_perwindowpb.js]
 skip-if = e10s
 [browser_bug767836_perwindowpb.js]
 [browser_bug771331.js]
 [browser_bug783614.js]
 [browser_bug817947.js]
 [browser_bug822367.js]
 [browser_bug832435.js]
 [browser_bug839103.js]
 [browser_bug880101.js]
+skip-if = e10s # Bug 1126316 - New e10s windows erroneously fire initial about:blank location through nsIWebProgressListener
 [browser_bug882977.js]
 [browser_bug902156.js]
 [browser_bug906190.js]
 skip-if = buildapp == "mulet" || e10s # Bug 1093642 - test manipulates content and relies on content focus
 [browser_bug970746.js]
 skip-if = e10s # Bug 1093155 - tries to use context menu from browser-chrome and gets in a mess when in e10s mode
 [browser_bug1015721.js]
 skip-if = os == 'win' || e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
@@ -422,16 +420,18 @@ skip-if = os == "linux" || os == "mac" #
 [browser_tabfocus.js]
 skip-if = e10s # Bug 921935 - focusmanager issues with e10s (test calls getFocusedElementForWindow with a content window)
 [browser_tabkeynavigation.js]
 skip-if = e10s
 [browser_tabopen_reflows.js]
 [browser_tabs_isActive.js]
 skip-if = e10s # Bug 1100664 - test relies on linkedBrowser.docShell
 [browser_tabs_owner.js]
+[browser_testOpenNewRemoteTabsFromNonRemoteBrowsers.js]
+skip-if = !e10s && os == "linux" # Bug 994541 - Need OMTC enabled by default on Linux, or else we get blocked by an alert dialog when opening e10s window.
 [browser_trackingUI.js]
 support-files =
   trackingPage.html
   benignPage.html
 [browser_typeAheadFind.js]
 skip-if = buildapp == 'mulet' || e10s # Bug 921935 - focusmanager issues with e10s (test calls waitForFocus)
 [browser_unloaddialogs.js]
 skip-if = e10s # Bug 1100700 - test relies on unload event firing on closed tabs, which it doesn't
--- a/browser/base/content/test/general/browser_bug562649.js
+++ b/browser/base/content/test/general/browser_bug562649.js
@@ -1,16 +1,15 @@
 function test() {
   const URI = "data:text/plain,bug562649";
   browserDOMWindow.openURI(makeURI(URI),
                            null,
                            Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
                            Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
 
-  ok(XULBrowserWindow.isBusy, "window is busy loading a page");
   is(gBrowser.userTypedValue, URI, "userTypedValue matches test URI");
   is(gURLBar.value, URI, "location bar value matches test URI");
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.removeCurrentTab();
   is(gBrowser.userTypedValue, URI, "userTypedValue matches test URI after switching tabs");
   is(gURLBar.value, URI, "location bar value matches test URI after switching tabs");
 
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_testOpenNewRemoteTabsFromNonRemoteBrowsers.js
@@ -0,0 +1,124 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const OPEN_LOCATION_PREF = "browser.link.open_newwindow";
+const NON_REMOTE_PAGE = "about:welcomeback";
+
+Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
+
+function frame_script() {
+  content.document.body.innerHTML = `
+    <a href="about:home" target="_blank" id="testAnchor">Open a window</a>
+  `;
+
+  let element = content.document.getElementById("testAnchor");
+  element.click();
+}
+
+/**
+ * Takes some browser in some window, and forces that browser
+ * to become non-remote, and then navigates it to a page that
+ * we're not supposed to be displaying remotely. Returns a
+ * Promise that resolves when the browser is no longer remote.
+ */
+function prepareNonRemoteBrowser(aWindow, browser) {
+  browser.loadURI(NON_REMOTE_PAGE);
+  return waitForDocLoadComplete(browser);
+}
+
+registerCleanupFunction(() => {
+  Services.prefs.clearUserPref(OPEN_LOCATION_PREF);
+});
+
+/**
+ * Test that if we open a new tab from a link in a non-remote
+ * browser in an e10s window, that the new tab will load properly.
+ */
+add_task(function* test_new_tab() {
+  let normalWindow = yield promiseOpenAndLoadWindow({
+    remote: true
+  }, true);
+  let privateWindow = yield promiseOpenAndLoadWindow({
+    remote: true,
+    private: true,
+  }, true);
+
+  for (let testWindow of [normalWindow, privateWindow]) {
+    yield promiseWaitForFocus(testWindow);
+    let testBrowser = testWindow.gBrowser.selectedBrowser;
+    info("Preparing non-remote browser");
+    yield prepareNonRemoteBrowser(testWindow, testBrowser);
+    info("Non-remote browser prepared - sending frame script");
+
+    // Get our framescript ready
+    let mm = testBrowser.messageManager;
+    mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", true);
+
+    let tabOpenEvent = yield waitForNewTabEvent(testWindow.gBrowser);
+    let newTab = tabOpenEvent.target;
+
+    yield promiseTabLoadEvent(newTab);
+
+    // Our framescript opens to about:home which means that the
+    // tab should eventually become remote.
+    ok(newTab.linkedBrowser.isRemoteBrowser,
+       "The opened browser never became remote.");
+
+    testWindow.gBrowser.removeTab(newTab);
+  }
+
+  normalWindow.close();
+  privateWindow.close();
+});
+
+/**
+ * Test that if we open a new window from a link in a non-remote
+ * browser in an e10s window, that the new window is not an e10s
+ * window. Also tests with a private browsing window.
+ */
+add_task(function* test_new_window() {
+  let normalWindow = yield promiseOpenAndLoadWindow({
+    remote: true
+  }, true);
+  let privateWindow = yield promiseOpenAndLoadWindow({
+    remote: true,
+    private: true,
+  }, true);
+
+  // Fiddle with the prefs so that we open target="_blank" links
+  // in new windows instead of new tabs.
+  Services.prefs.setIntPref(OPEN_LOCATION_PREF,
+                            Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW);
+
+  for (let testWindow of [normalWindow, privateWindow]) {
+    yield promiseWaitForFocus(testWindow);
+    let testBrowser = testWindow.gBrowser.selectedBrowser;
+    yield prepareNonRemoteBrowser(testWindow, testBrowser);
+
+    // Get our framescript ready
+    let mm = testBrowser.messageManager;
+    mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", true);
+
+    // Click on the link in the browser, and wait for the new window.
+    let {subject: newWindow} =
+      yield promiseTopicObserved("browser-delayed-startup-finished");
+
+    is(PrivateBrowsingUtils.isWindowPrivate(testWindow),
+       PrivateBrowsingUtils.isWindowPrivate(newWindow),
+       "Private browsing state of new window does not match the original!");
+
+    let newTab = newWindow.gBrowser.selectedTab;
+
+    yield promiseTabLoadEvent(newTab);
+
+    // Our framescript opens to about:home which means that the
+    // tab should eventually become remote.
+    ok(newTab.linkedBrowser.isRemoteBrowser,
+       "The opened browser never became remote.");
+    newWindow.close();
+  }
+
+  normalWindow.close();
+  privateWindow.close();
+});
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -118,16 +118,33 @@ function promiseWaitForEvent(object, eve
       resolve(event);
     }
 
     info("Waiting for " + eventName);
     object.addEventListener(eventName, listener, capturing, chrome);
   });
 }
 
+/**
+ * Allows setting focus on a window, and waiting for that window to achieve
+ * focus.
+ *
+ * @param aWindow
+ *        The window to focus and wait for.
+ *
+ * @return {Promise}
+ * @resolves When the window is focused.
+ * @rejects Never.
+ */
+function promiseWaitForFocus(aWindow) {
+  return new Promise((resolve) => {
+    waitForFocus(resolve, aWindow);
+  });
+}
+
 function getTestPlugin(aName) {
   var pluginName = aName || "Test Plug-in";
   var ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
   var tags = ph.getPluginTags();
 
   // Find the test plugin
   for (var i = 0; i < tags.length; i++) {
     if (tags[i].name == pluginName)
@@ -567,16 +584,31 @@ function promiseTabLoadEvent(tab, url, e
   }, 30000);
 
   tab.linkedBrowser.addEventListener(eventType, handle, true, true);
   if (url)
     tab.linkedBrowser.loadURI(url);
   return deferred.promise;
 }
 
+/**
+ * Returns a Promise that resolves once a new tab has been opened in
+ * a xul:tabbrowser.
+ *
+ * @param aTabBrowser
+ *        The xul:tabbrowser to monitor for a new tab.
+ * @return {Promise}
+ *        Resolved when the new tab has been opened.
+ * @resolves to the TabOpen event that was fired.
+ * @rejects Never.
+ */
+function waitForNewTabEvent(aTabBrowser) {
+  return promiseWaitForEvent(aTabBrowser.tabContainer, "TabOpen");
+}
+
 function assertWebRTCIndicatorStatus(expected) {
   let ui = Cu.import("resource:///modules/webrtcUI.jsm", {}).webrtcUI;
   let expectedState = expected ? "visible" : "hidden";
   let msg = "WebRTC indicator " + expectedState;
   is(ui.showGlobalIndicator, !!expected, msg);
 
   let expectVideo = false, expectAudio = false, expectScreen = false;
   if (expected) {
@@ -740,8 +772,31 @@ function promiseAutocompleteResultPopup(
   waitForFocus(() => {
     win.gURLBar.focus();
     win.gURLBar.value = inputText;
     win.gURLBar.controller.startSearch(inputText);
   }, win);
 
   return promiseSearchComplete(win);
 }
+
+/**
+ * Allows waiting for an observer notification once.
+ *
+ * @param aTopic
+ *        Notification topic to observe.
+ *
+ * @return {Promise}
+ * @resolves An object with subject and data properties from the observed
+ *           notification.
+ * @rejects Never.
+ */
+function promiseTopicObserved(aTopic)
+{
+  return new Promise((resolve) => {
+    Services.obs.addObserver(
+      function PTO_observe(aSubject, aTopic, aData) {
+        Services.obs.removeObserver(PTO_observe, aTopic);
+        resolve({subject: aSubject, data: aData});
+      }, aTopic, false);
+  });
+}
+
--- a/browser/base/content/test/general/test_contextmenu_input.html
+++ b/browser/base/content/test/general/test_contextmenu_input.html
@@ -155,17 +155,18 @@ function runTest(testNum) {
         input.type = 'password';
         openContextMenuFor(input); // Invoke context menu for next test.
         break;
 
     case 7: // password
     case 8: // email
     case 9: // url
     case 10: // tel
-        // Context menu for tel, password, email and url input fields.
+    case 11: // type='number'
+        // Context menu for tel, password, email, url and number input fields.
         checkContextMenu(["context-undo",        false,
                           "---",                 null,
                           "context-cut",         false,
                           "context-copy",        false,
                           "context-paste",       null, // ignore clipboard state
                           "context-delete",      false,
                           "---",                 null,
                           "context-selectall",   false,
@@ -176,25 +177,26 @@ function runTest(testNum) {
 
         if (testNum == 7) {
           input.type = 'email';
         } else if (testNum == 8) {
           input.type = 'url';
         } else if (testNum == 9) {
           input.type = 'tel';
         } else if (testNum == 10) {
+          input.type = 'number';
+        } else if (testNum == 11) {
           input.type = 'date';
         }
 
         openContextMenuFor(input); // Invoke context menu for next test.
         break;
 
-    case 11: // type='date'
-    case 12: // type='time'
-    case 13: // type='number'
+    case 12: // type='date'
+    case 13: // type='time'
     case 14: // type='color'
     case 15: // type='range'
         checkContextMenu(["context-navigation", null,
                               ["context-back",         false,
                                "context-forward",      false,
                                "context-reload",       true,
                                "context-bookmarkpage", true], null,
                           "---",                  null,
@@ -204,20 +206,18 @@ function runTest(testNum) {
                           "context-selectall",    null,
                           "---",                  null,
                           "context-viewsource",   true,
                           "context-viewinfo",     true,
                           "---",                 null,
                           "context-inspect",     true]);
         closeContextMenu();
 
-        if (testNum == 11) {
+        if (testNum == 12) {
           input.type = 'time';
-        } else if (testNum == 12) {
-          input.type = 'number';
         } else if (testNum == 13) {
           input.type = 'color';
         } else if (testNum == 14) {
           input.type = 'range';
         } else {
           input.type = 'search';
         }
 
--- a/browser/components/sessionstore/test/browser_394759_behavior.js
+++ b/browser/components/sessionstore/test/browser_394759_behavior.js
@@ -34,21 +34,33 @@ function test() {
 
     // hack to force window to be considered a popup (toolbar=no didn't work)
     let winData = windowsToOpen.shift();
     let settings = "chrome,dialog=no," +
                    (winData.isPopup ? "all=no" : "all");
     let url = "http://example.com/?window=" + windowsToOpen.length;
 
     provideWindow(function onTestURLLoaded(win) {
-      win.close();
-      // Give it time to close
-      executeSoon(function() {
-        openWindowRec(windowsToOpen, expectedResults, recCallback);
-      });
+      let tabReady = () => {
+        win.close();
+        // Give it time to close
+        executeSoon(function() {
+          openWindowRec(windowsToOpen, expectedResults, recCallback);
+        });
+      };
+
+      if (win.gMultiProcessBrowser) {
+        let tab = win.gBrowser.selectedTab;
+        tab.addEventListener("SSTabRestored", function onTabRestored() {
+          tab.removeEventListener("SSTabRestored", onTabRestored);
+          tabReady();
+        });
+      } else {
+        tabReady();
+      }
     }, url, settings);
   }
 
   let windowsToOpen = [{isPopup: false},
                        {isPopup: false},
                        {isPopup: true},
                        {isPopup: true},
                        {isPopup: true}];
--- a/browser/components/sessionstore/test/browser_423132.js
+++ b/browser/components/sessionstore/test/browser_423132.js
@@ -25,51 +25,60 @@ function test() {
   newWin.addEventListener("load", function (aEvent) {
     newWin.removeEventListener("load", arguments.callee, false);
 
     // Wait for sessionstore to be ready to restore this window
     executeSoon(function() {
       newWin.gBrowser.loadURI(testURL, null, null);
 
       promiseBrowserLoaded(newWin.gBrowser.selectedBrowser).then(() => {
-        // get the sessionstore state for the window
-        TabState.flush(newWin.gBrowser.selectedBrowser);
-        let state = ss.getWindowState(newWin);
+        let ready = () => {
+          // get the sessionstore state for the window
+          TabState.flush(newWin.gBrowser.selectedBrowser);
+          let state = ss.getWindowState(newWin);
 
-        // verify our cookie got set during pageload
-        let e = cs.enumerator;
-        let cookie;
-        let i = 0;
-        while (e.hasMoreElements()) {
-          cookie = e.getNext().QueryInterface(Ci.nsICookie);
-          i++;
-        }
-        is(i, 1, "expected one cookie");
+          // verify our cookie got set during pageload
+          let e = cs.enumerator;
+          let cookie;
+          let i = 0;
+          while (e.hasMoreElements()) {
+            cookie = e.getNext().QueryInterface(Ci.nsICookie);
+            i++;
+          }
+          is(i, 1, "expected one cookie");
 
-        // remove the cookie
-        cs.removeAll();
+          // remove the cookie
+          cs.removeAll();
+
+          // restore the window state
+          ss.setWindowState(newWin, state, true);
 
-        // restore the window state
-        ss.setWindowState(newWin, state, true);
+          // at this point, the cookie should be restored...
+          e = cs.enumerator;
+          let cookie2;
+          while (e.hasMoreElements()) {
+            cookie2 = e.getNext().QueryInterface(Ci.nsICookie);
+            if (cookie.name == cookie2.name)
+              break;
+          }
+          is(cookie.name, cookie2.name, "cookie name successfully restored");
+          is(cookie.value, cookie2.value, "cookie value successfully restored");
+          is(cookie.path, cookie2.path, "cookie path successfully restored");
 
-        // at this point, the cookie should be restored...
-        e = cs.enumerator;
-        let cookie2;
-        while (e.hasMoreElements()) {
-          cookie2 = e.getNext().QueryInterface(Ci.nsICookie);
-          if (cookie.name == cookie2.name)
-            break;
+          // clean up
+          if (gPrefService.prefHasUserValue("browser.sessionstore.interval"))
+            gPrefService.clearUserPref("browser.sessionstore.interval");
+          cs.removeAll();
+          newWin.close();
+          finish();
+        };
+
+        if (newWin.gMultiProcessBrowser) {
+          let tab = newWin.gBrowser.selectedTab;
+          promiseTabRestored(tab).then(ready);
+        } else {
+          ready();
         }
-        is(cookie.name, cookie2.name, "cookie name successfully restored");
-        is(cookie.value, cookie2.value, "cookie value successfully restored");
-        is(cookie.path, cookie2.path, "cookie path successfully restored");
-
-        // clean up
-        if (gPrefService.prefHasUserValue("browser.sessionstore.interval"))
-          gPrefService.clearUserPref("browser.sessionstore.interval");
-        cs.removeAll();
-        newWin.close();
-        finish();
       }, true, testURL);
     });
   }, false);
 }
 
--- a/browser/devtools/canvasdebugger/canvasdebugger.js
+++ b/browser/devtools/canvasdebugger/canvasdebugger.js
@@ -411,20 +411,27 @@ let SnapshotsListView = Heritage.extend(
     fp.init(window, L10N.getStr("snapshotsList.saveDialogTitle"), Ci.nsIFilePicker.modeOpen);
     fp.appendFilter(L10N.getStr("snapshotsList.saveDialogJSONFilter"), "*.json");
     fp.appendFilter(L10N.getStr("snapshotsList.saveDialogAllFilter"), "*.*");
 
     if (fp.show() != Ci.nsIFilePicker.returnOK) {
       return;
     }
 
-    let channel = NetUtil.newChannel(fp.file);
+    let channel = NetUtil.newChannel2(fp.file,
+                                      null,
+                                      null,
+                                      window.document,
+                                      null, // aLoadingPrincipal
+                                      null, // aTriggeringPrincipal
+                                      Ci.nsILoadInfo.SEC_NORMAL,
+                                      Ci.nsIContentPolicy.TYPE_OTHER);
     channel.contentType = "text/plain";
 
-    NetUtil.asyncFetch(channel, (inputStream, status) => {
+    NetUtil.asyncFetch2(channel, (inputStream, status) => {
       if (!Components.isSuccessCode(status)) {
         console.error("Could not import recorded animation frame snapshot file.");
         return;
       }
       try {
         let string = NetUtil.readInputStreamToString(inputStream, inputStream.available());
         var data = JSON.parse(string);
       } catch (e) {
--- a/browser/devtools/profiler/ui-recordings.js
+++ b/browser/devtools/profiler/ui-recordings.js
@@ -321,20 +321,27 @@ function saveRecordingToFile(recordingIt
  *        The file to import the data from.
  * @return object
  *         A promise that is resolved once importing finishes, or rejected
  *         if there was an error.
  */
 function loadRecordingFromFile(file) {
   let deferred = promise.defer();
 
-  let channel = NetUtil.newChannel(file);
+  let channel = NetUtil.newChannel2(file,
+                                    null,
+                                    null,
+                                    window.document,
+                                    null,  // aLoadingPrincipal
+                                    null,  // aTriggeringPrincipal
+                                    Ci.nsILoadInfo.SEC_NORMAL,
+                                    Ci.nsIContentPolicy.TYPE_OTHER);
   channel.contentType = "text/plain";
 
-  NetUtil.asyncFetch(channel, (inputStream, status) => {
+  NetUtil.asyncFetch2(channel, (inputStream, status) => {
     if (!Components.isSuccessCode(status)) {
       deferred.reject(new Error("Could not import recording data file."));
       return;
     }
     try {
       let string = NetUtil.readInputStreamToString(inputStream, inputStream.available());
       var recordingData = JSON.parse(string);
     } catch (e) {
--- a/browser/devtools/projecteditor/test/head.js
+++ b/browser/devtools/projecteditor/test/head.js
@@ -255,32 +255,37 @@ function getTempFile(path) {
 
 // https://developer.mozilla.org/en-US/Add-ons/Code_snippets/File_I_O#Writing_to_a_file
 function* getFileData(file) {
   if (typeof file === "string") {
     file = new FileUtils.File(file);
   }
   let def = promise.defer();
 
-  NetUtil.asyncFetch(file, function(inputStream, status) {
+  NetUtil.asyncFetch2(file, function(inputStream, status) {
     if (!Components.isSuccessCode(status)) {
       info("ERROR READING TEMP FILE", status);
     }
 
     // Detect if an empty file is loaded
     try {
       inputStream.available();
     } catch(e) {
       def.resolve("");
       return;
     }
 
     var data = NetUtil.readInputStreamToString(inputStream, inputStream.available());
     def.resolve(data);
-  });
+  },
+  null,      // aLoadingNode
+  Services.scriptSecurityManager.getSystemPrincipal(),
+  null,      // aTriggeringPrincipal
+  Ci.nsILoadInfo.SEC_NORMAL,
+  Ci.nsIContentPolicy.TYPE_OTHER);
 
   return def.promise;
 }
 
 function onceEditorCreated(projecteditor) {
   let def = promise.defer();
   projecteditor.once("onEditorCreated", (editor) => {
     def.resolve(editor);
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -1128,22 +1128,29 @@ var Scratchpad = {
    *        Optional function you want to call when file load completes. It will
    *        get the following arguments:
    *        1) the nsresult status code for the import operation.
    *        2) the data that was read from the file, if any.
    */
   importFromFile: function SP_importFromFile(aFile, aSilentError, aCallback)
   {
     // Prevent file type detection.
-    let channel = NetUtil.newChannel(aFile);
+    let channel = NetUtil.newChannel2(aFile,
+                                      null,
+                                      null,
+                                      window.document,
+                                      null, // aLoadingPrincipal
+                                      null, // aTriggeringPrincipal
+                                      Ci.nsILoadInfo.SEC_NORMAL,
+                                      Ci.nsIContentPolicy.TYPE_OTHER);
     channel.contentType = "application/javascript";
 
     this.notificationBox.removeAllNotifications(false);
 
-    NetUtil.asyncFetch(channel, (aInputStream, aStatus) => {
+    NetUtil.asyncFetch2(channel, (aInputStream, aStatus) => {
       let content = null;
 
       if (Components.isSuccessCode(aStatus)) {
         let charsets = this._getApplicableCharsets();
         content = NetUtil.readInputStreamToString(aInputStream,
                                                   aInputStream.available());
         content = this._getUnicodeContent(content, charsets);
         if (!content) {
--- a/browser/devtools/scratchpad/test/browser_scratchpad_confirm_close.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_confirm_close.js
@@ -209,20 +209,27 @@ function writeFile(file, content, callba
   converter.charset = "UTF-8";
   let fileContentStream = converter.convertToInputStream(content);
 
   NetUtil.asyncCopy(fileContentStream, fout, callback);
 }
 
 function readFile(file, callback)
 {
-  let channel = NetUtil.newChannel(file);
+  let channel = NetUtil.newChannel2(file,
+                                    null,
+                                    null,
+                                    null,      // aLoadingNode
+                                    Services.scriptSecurityManager.getSystemPrincipal(),
+                                    null,      // aTriggeringPrincipal
+                                    Ci.nsILoadInfo.SEC_NORMAL,
+                                    Ci.nsIContentPolicy.TYPE_OTHER);
   channel.contentType = "application/javascript";
 
-  NetUtil.asyncFetch(channel, function(inputStream, status) {
+  NetUtil.asyncFetch2(channel, function(inputStream, status) {
     ok(Components.isSuccessCode(status),
        "file was read successfully");
 
     let content = NetUtil.readInputStreamToString(inputStream,
                                                   inputStream.available());
     callback(content);
   });
 }
--- a/browser/devtools/scratchpad/test/browser_scratchpad_files.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_files.js
@@ -86,21 +86,28 @@ function fileExported(aStatus)
                           fileExported2);
 
   gScratchpadWindow.confirm = oldConfirm;
 
   ok(askedConfirmation, "exportToFile() asked for overwrite confirmation");
 
   gFileContent = oldContent;
 
-  let channel = NetUtil.newChannel(gFile);
+  let channel = NetUtil.newChannel2(gFile,
+                                    null,
+                                    null,
+                                    null,      // aLoadingNode
+                                    Services.scriptSecurityManager.getSystemPrincipal(),
+                                    null,      // aTriggeringPrincipal
+                                    Ci.nsILoadInfo.SEC_NORMAL,
+                                    Ci.nsIContentPolicy.TYPE_OTHER);
   channel.contentType = "application/javascript";
 
   // Read back the temporary file.
-  NetUtil.asyncFetch(channel, fileRead);
+  NetUtil.asyncFetch2(channel, fileRead);
 }
 
 function fileExported2()
 {
   ok(false, "exportToFile() did not cancel file overwrite");
 }
 
 function fileRead(aInputStream, aStatus)
--- a/browser/devtools/shared/AppCacheUtils.jsm
+++ b/browser/devtools/shared/AppCacheUtils.jsm
@@ -180,17 +180,24 @@ AppCacheUtils.prototype = {
     return deferred.promise;
   },
 
   _getURIInfo: function ACU__getURIInfo(uri) {
     let inputStream = Cc["@mozilla.org/scriptableinputstream;1"]
                         .createInstance(Ci.nsIScriptableInputStream);
     let deferred = promise.defer();
     let buffer = "";
-    let channel = Services.io.newChannel(uri, null, null);
+    let channel = Services.io.newChannel2(uri,
+                                          null,
+                                          null,
+                                          null,      // aLoadingNode
+                                          Services.scriptSecurityManager.getSystemPrincipal(),
+                                          null,      // aTriggeringPrincipal
+                                          Ci.nsILoadInfo.SEC_NORMAL,
+                                          Ci.nsIContentPolicy.TYPE_OTHER);
 
     // Avoid the cache:
     channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
     channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
 
     channel.asyncOpen({
       onStartRequest: function (request, context) {
         // This empty method is needed in order for onDataAvailable to be
--- a/browser/devtools/sourceeditor/test/head.js
+++ b/browser/devtools/sourceeditor/test/head.js
@@ -82,17 +82,24 @@ function limit(source, [line, ch]) {
     return list[0].slice(0, ch);
   return [...list.slice(0, line - 1), list[line - 1].slice(0, ch)].join("\n");
 }
 
 function read(url) {
   let scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
     .getService(Ci.nsIScriptableInputStream);
 
-  let channel = Services.io.newChannel(url, null, null);
+  let channel = Services.io.newChannel2(url,
+                                        null,
+                                        null,
+                                        null,      // aLoadingNode
+                                        Services.scriptSecurityManager.getSystemPrincipal(),
+                                        null,      // aTriggeringPrincipal
+                                        Ci.nsILoadInfo.SEC_NORMAL,
+                                        Ci.nsIContentPolicy.TYPE_OTHER);
   let input = channel.open();
   scriptableStream.init(input);
 
   let data = "";
   while (input.available()) {
     data = data.concat(scriptableStream.read(input.available()));
   }
   scriptableStream.close();
--- a/browser/devtools/styleeditor/StyleEditorUI.jsm
+++ b/browser/devtools/styleeditor/StyleEditorUI.jsm
@@ -334,29 +334,33 @@ StyleEditorUI.prototype = {
    *        Optional parent window for the file picker.
    */
   _importFromFile: function(file, parentWindow) {
     let onFileSelected = (file) => {
       if (!file) {
         // nothing selected
         return;
       }
-      NetUtil.asyncFetch(file, (stream, status) => {
+      NetUtil.asyncFetch2(file, (stream, status) => {
         if (!Components.isSuccessCode(status)) {
           this.emit("error", { key: LOAD_ERROR });
           return;
         }
         let source = NetUtil.readInputStreamToString(stream, stream.available());
         stream.close();
 
         this._debuggee.addStyleSheet(source).then((styleSheet) => {
           this._onStyleSheetCreated(styleSheet, file);
         });
-      });
-
+      },
+      this._window.document,
+      null,  // aLoadingPrincipal
+      null,  // aTriggeringPrincipal
+      Ci.nsILoadInfo.SEC_NORMAL,
+      Ci.nsIContentPolicy.TYPE_OTHER);
     };
 
     showFilePicker(file, false, parentWindow, onFileSelected);
   },
 
 
   /**
    * When a new or imported stylesheet has been added to the document.
--- a/browser/devtools/styleeditor/test/browser_styleeditor_filesave.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_filesave.js
@@ -60,17 +60,24 @@ function copy(aSrcChromeURL, aDestFileNa
   write(read(aSrcChromeURL), destFile, aCallback);
 }
 
 function read(aSrcChromeURL)
 {
   let scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
     .getService(Ci.nsIScriptableInputStream);
 
-  let channel = Services.io.newChannel(aSrcChromeURL, null, null);
+  let channel = Services.io.newChannel2(aSrcChromeURL,
+                                        null,
+                                        null,
+                                        null,      // aLoadingNode
+                                        Services.scriptSecurityManager.getSystemPrincipal(),
+                                        null,      // aTriggeringPrincipal
+                                        Ci.nsILoadInfo.SEC_NORMAL,
+                                        Ci.nsIContentPolicy.TYPE_OTHER);
   let input = channel.open();
   scriptableStream.init(input);
 
   let data = "";
   while (input.available()) {
     data = data.concat(scriptableStream.read(input.available()));
   }
   scriptableStream.close();
--- a/browser/devtools/styleeditor/test/browser_styleeditor_sourcemap_watching.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_sourcemap_watching.js
@@ -145,17 +145,24 @@ function copy(aSrcChromeURL, aDestFilePa
   return write(read(aSrcChromeURL), destFile);
 }
 
 function read(aSrcChromeURL)
 {
   let scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
     .getService(Ci.nsIScriptableInputStream);
 
-  let channel = Services.io.newChannel(aSrcChromeURL, null, null);
+  let channel = Services.io.newChannel2(aSrcChromeURL,
+                                        null,
+                                        null,
+                                        null,      // aLoadingNode
+                                        Services.scriptSecurityManager.getSystemPrincipal(),
+                                        null,      // aTriggeringPrincipal
+                                        Ci.nsILoadInfo.SEC_NORMAL,
+                                        Ci.nsIContentPolicy.TYPE_OTHER);
   let input = channel.open();
   scriptableStream.init(input);
 
   let data = "";
   while (input.available()) {
     data = data.concat(scriptableStream.read(input.available()));
   }
   scriptableStream.close();
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -43,16 +43,17 @@ SEARCH_PATHS = [
     'layout/tools/reftest',
     'other-licenses/ply',
     'xpcom/idl-parser',
     'testing',
     'testing/taskcluster',
     'testing/xpcshell',
     'testing/web-platform',
     'testing/web-platform/harness',
+    'testing/marionette/client',
     'testing/marionette/client/marionette',
     'testing/marionette/transport',
     'testing/mozbase/mozcrash',
     'testing/mozbase/mozdebug',
     'testing/mozbase/mozdevice',
     'testing/mozbase/mozfile',
     'testing/mozbase/mozhttpd',
     'testing/mozbase/mozlog',
--- a/build/moz-automation.mk
+++ b/build/moz-automation.mk
@@ -76,16 +76,20 @@ automation/upload: automation/package-te
 automation/upload: automation/buildsymbols
 automation/upload: automation/update-packaging
 
 # automation/{pretty-}package should depend on build (which is implicit due to
 # the way client.mk invokes automation/build), but buildsymbols changes the
 # binaries/libs, and that's what we package/test.
 automation/pretty-package: automation/buildsymbols
 
+# The installer and packager both run stage-package, and may conflict
+# with each other.
+automation/installer: automation/package
+
 # The 'pretty' versions of targets run before the regular ones to avoid
 # conflicts in writing to the same files.
 automation/installer: automation/pretty-installer
 automation/package: automation/pretty-package
 automation/package-tests: automation/pretty-package-tests
 automation/l10n-check: automation/pretty-l10n-check
 automation/update-packaging: automation/pretty-update-packaging
 
--- a/config/external/nss/nss.def
+++ b/config/external/nss/nss.def
@@ -672,12 +672,12 @@ SSL_VersionRangeSet
 SSL_VersionRangeSetDefault
 UTIL_SetForkState
 VFY_Begin
 VFY_CreateContext
 VFY_DestroyContext
 VFY_End
 VFY_Update
 VFY_VerifyData
-VFY_VerifyDataDirect
 VFY_VerifyDataWithAlgorithmID
+VFY_VerifyDigestDirect
 _SGN_VerifyPKCS1DigestInfo
 PK11_PQG_ParamGenV2
--- a/config/gcc-stl-wrapper.template.h
+++ b/config/gcc-stl-wrapper.template.h
@@ -5,17 +5,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_${HEADER}_h
 #define mozilla_${HEADER}_h
 
 // For some reason, Apple's GCC refuses to honor -fno-exceptions when
 // compiling ObjC.
-#if __EXCEPTIONS && !(__OBJC__ && __GNUC__ && XP_IOS)
+#if defined(__EXCEPTIONS) && __EXCEPTIONS && !(__OBJC__ && __GNUC__ && XP_IOS)
 #  error "STL code can only be used with -fno-exceptions"
 #endif
 
 // Silence "warning: #include_next is a GCC extension"
 #pragma GCC system_header
 
 #ifdef _WIN32
 // Suppress windef.h min and max macros - they make std::min/max not compile.
--- a/configure.in
+++ b/configure.in
@@ -617,18 +617,18 @@ See https://developer.mozilla.org/en/Win
                                 [std::vector<int> v; return v.at(1);],
                                 ac_cv_have_dllimport_exception_bug="no",
                                 ac_cv_have_dllimport_exception_bug="yes")
                     CXXFLAGS="$_SAVE_CXXFLAGS"
                     AC_LANG_RESTORE
                 ])
             if test "$ac_cv_have_dllimport_exception_bug" = "no"; then
                 WRAP_STL_INCLUDES=1
-                MOZ_MSVC_STL_WRAP__Throw=1
-                AC_DEFINE(MOZ_MSVC_STL_WRAP__Throw)
+                MOZ_MSVC_STL_WRAP_Throw=1
+                AC_DEFINE(MOZ_MSVC_STL_WRAP_Throw)
             fi
         else
             AC_CACHE_CHECK(for overridable _RAISE,
                            ac_cv_have__RAISE,
                 [
                     AC_LANG_SAVE
                     AC_LANG_CPLUSPLUS
                     _SAVE_CXXFLAGS="$CXXFLAGS"
@@ -641,18 +641,18 @@ See https://developer.mozilla.org/en/Win
                                    [std::vector<int> v; return v.at(1);],
                                    ac_cv_have__RAISE="no",
                                    ac_cv_have__RAISE="yes")
                     CXXFLAGS="$_SAVE_CXXFLAGS"
                     AC_LANG_RESTORE
                 ])
             if test "$ac_cv_have__RAISE" = "yes"; then
                 WRAP_STL_INCLUDES=1
-                MOZ_MSVC_STL_WRAP__RAISE=1
-                AC_DEFINE(MOZ_MSVC_STL_WRAP__RAISE)
+                MOZ_MSVC_STL_WRAP_RAISE=1
+                AC_DEFINE(MOZ_MSVC_STL_WRAP_RAISE)
             else
                 AC_MSG_ERROR([Gecko exception wrapping doesn't understand your your MSVC/SDK.  Please file a bug describing this error and your build configuration.])
             fi
         fi
 
         if test "$WRAP_STL_INCLUDES" = "1"; then
             STL_FLAGS='-I$(DIST)/stl_wrappers'
         fi
@@ -762,18 +762,18 @@ AC_SUBST(GNU_AS)
 AC_SUBST(GNU_LD)
 AC_SUBST(GNU_CC)
 AC_SUBST(GNU_CXX)
 AC_SUBST(INTEL_CC)
 AC_SUBST(INTEL_CXX)
 
 AC_SUBST(STL_FLAGS)
 AC_SUBST(WRAP_STL_INCLUDES)
-AC_SUBST(MOZ_MSVC_STL_WRAP__Throw)
-AC_SUBST(MOZ_MSVC_STL_WRAP__RAISE)
+AC_SUBST(MOZ_MSVC_STL_WRAP_Throw)
+AC_SUBST(MOZ_MSVC_STL_WRAP_RAISE)
 
 dnl ========================================================
 dnl Checks for programs.
 dnl ========================================================
 AC_PROG_INSTALL
 AC_PROG_LN_S
 
 AC_MSG_CHECKING([for minimum required perl version >= $PERL_VERSION])
@@ -7278,17 +7278,17 @@ if test -f "${srcdir}/${MOZ_BUILD_APP}/c
   rm -f $tmpscript
 fi
 
 dnl We need to wrap dlopen and related functions on Android because we use
 dnl our own linker.
 if test "$OS_TARGET" = Android; then
     MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=PR_GetEnv,--wrap=PR_SetEnv"
     if test "$MOZ_WIDGET_TOOLKIT" = gonk -a -n "$MOZ_NUWA_PROCESS"; then
-        MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=pthread_create,--wrap=epoll_wait,--wrap=poll,--wrap=pthread_cond_timedwait,--wrap=pthread_cond_wait,--wrap=epoll_create,--wrap=epoll_ctl,--wrap=close,--wrap=pthread_key_create,--wrap=pthread_key_delete,--wrap=socketpair,--wrap=pthread_self,--wrap=pthread_mutex_lock,--wrap=pthread_join,--wrap=pipe,--wrap=pipe2"
+        MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=pthread_create,--wrap=epoll_wait,--wrap=poll,--wrap=pthread_cond_timedwait,--wrap=pthread_cond_wait,--wrap=epoll_create,--wrap=epoll_ctl,--wrap=close,--wrap=pthread_key_create,--wrap=pthread_key_delete,--wrap=socketpair,--wrap=pthread_self,--wrap=pthread_mutex_lock,--wrap=pthread_mutex_trylock,--wrap=pthread_join,--wrap=pipe,--wrap=pipe2"
     fi
     if test "$MOZ_WIDGET_TOOLKIT" = android; then
         MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=getaddrinfo,--wrap=freeaddrinfo,--wrap=gai_strerror"
     fi
 fi
 
 AC_SUBST_LIST(MOZ_GLUE_WRAP_LDFLAGS)
 export MOZ_GLUE_WRAP_LDFLAGS
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -13749,29 +13749,16 @@ nsDocShell::SetOpener(nsITabParent* aOpe
 
 nsITabParent*
 nsDocShell::GetOpener()
 {
   nsCOMPtr<nsITabParent> opener(do_QueryReferent(mOpener));
   return opener;
 }
 
-void
-nsDocShell::SetOpenedRemote(nsITabParent* aOpenedRemote)
-{
-  mOpenedRemote = do_GetWeakReference(aOpenedRemote);
-}
-
-nsITabParent*
-nsDocShell::GetOpenedRemote()
-{
-  nsCOMPtr<nsITabParent> openedRemote(do_QueryReferent(mOpenedRemote));
-  return openedRemote;
-}
-
 URLSearchParams*
 nsDocShell::GetURLSearchParams()
 {
   return mURLSearchParams;
 }
 
 void
 nsDocShell::NotifyJSRunToCompletionStart()
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -958,17 +958,16 @@ private:
     nsCString         mParentCharset;
     int32_t           mParentCharsetSource;
     nsCOMPtr<nsIPrincipal> mParentCharsetPrincipal;
     nsTObserverArray<nsWeakPtr> mPrivacyObservers;
     nsTObserverArray<nsWeakPtr> mReflowObservers;
     nsTObserverArray<nsWeakPtr> mScrollObservers;
     nsCString         mOriginalUriString;
     nsWeakPtr mOpener;
-    nsWeakPtr mOpenedRemote;
 
     // A depth count of how many times NotifyRunToCompletionStart
     // has been called without a matching NotifyRunToCompletionStop.
     uint32_t          mJSRunToCompletionDepth;
 
     // True if recording profiles.
     bool mProfileTimelineRecording;
 
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -49,17 +49,17 @@ interface nsIWebBrowserPrint;
 interface nsIVariant;
 interface nsIPrivacyTransitionObserver;
 interface nsIReflowObserver;
 interface nsIScrollObserver;
 interface nsITabParent;
  
 typedef unsigned long nsLoadFlags;
 
-[scriptable, builtinclass, uuid(888fcf04-a69b-11e4-8d33-6fbb72d2eb03)]
+[scriptable, builtinclass, uuid(f84b1ae4-2f78-4bad-b36a-6a8516ee6e40)]
 interface nsIDocShell : nsIDocShellTreeItem
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -1020,23 +1020,16 @@ interface nsIDocShell : nsIDocShellTreeI
    * Regarding setOpener / getOpener - We can't use XPIDL's "attribute"
    * for notxpcom, so we're relegated to using explicit gets / sets. This
    * should be fine, considering that these methods should only ever be
    * called from native code.
    */
   [noscript,notxpcom,nostdcall] void setOpener(in nsITabParent aOpener);
   [noscript,notxpcom,nostdcall] nsITabParent getOpener();
 
-  /**
-   * See the documentation for setOpener and getOpener about why we
-   * don't use attribute here instead.
-   */
-  [noscript,notxpcom,nostdcall] void setOpenedRemote(in nsITabParent aOpenedRemote);
-  [noscript,notxpcom,nostdcall] nsITabParent getOpenedRemote();
-
   // URLSearchParams for the window.location is owned by the docShell.
   [noscript,notxpcom] URLSearchParams getURLSearchParams();
 
   /**
    * Notify DocShell when the browser is about to start executing JS, and after
    * that execution has stopped.  This only occurs when the Timeline devtool
    * is collecting information.
    */
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -987,31 +987,35 @@ Element::CreateShadowRoot(ErrorResult& a
   protoBinding->SetInheritsStyle(false);
 
   // Calling SetPrototypeBinding takes ownership of protoBinding.
   docInfo->SetPrototypeBinding(NS_LITERAL_CSTRING("shadowroot"), protoBinding);
 
   nsRefPtr<ShadowRoot> shadowRoot = new ShadowRoot(this, nodeInfo.forget(),
                                                    protoBinding);
 
+  shadowRoot->SetIsComposedDocParticipant(IsInComposedDoc());
+
   // Replace the old ShadowRoot with the new one and let the old
   // ShadowRoot know about the younger ShadowRoot because the old
   // ShadowRoot is projected into the younger ShadowRoot's shadow
   // insertion point (if it exists).
   ShadowRoot* olderShadow = GetShadowRoot();
   SetShadowRoot(shadowRoot);
   if (olderShadow) {
     olderShadow->SetYoungerShadow(shadowRoot);
 
     // Unbind children of older shadow root because they
     // are no longer in the composed tree.
     for (nsIContent* child = olderShadow->GetFirstChild(); child;
          child = child->GetNextSibling()) {
       child->UnbindFromTree(true, false);
     }
+
+    olderShadow->SetIsComposedDocParticipant(false);
   }
 
   // xblBinding takes ownership of docInfo.
   nsRefPtr<nsXBLBinding> xblBinding = new nsXBLBinding(shadowRoot, protoBinding);
   shadowRoot->SetAssociatedBinding(xblBinding);
   xblBinding->SetBoundElement(this);
 
   SetXBLBinding(xblBinding);
@@ -1464,23 +1468,16 @@ Element::BindToTree(nsIDocument* aDocume
 
     // We no longer need to track the subtree pointer (and in fact we'll assert
     // if we do this any later).
     ClearSubtreeRootPointer();
 
     // Being added to a document.
     SetInDocument();
 
-    // Attached callback must be enqueued whenever custom element is inserted into a
-    // document and this document has a browsing context.
-    if (GetCustomElementData() && aDocument->GetDocShell()) {
-      // Enqueue an attached callback for the custom element.
-      aDocument->EnqueueLifecycleCallback(nsIDocument::eAttached, this);
-    }
-
     // Unset this flag since we now really are in a document.
     UnsetFlags(NODE_FORCE_XBL_BINDINGS |
                // And clear the lazy frame construction bits.
                NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES |
                // And the restyle bits
                ELEMENT_ALL_RESTYLE_FLAGS);
   } else if (IsInShadowTree()) {
     // We're not in a document, but we did get inserted into a shadow tree.
@@ -1493,16 +1490,26 @@ Element::BindToTree(nsIDocument* aDocume
                NODE_NEEDS_FRAME | NODE_DESCENDANTS_NEED_FRAMES |
                ELEMENT_ALL_RESTYLE_FLAGS);
   } else {
     // If we're not in the doc and not in a shadow tree,
     // update our subtree pointer.
     SetSubtreeRootPointer(aParent->SubtreeRoot());
   }
 
+  nsIDocument* composedDoc = GetComposedDoc();
+  if (composedDoc) {
+    // Attached callback must be enqueued whenever custom element is inserted into a
+    // document and this document has a browsing context.
+    if (GetCustomElementData() && composedDoc->GetDocShell()) {
+      // Enqueue an attached callback for the custom element.
+      composedDoc->EnqueueLifecycleCallback(nsIDocument::eAttached, this);
+    }
+  }
+
   // Propagate scoped style sheet tracking bit.
   if (mParent->IsContent()) {
     nsIContent* parent;
     ShadowRoot* shadowRootParent = ShadowRoot::FromNode(mParent);
     if (shadowRootParent) {
       parent = shadowRootParent->GetHost();
     } else {
       parent = mParent->AsContent();
@@ -1573,16 +1580,17 @@ Element::BindToTree(nsIDocument* aDocume
     if (sheet) {
       mAttrsAndChildren.SetMappedAttrStyleSheet(sheet);
     }
   }
 
   // Call BindToTree on shadow root children.
   ShadowRoot* shadowRoot = GetShadowRoot();
   if (shadowRoot) {
+    shadowRoot->SetIsComposedDocParticipant(IsInComposedDoc());
     for (nsIContent* child = shadowRoot->GetFirstChild(); child;
          child = child->GetNextSibling()) {
       rv = child->BindToTree(nullptr, shadowRoot,
                              shadowRoot->GetBindingParent(),
                              aCompileEventHandlers);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
@@ -1630,18 +1638,17 @@ Element::UnbindFromTree(bool aDeep, bool
   NS_PRECONDITION(aDeep || (!GetUncomposedDoc() && !GetBindingParent()),
                   "Shallow unbind won't clear document and binding parent on "
                   "kids!");
 
   RemoveFromIdTable();
 
   // Make sure to unbind this node before doing the kids
   nsIDocument* document =
-    HasFlag(NODE_FORCE_XBL_BINDINGS) || IsInShadowTree() ?
-      OwnerDoc() : GetUncomposedDoc();
+    HasFlag(NODE_FORCE_XBL_BINDINGS) ? OwnerDoc() : GetComposedDoc();
 
   if (aNullParent) {
     if (IsFullScreenAncestor()) {
       // The element being removed is an ancestor of the full-screen element,
       // exit full-screen state.
       nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
                                       NS_LITERAL_CSTRING("DOM"), OwnerDoc(),
                                       nsContentUtils::eDOM_PROPERTIES,
@@ -1751,16 +1758,18 @@ Element::UnbindFromTree(bool aDeep, bool
 
   // Unbind children of shadow root.
   ShadowRoot* shadowRoot = GetShadowRoot();
   if (shadowRoot) {
     for (nsIContent* child = shadowRoot->GetFirstChild(); child;
          child = child->GetNextSibling()) {
       child->UnbindFromTree(true, false);
     }
+
+    shadowRoot->SetIsComposedDocParticipant(false);
   }
 }
 
 nsICSSDeclaration*
 Element::GetSMILOverrideStyle()
 {
   Element::nsDOMSlots *slots = DOMSlots();
 
--- a/dom/base/File.cpp
+++ b/dom/base/File.cpp
@@ -637,16 +637,20 @@ File::Constructor(const GlobalObject& aG
 
   nsRefPtr<MultipartFileImpl> impl = new MultipartFileImpl(EmptyString());
   impl->InitializeChromeFile(aData, aBag, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
   MOZ_ASSERT(impl->IsFile());
 
+  if (aBag.mLastModified.WasPassed()) {
+    impl->SetLastModified(aBag.mLastModified.Value());
+  }
+
   nsRefPtr<File> domFile = new File(aGlobal.GetAsSupports(), impl);
   return domFile.forget();
 }
 
 /* static */ already_AddRefed<File>
 File::Constructor(const GlobalObject& aGlobal,
                   nsIFile* aData,
                   const ChromeFilePropertyBag& aBag,
@@ -662,16 +666,20 @@ File::Constructor(const GlobalObject& aG
 
   nsRefPtr<MultipartFileImpl> impl = new MultipartFileImpl(EmptyString());
   impl->InitializeChromeFile(window, aData, aBag, true, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
   MOZ_ASSERT(impl->IsFile());
 
+  if (aBag.mLastModified.WasPassed()) {
+    impl->SetLastModified(aBag.mLastModified.Value());
+  }
+
   nsRefPtr<File> domFile = new File(aGlobal.GetAsSupports(), impl);
   return domFile.forget();
 }
 
 /* static */ already_AddRefed<File>
 File::Constructor(const GlobalObject& aGlobal,
                   const nsAString& aData,
                   const ChromeFilePropertyBag& aBag,
@@ -686,16 +694,20 @@ File::Constructor(const GlobalObject& aG
 
   nsRefPtr<MultipartFileImpl> impl = new MultipartFileImpl(EmptyString());
   impl->InitializeChromeFile(window, aData, aBag, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
   MOZ_ASSERT(impl->IsFile());
 
+  if (aBag.mLastModified.WasPassed()) {
+    impl->SetLastModified(aBag.mLastModified.Value());
+  }
+
   nsRefPtr<File> domFile = new File(aGlobal.GetAsSupports(), impl);
   return domFile.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////
 // mozilla::dom::FileImpl implementation
 
 already_AddRefed<FileImpl>
--- a/dom/base/MultipartFileImpl.cpp
+++ b/dom/base/MultipartFileImpl.cpp
@@ -184,41 +184,54 @@ MultipartFileImpl::InitializeBlob(
 
 void
 MultipartFileImpl::SetLengthAndModifiedDate()
 {
   MOZ_ASSERT(mLength == UINT64_MAX);
   MOZ_ASSERT(mLastModificationDate == UINT64_MAX);
 
   uint64_t totalLength = 0;
+  uint64_t lastModified = 0;
+  bool lastModifiedSet = false;
 
   for (uint32_t index = 0, count = mBlobImpls.Length(); index < count; index++) {
     nsRefPtr<FileImpl>& blob = mBlobImpls[index];
 
 #ifdef DEBUG
     MOZ_ASSERT(!blob->IsSizeUnknown());
     MOZ_ASSERT(!blob->IsDateUnknown());
 #endif
 
     ErrorResult error;
     uint64_t subBlobLength = blob->GetSize(error);
     MOZ_ALWAYS_TRUE(!error.Failed());
 
     MOZ_ASSERT(UINT64_MAX - subBlobLength >= totalLength);
     totalLength += subBlobLength;
+
+    if (blob->IsFile()) {
+      uint64_t partLastModified = blob->GetLastModified(error);
+      MOZ_ALWAYS_TRUE(!error.Failed());
+
+      if (lastModified < partLastModified) {
+        lastModified = partLastModified;
+        lastModifiedSet = true;
+      }
+    }
   }
 
   mLength = totalLength;
 
   if (mIsFile) {
     // We cannot use PR_Now() because bug 493756 and, for this reason:
     //   var x = new Date(); var f = new File(...);
     //   x.getTime() < f.dateModified.getTime()
     // could fail.
-    mLastModificationDate = JS_Now();
+    mLastModificationDate =
+      lastModifiedSet ? lastModified * PR_USEC_PER_MSEC : JS_Now();
   }
 }
 
 void
 MultipartFileImpl::GetMozFullPathInternal(nsAString& aFilename,
                                           ErrorResult& aRv)
 {
   if (!mIsFromNsIFile || mBlobImpls.Length() == 0) {
--- a/dom/base/ShadowRoot.cpp
+++ b/dom/base/ShadowRoot.cpp
@@ -62,17 +62,17 @@ NS_INTERFACE_MAP_END_INHERITING(Document
 NS_IMPL_ADDREF_INHERITED(ShadowRoot, DocumentFragment)
 NS_IMPL_RELEASE_INHERITED(ShadowRoot, DocumentFragment)
 
 ShadowRoot::ShadowRoot(nsIContent* aContent,
                        already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
                        nsXBLPrototypeBinding* aProtoBinding)
   : DocumentFragment(aNodeInfo), mPoolHost(aContent),
     mProtoBinding(aProtoBinding), mShadowElement(nullptr),
-    mInsertionPointChanged(false)
+    mInsertionPointChanged(false), mIsComposedDocParticipant(false)
 {
   SetHost(aContent);
 
   // Nodes in a shadow tree should never store a value
   // in the subtree root pointer, nodes in the shadow tree
   // track the subtree root using GetContainingShadow().
   ClearSubtreeRootPointer();
 
--- a/dom/base/ShadowRoot.h
+++ b/dom/base/ShadowRoot.h
@@ -122,16 +122,22 @@ public:
                            const nsAString& aLocalName);
   already_AddRefed<nsContentList>
     GetElementsByClassName(const nsAString& aClasses);
   void GetInnerHTML(nsAString& aInnerHTML);
   void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError);
   Element* Host();
   ShadowRoot* GetOlderShadowRoot() { return mOlderShadow; }
   void StyleSheetChanged();
+
+  bool IsComposedDocParticipant() { return mIsComposedDocParticipant; }
+  void SetIsComposedDocParticipant(bool aIsComposedDocParticipant)
+  {
+    mIsComposedDocParticipant = aIsComposedDocParticipant;
+  }
 protected:
   virtual ~ShadowRoot();
 
   // The pool host is the parent of the nodes that will be distributed
   // into the insertion points in this ShadowRoot. See |ChangeShadowRoot|.
   nsCOMPtr<nsIContent> mPoolHost;
 
   // An array of content insertion points that are a descendant of the ShadowRoot
@@ -166,16 +172,22 @@ protected:
   // this ShadowRoot was created.
   nsRefPtr<ShadowRoot> mYoungerShadow;
 
   // A boolean that indicates that an insertion point was added or removed
   // from this ShadowRoot and that the nodes need to be redistributed into
   // the insertion points. After this flag is set, nodes will be distributed
   // on the next mutation event.
   bool mInsertionPointChanged;
+
+  // Flag to indicate whether the descendants of this shadow root are part of the
+  // composed document. Ideally, we would use a node flag on nodes to
+  // mark whether it is in the composed document, but we have run out of flags
+  // so instead we track it here.
+  bool mIsComposedDocParticipant;
 };
 
 class ShadowRootStyleSheetList : public StyleSheetList
 {
 public:
   explicit ShadowRootStyleSheetList(ShadowRoot* aShadowRoot);
 
   NS_DECL_ISUPPORTS_INHERITED
--- a/dom/base/nsContentList.cpp
+++ b/dom/base/nsContentList.cpp
@@ -219,17 +219,17 @@ NS_GetContentList(nsINode* aRootNode,
     PL_DHashTableInit(&gContentListHashTable, &hash_table_ops,
                       sizeof(ContentListHashEntry));
   }
 
   ContentListHashEntry *entry = nullptr;
   // First we look in our hashtable.  Then we create a content list if needed
   if (gContentListHashTable.IsInitialized()) {
     entry = static_cast<ContentListHashEntry *>
-      (PL_DHashTableAdd(&gContentListHashTable, &hashKey, fallible));
+                       (PL_DHashTableAdd(&gContentListHashTable, &hashKey));
     if (entry)
       list = entry->mContentList;
   }
 
   if (!list) {
     // We need to create a ContentList and add it to our new entry, if
     // we have an entry
     nsCOMPtr<nsIAtom> xmlAtom = do_GetAtom(aTagname);
@@ -327,17 +327,18 @@ GetFuncStringContentList(nsINode* aRootN
   }
 
   FuncStringContentListHashEntry *entry = nullptr;
   // First we look in our hashtable.  Then we create a content list if needed
   if (gFuncStringContentListHashTable.IsInitialized()) {
     nsFuncStringCacheKey hashKey(aRootNode, aFunc, aString);
 
     entry = static_cast<FuncStringContentListHashEntry *>
-      (PL_DHashTableAdd(&gFuncStringContentListHashTable, &hashKey, fallible));
+                       (PL_DHashTableAdd(&gFuncStringContentListHashTable,
+                                         &hashKey));
     if (entry) {
       list = entry->mContentList;
 #ifdef DEBUG
       MOZ_ASSERT_IF(list, list->mType == ListType::sType);
 #endif
     }
   }
 
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -3959,17 +3959,17 @@ nsContentUtils::GetListenerManagerForNod
     // We're already shut down, don't bother creating an event listener
     // manager.
 
     return nullptr;
   }
 
   EventListenerManagerMapEntry *entry =
     static_cast<EventListenerManagerMapEntry *>
-      (PL_DHashTableAdd(&sEventListenerManagersHash, aNode, fallible));
+               (PL_DHashTableAdd(&sEventListenerManagersHash, aNode));
 
   if (!entry) {
     return nullptr;
   }
 
   if (!entry->mListenerManager) {
     entry->mListenerManager = new EventListenerManager(aNode);
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -453,17 +453,17 @@ CustomElementCallback::Call()
 
       // The callback hasn't actually been invoked yet, but we need to flip
       // this now in order to enqueue the attached callback. This is a spec
       // bug (w3c bug 27437).
       mOwnerData->mCreatedCallbackInvoked = true;
 
       // If ELEMENT is in a document and this document has a browsing context,
       // enqueue attached callback for ELEMENT.
-      nsIDocument* document = mThisObject->GetUncomposedDoc();
+      nsIDocument* document = mThisObject->GetComposedDoc();
       if (document && document->GetDocShell()) {
         document->EnqueueLifecycleCallback(nsIDocument::eAttached, mThisObject);
       }
 
       static_cast<LifecycleCreatedCallback *>(mCallback.get())->Call(mThisObject, rv);
       mOwnerData->mElementIsBeingCreated = false;
       break;
     }
@@ -1710,18 +1710,18 @@ nsDocument::~nsDocument()
     mAnimationController->Disconnect();
   }
 
   mParentDocument = nullptr;
 
   // Kill the subdocument map, doing this will release its strong
   // references, if any.
   if (mSubDocuments) {
-    PL_DHashTableFinish(mSubDocuments);
-    delete mSubDocuments;
+    PL_DHashTableDestroy(mSubDocuments);
+
     mSubDocuments = nullptr;
   }
 
   // Destroy link map now so we don't waste time removing
   // links one by one
   DestroyElementMaps();
 
   nsAutoScriptBlocker scriptBlocker;
@@ -2121,18 +2121,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMStyleSheets)
 
   if (tmp->mStyleSheetSetList) {
     tmp->mStyleSheetSetList->Disconnect();
     tmp->mStyleSheetSetList = nullptr;
   }
 
   if (tmp->mSubDocuments) {
-    PL_DHashTableFinish(tmp->mSubDocuments);
-    delete tmp->mSubDocuments;
+    PL_DHashTableDestroy(tmp->mSubDocuments);
     tmp->mSubDocuments = nullptr;
   }
 
   tmp->mFrameRequestCallbacks.Clear();
 
   tmp->mRadioGroups.Clear();
 
   // nsDocument has a pretty complex destructor, so we're going to
@@ -2327,18 +2326,18 @@ nsDocument::ResetToURI(nsIURI *aURI, nsI
 
   mSecurityInfo = nullptr;
 
   mDocumentLoadGroup = nullptr;
 
   // Delete references to sub-documents and kill the subdocument map,
   // if any. It holds strong references
   if (mSubDocuments) {
-    PL_DHashTableFinish(mSubDocuments);
-    delete mSubDocuments;
+    PL_DHashTableDestroy(mSubDocuments);
+
     mSubDocuments = nullptr;
   }
 
   // Destroy link map now so we don't waste time removing
   // links one by one
   DestroyElementMaps();
 
   bool oldVal = mInUnlinkOrDeletion;
@@ -3997,23 +3996,26 @@ nsDocument::SetSubDocumentFor(Element* a
       {
         PL_DHashVoidPtrKeyStub,
         PL_DHashMatchEntryStub,
         PL_DHashMoveEntryStub,
         SubDocClearEntry,
         SubDocInitEntry
       };
 
-      mSubDocuments = new PLDHashTable();
-      PL_DHashTableInit(mSubDocuments, &hash_table_ops, sizeof(SubDocMapEntry));
+      mSubDocuments = PL_NewDHashTable(&hash_table_ops, sizeof(SubDocMapEntry));
+      if (!mSubDocuments) {
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
     }
 
     // Add a mapping to the hash table
-    SubDocMapEntry *entry = static_cast<SubDocMapEntry*>
-      (PL_DHashTableAdd(mSubDocuments, aElement, fallible));
+    SubDocMapEntry *entry =
+      static_cast<SubDocMapEntry*>
+                 (PL_DHashTableAdd(mSubDocuments, aElement));
 
     if (!entry) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     if (entry->mSubDocument) {
       entry->mSubDocument->SetParentDocument(nullptr);
 
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -164,24 +164,22 @@ nsFrameLoader::nsFrameLoader(Element* aO
   , mInSwap(false)
   , mInShow(false)
   , mHideCalled(false)
   , mNetworkCreated(aNetworkCreated)
   , mRemoteBrowserShown(false)
   , mRemoteFrame(false)
   , mClipSubdocument(true)
   , mClampScrollPosition(true)
-  , mRemoteBrowserInitialized(false)
   , mObservingOwnerContent(false)
   , mVisible(true)
   , mCurrentRemoteFrame(nullptr)
   , mRemoteBrowser(nullptr)
   , mChildID(0)
   , mEventMode(EVENT_MODE_NORMAL_DISPATCH)
-  , mPendingFrameSent(false)
 {
   ResetPermissionManagerStatus();
 }
 
 nsFrameLoader::~nsFrameLoader()
 {
   mNeedsAsyncDestroy = true;
   if (mMessageManager) {
@@ -358,25 +356,16 @@ nsFrameLoader::ReallyStartLoadingInterna
 
   nsresult rv = MaybeCreateDocShell();
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (mRemoteFrame) {
     if (!mRemoteBrowser) {
-      if (!mPendingFrameSent) {
-        nsCOMPtr<nsIObserverService> os = services::GetObserverService();
-        if (os && !mRemoteBrowserInitialized) {
-          os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
-                              "remote-browser-pending", nullptr);
-          mPendingFrameSent = true;
-        }
-      }
-
       TryRemoteBrowser();
 
       if (!mRemoteBrowser) {
         NS_WARNING("Couldn't create child process for iframe.");
         return NS_ERROR_FAILURE;
       }
     }
 
@@ -908,26 +897,21 @@ nsFrameLoader::ShowRemoteFrame(const nsI
         parentIsActive = topWin && topWin->IsActive();
       }
     }
     mRemoteBrowser->Show(size, parentIsActive);
     mRemoteBrowserShown = true;
 
     EnsureMessageManager();
 
+    InitializeBrowserAPI();
     nsCOMPtr<nsIObserverService> os = services::GetObserverService();
-    if (os && !mRemoteBrowserInitialized) {
-      if (!mPendingFrameSent) {
-        os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
-                            "remote-browser-pending", nullptr);
-        mPendingFrameSent = true;
-      }
+    if (os) {
       os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
                           "remote-browser-shown", nullptr);
-      mRemoteBrowserInitialized = true;
     }
   } else {
     nsIntRect dimensions;
     NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), false);
 
     // Don't show remote iframe if we are waiting for the completion of reflow.
     if (!aFrame || !(aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
       nsIntPoint chromeDisp = aFrame->GetChromeDisplacement();
@@ -1780,16 +1764,17 @@ nsFrameLoader::MaybeCreateDocShell()
     uint32_t containingAppId = nsIScriptSecurityManager::NO_APP_ID;
     if (containingApp) {
       NS_ENSURE_SUCCESS(containingApp->GetLocalId(&containingAppId),
                         NS_ERROR_FAILURE);
     }
     mDocShell->SetIsBrowserInsideApp(containingAppId);
   }
 
+  InitializeBrowserAPI();
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (os) {
     os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
                         "inprocess-browser-shown", nullptr);
   }
 
   if (OwnerIsBrowserOrAppFrame() && mMessageManager) {
     mMessageManager->LoadFrameScript(
@@ -2200,17 +2185,16 @@ nsFrameLoader::TryRemoteBrowser()
     mContentParent = mRemoteBrowser->Manager();
 
     if (mOwnerContent->AttrValueIs(kNameSpaceID_None,
                                    nsGkAtoms::mozpasspointerevents,
                                    nsGkAtoms::_true,
                                    eCaseMatters)) {
       unused << mRemoteBrowser->SendSetUpdateHitRegion(true);
     }
-    parentDocShell->SetOpenedRemote(mRemoteBrowser);
   }
   return true;
 }
 
 mozilla::dom::PBrowserParent*
 nsFrameLoader::GetRemoteBrowser()
 {
   return mRemoteBrowser;
@@ -2744,8 +2728,17 @@ nsFrameLoader::GetLoadContext(nsILoadCon
   } else {
     nsCOMPtr<nsIDocShell> docShell;
     GetDocShell(getter_AddRefs(docShell));
     loadContext = do_GetInterface(docShell);
   }
   loadContext.forget(aLoadContext);
   return NS_OK;
 }
+
+void
+nsFrameLoader::InitializeBrowserAPI()
+{
+  nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
+  if (browserFrame) {
+    browserFrame->InitializeBrowserAPI();
+  }
+}
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -307,16 +307,18 @@ private:
   nsIAtom* TypeAttrName() const {
     return mOwnerContent->IsXUL() ? nsGkAtoms::type : nsGkAtoms::mozframetype;
   }
 
   // Update the permission manager's app-id refcount based on mOwnerContent's
   // own-or-containing-app.
   void ResetPermissionManagerStatus();
 
+  void InitializeBrowserAPI();
+
   nsCOMPtr<nsIDocShell> mDocShell;
   nsCOMPtr<nsIURI> mURIToLoad;
   mozilla::dom::Element* mOwnerContent; // WEAK
 
   // Note: this variable must be modified only by ResetPermissionManagerStatus()
   uint32_t mAppIdSentToPermissionManager;
 
 public:
@@ -346,17 +348,16 @@ private:
   // created using NS_FROM_PARSER_NETWORK flag. If the element is modified,
   // it may lose the flag.
   bool mNetworkCreated : 1;
 
   bool mRemoteBrowserShown : 1;
   bool mRemoteFrame : 1;
   bool mClipSubdocument : 1;
   bool mClampScrollPosition : 1;
-  bool mRemoteBrowserInitialized : 1;
   bool mObservingOwnerContent : 1;
 
   // Backs nsIFrameLoader::{Get,Set}Visible.  Visibility state here relates to
   // whether this frameloader's <iframe mozbrowser> is setVisible(true)'ed, and
   // doesn't necessarily correlate with docshell/document visibility.
   bool mVisible : 1;
 
   // The ContentParent associated with mRemoteBrowser.  This was added as a
@@ -364,14 +365,11 @@ private:
   nsRefPtr<mozilla::dom::nsIContentParent> mContentParent;
   RenderFrameParent* mCurrentRemoteFrame;
   TabParent* mRemoteBrowser;
   uint64_t mChildID;
 
   // See nsIFrameLoader.idl. EVENT_MODE_NORMAL_DISPATCH automatically
   // forwards some input events to out-of-process content.
   uint32_t mEventMode;
-
-  // Indicate if we have sent 'remote-browser-pending'.
-  bool mPendingFrameSent;
 };
 
 #endif
--- a/dom/base/nsGenericDOMDataNode.cpp
+++ b/dom/base/nsGenericDOMDataNode.cpp
@@ -108,16 +108,20 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   if (slots) {
     slots->Traverse(cb);
   }
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGenericDOMDataNode)
   nsINode::Unlink(tmp);
 
+  // Clear flag here because unlinking slots will clear the
+  // containing shadow root pointer.
+  tmp->UnsetFlags(NODE_IS_IN_SHADOW_TREE);
+
   nsDataSlots *slots = tmp->GetExistingDataSlots();
   if (slots) {
     slots->Unlink();
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN(nsGenericDOMDataNode)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
@@ -557,18 +561,17 @@ nsGenericDOMDataNode::BindToTree(nsIDocu
 void
 nsGenericDOMDataNode::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   // Unset frame flags; if we need them again later, they'll get set again.
   UnsetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE |
              NS_REFRAME_IF_WHITESPACE);
 
   nsIDocument* document =
-    HasFlag(NODE_FORCE_XBL_BINDINGS) || IsInShadowTree() ?
-      OwnerDoc() : GetUncomposedDoc();
+    HasFlag(NODE_FORCE_XBL_BINDINGS) ? OwnerDoc() : GetComposedDoc();
 
   if (aNullParent) {
     if (GetParent()) {
       NS_RELEASE(mParent);
     } else {
       mParent = nullptr;
     }
     SetParentIsContent(false);
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -385,27 +385,18 @@ nsINode::GetTextContentInternal(nsAStrin
 }
 
 nsIDocument*
 nsINode::GetComposedDocInternal() const
 {
   MOZ_ASSERT(HasFlag(NODE_IS_IN_SHADOW_TREE) && IsContent(),
              "Should only be caled on nodes in the shadow tree.");
 
-  // Cross ShadowRoot boundary.
   ShadowRoot* containingShadow = AsContent()->GetContainingShadow();
-
-  nsIContent* poolHost = containingShadow->GetPoolHost();
-  if (!poolHost) {
-    // This node is in an older shadow root that does not get projected into
-    // an insertion point, thus this node can not be in the composed document.
-    return nullptr;
-  }
-
-  return poolHost->GetComposedDoc();
+  return containingShadow->IsComposedDocParticipant() ?  OwnerDoc() : nullptr;
 }
 
 #ifdef DEBUG
 void
 nsINode::CheckNotNativeAnonymous() const
 {
   if (!IsNodeOfType(eCONTENT))
     return;
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -515,21 +515,19 @@ public:
    * @deprecated
    */
   nsIDocument *GetCurrentDoc() const
   {
     return GetUncomposedDoc();
   }
 
   /**
-   * This method gets the current doc of the node hosting this content
-   * or the current doc of this content if it is not being hosted. This
-   * method walks through ShadowRoot boundaries until it reach the host
-   * that is located in the root of the "tree of trees" (see Shadow DOM
-   * spec) and returns the current doc for that host.
+   * This method returns the owner doc if the node is in the
+   * composed document (as defined in the Shadow DOM spec), otherwise
+   * it returns null.
    */
   nsIDocument* GetComposedDoc() const
   {
     return IsInShadowTree() ?
       GetComposedDocInternal() : GetUncomposedDoc();
   }
 
   /**
--- a/dom/base/nsISlowScriptDebug.idl
+++ b/dom/base/nsISlowScriptDebug.idl
@@ -9,26 +9,26 @@ interface nsIDOMEventTarget;
 
 [scriptable, function, uuid(f7dbb80c-5d1e-4fd9-b55c-a9ffda4a75b1)]
 interface nsISlowScriptDebugCallback : nsISupports
 {
   void handleSlowScriptDebug(in nsIDOMWindow aWindow);
 };
 
 [scriptable, function, uuid(b1c6ecd0-8fa4-11e4-b4a9-0800200c9a66)]
-interface nsISlowScriptDebugerStartupCallback : nsISupports
+interface nsISlowScriptDebuggerStartupCallback : nsISupports
 {
   void finishDebuggerStartup();
 };
 
 [scriptable, function, uuid(dbee14b0-8fa0-11e4-b4a9-0800200c9a66)]
 interface nsISlowScriptDebugRemoteCallback : nsISupports
 {
   void handleSlowScriptDebug(in nsIDOMEventTarget aBrowser,
-      	                     in nsISlowScriptDebugerStartupCallback aCallback);
+                             in nsISlowScriptDebuggerStartupCallback aCallback);
 };
 
 [scriptable, uuid(f75d4164-3aa7-4395-ba44-a5f95b2e8427)]
 interface nsISlowScriptDebug : nsISupports
 {
   attribute nsISlowScriptDebugCallback activationHandler;
   attribute nsISlowScriptDebugRemoteCallback remoteActivationHandler;
 };
--- a/dom/base/nsPropertyTable.cpp
+++ b/dom/base/nsPropertyTable.cpp
@@ -225,17 +225,17 @@ nsPropertyTable::SetPropertyInternal(nsP
     propertyList->mNext = mPropertyList;
     mPropertyList = propertyList;
   }
 
   // The current property value (if there is one) is replaced and the current
   // value is destroyed
   nsresult result = NS_OK;
   PropertyListMapEntry *entry = static_cast<PropertyListMapEntry*>
-    (PL_DHashTableAdd(&propertyList->mObjectValueMap, aObject, fallible));
+                                           (PL_DHashTableAdd(&propertyList->mObjectValueMap, aObject));
   if (!entry)
     return NS_ERROR_OUT_OF_MEMORY;
   // A nullptr entry->key is the sign that the entry has just been allocated
   // for us.  If it's non-nullptr then we have an existing entry.
   if (entry->key) {
     if (aOldValue)
       *aOldValue = entry->value;
     else if (propertyList->mDtorFunc)
--- a/dom/base/nsScriptNameSpaceManager.cpp
+++ b/dom/base/nsScriptNameSpaceManager.cpp
@@ -117,35 +117,39 @@ GlobalNameHashInitEntry(PLDHashTable *ta
 
 NS_IMPL_ISUPPORTS(
   nsScriptNameSpaceManager,
   nsIObserver,
   nsISupportsWeakReference,
   nsIMemoryReporter)
 
 nsScriptNameSpaceManager::nsScriptNameSpaceManager()
+  : mIsInitialized(false)
 {
   MOZ_COUNT_CTOR(nsScriptNameSpaceManager);
 }
 
 nsScriptNameSpaceManager::~nsScriptNameSpaceManager()
 {
-  UnregisterWeakMemoryReporter(this);
-  // Destroy the hash
-  PL_DHashTableFinish(&mGlobalNames);
-  PL_DHashTableFinish(&mNavigatorNames);
+  if (mIsInitialized) {
+    UnregisterWeakMemoryReporter(this);
+    // Destroy the hash
+    PL_DHashTableFinish(&mGlobalNames);
+    PL_DHashTableFinish(&mNavigatorNames);
+  }
   MOZ_COUNT_DTOR(nsScriptNameSpaceManager);
 }
 
 nsGlobalNameStruct *
 nsScriptNameSpaceManager::AddToHash(PLDHashTable *aTable, const nsAString *aKey,
                                     const char16_t **aClassName)
 {
-  GlobalNameMapEntry *entry = static_cast<GlobalNameMapEntry *>
-    (PL_DHashTableAdd(aTable, aKey, fallible));
+  GlobalNameMapEntry *entry =
+    static_cast<GlobalNameMapEntry *>
+               (PL_DHashTableAdd(aTable, aKey));
 
   if (!entry) {
     return nullptr;
   }
 
   if (aClassName) {
     *aClassName = entry->mKey.get();
   }
@@ -317,23 +321,31 @@ nsScriptNameSpaceManager::Init()
   {
     GlobalNameHashHashKey,
     GlobalNameHashMatchEntry,
     PL_DHashMoveEntryStub,
     GlobalNameHashClearEntry,
     GlobalNameHashInitEntry
   };
 
-  PL_DHashTableInit(&mGlobalNames, &hash_table_ops,
-                    sizeof(GlobalNameMapEntry),
-                    GLOBALNAME_HASHTABLE_INITIAL_LENGTH);
+  mIsInitialized = PL_DHashTableInit(&mGlobalNames, &hash_table_ops,
+                                     sizeof(GlobalNameMapEntry),
+                                     fallible,
+                                     GLOBALNAME_HASHTABLE_INITIAL_LENGTH);
+  NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_OUT_OF_MEMORY);
 
-  PL_DHashTableInit(&mNavigatorNames, &hash_table_ops,
-                    sizeof(GlobalNameMapEntry),
-                    GLOBALNAME_HASHTABLE_INITIAL_LENGTH);
+  mIsInitialized = PL_DHashTableInit(&mNavigatorNames, &hash_table_ops,
+                                     sizeof(GlobalNameMapEntry),
+                                     fallible,
+                                     GLOBALNAME_HASHTABLE_INITIAL_LENGTH);
+  if (!mIsInitialized) {
+    PL_DHashTableFinish(&mGlobalNames);
+
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
 
   RegisterWeakMemoryReporter(this);
 
   nsresult rv = NS_OK;
 
   rv = RegisterExternalInterfaces(false);
   NS_ENSURE_SUCCESS(rv, rv);
 
--- a/dom/base/nsScriptNameSpaceManager.h
+++ b/dom/base/nsScriptNameSpaceManager.h
@@ -235,11 +235,13 @@ private:
                                     nsISupports* aEntry,
                                     bool aRemove);
 
   nsGlobalNameStruct* LookupNameInternal(const nsAString& aName,
                                          const char16_t **aClassName = nullptr);
 
   PLDHashTable mGlobalNames;
   PLDHashTable mNavigatorNames;
+
+  bool mIsInitialized;
 };
 
 #endif /* nsScriptNameSpaceManager_h__ */
--- a/dom/base/nsWrapperCache.cpp
+++ b/dom/base/nsWrapperCache.cpp
@@ -1,17 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsWrapperCacheInlines.h"
 
-#include "jsproxy.h"
+#include "js/Proxy.h"
 #include "mozilla/dom/DOMJSProxyHandler.h"
 #include "mozilla/HoldDropJSObjects.h"
 #include "nsCycleCollectionTraversalCallback.h"
 #include "nsCycleCollector.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
--- a/dom/base/test/bug403852_fileOpener.js
+++ b/dom/base/test/bug403852_fileOpener.js
@@ -6,10 +6,12 @@ let testFile = Cc["@mozilla.org/file/dir
                  .QueryInterface(Ci.nsIProperties)
                  .get("ProfD", Ci.nsIFile);
 testFile.append("prefs.js");
 
 addMessageListener("file.open", function () {
   sendAsyncMessage("file.opened", {
     file: new File(testFile),
     mtime: testFile.lastModifiedTime,
+    fileWithDate: new File(testFile, { lastModified: 123 }),
+    fileDate: 123,
   });
 });
--- a/dom/base/test/test_bug403852.html
+++ b/dom/base/test/test_bug403852.html
@@ -33,28 +33,33 @@ function onOpened(message) {
   // Make sure the file is accessible with indexed notation
   var domFile = fileList.files[0];
 
   is(domFile.name, "prefs.js", "fileName should be prefs.js");
 
   ok("lastModifiedDate" in domFile, "lastModifiedDate must be present");
 
   var d = new Date(message.mtime);
-  todo(d.getTime() == domFile.lastModifiedDate.getTime(), "lastModifiedDate should be the same (FIXME: bug 1121722)");
+  is(d.getTime(), domFile.lastModifiedDate.getTime(), "lastModifiedDate should be the same");
 
   var x = new Date();
 
   // In our implementation of File object, lastModifiedDate is unknown only for new objects.
   // Using canvas or input[type=file] elements, we 'often' have a valid lastModifiedDate values.
   // For canvas we use memory files and the lastModifiedDate is now().
   var f = new File([new Blob(['test'], {type: 'text/plain'})], "test-name");
 
   var y = f.lastModifiedDate;
   var z = new Date();
 
   ok((x.getTime() <= y.getTime()) && (y.getTime() <= z.getTime()), "lastModifiedDate of file which does not have last modified date should be current time");
+
+
+  var d = new Date(message.fileDate);
+  is(d.getTime(), message.fileWithDate.lastModifiedDate.getTime(), "lastModifiedDate should be the same when lastModified is set: " + message.fileWithDate.lastModified);
+
   script.destroy();
   SimpleTest.finish();
 }
 
 </script>
 </pre>
 </body> </html>
--- a/dom/bindings/DOMJSProxyHandler.h
+++ b/dom/bindings/DOMJSProxyHandler.h
@@ -5,17 +5,17 @@
 
 #ifndef mozilla_dom_DOMJSProxyHandler_h
 #define mozilla_dom_DOMJSProxyHandler_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Likely.h"
 
 #include "jsapi.h"
-#include "jsproxy.h"
+#include "js/Proxy.h"
 #include "nsString.h"
 
 #define DOM_PROXY_OBJECT_SLOT js::PROXY_PRIVATE_SLOT
 
 namespace mozilla {
 namespace dom {
 
 enum {
--- a/dom/events/EventDispatcher.cpp
+++ b/dom/events/EventDispatcher.cpp
@@ -392,17 +392,17 @@ EventTargetChainItemForChromeTarget(nsTA
 
 /* static */ nsresult
 EventDispatcher::Dispatch(nsISupports* aTarget,
                           nsPresContext* aPresContext,
                           WidgetEvent* aEvent,
                           nsIDOMEvent* aDOMEvent,
                           nsEventStatus* aEventStatus,
                           EventDispatchingCallback* aCallback,
-                          nsCOMArray<EventTarget>* aTargets)
+                          nsTArray<EventTarget*>* aTargets)
 {
   PROFILER_LABEL("EventDispatcher", "Dispatch",
     js::ProfileEntry::Category::EVENTS);
 
   NS_ASSERTION(aEvent, "Trying to dispatch without WidgetEvent!");
   NS_ENSURE_TRUE(!aEvent->mFlags.mIsBeingDispatched,
                  NS_ERROR_DOM_INVALID_STATE_ERR);
   NS_ASSERTION(!aTargets || !aEvent->message, "Wrong parameters!");
@@ -471,17 +471,17 @@ EventDispatcher::Dispatch(nsISupports* a
       // Set the target to be the original dispatch target,
       aEvent->target = target;
       // but use chrome event handler or TabChildGlobal for event target chain.
       target = piTarget;
     }
   }
 
 #ifdef DEBUG
-  if (!nsContentUtils::IsSafeToRunScript()) {
+  if (aEvent->message != NS_EVENT_NULL && !nsContentUtils::IsSafeToRunScript()) {
     nsresult rv = NS_ERROR_FAILURE;
     if (target->GetContextForEventHandlers(&rv) ||
         NS_FAILED(rv)) {
       nsCOMPtr<nsINode> node = do_QueryInterface(target);
       if (node && nsContentUtils::IsChromeDoc(node->OwnerDoc())) {
         NS_WARNING("Fix the caller!");
       } else {
         NS_ERROR("This is unsafe! Fix the caller!");
@@ -620,17 +620,17 @@ EventDispatcher::Dispatch(nsISupports* a
         break;
       }
     }
     if (NS_SUCCEEDED(rv)) {
       if (aTargets) {
         aTargets->Clear();
         aTargets->SetCapacity(chain.Length());
         for (uint32_t i = 0; i < chain.Length(); ++i) {
-          aTargets->AppendObject(chain[i].CurrentTarget()->GetTargetForDOMEvent());
+          aTargets->AppendElement(chain[i].CurrentTarget()->GetTargetForDOMEvent());
         }
       } else {
         // Event target chain is created. Handle the chain.
         EventChainPostVisitor postVisitor(preVisitor);
         EventTargetChainItem::HandleEventTargetChain(chain, postVisitor,
                                                      aCallback, cd);
 
         preVisitor.mEventStatus = postVisitor.mEventStatus;
--- a/dom/events/EventDispatcher.h
+++ b/dom/events/EventDispatcher.h
@@ -250,17 +250,17 @@ public:
    * @note Use this method when dispatching a WidgetEvent.
    */
   static nsresult Dispatch(nsISupports* aTarget,
                            nsPresContext* aPresContext,
                            WidgetEvent* aEvent,
                            nsIDOMEvent* aDOMEvent = nullptr,
                            nsEventStatus* aEventStatus = nullptr,
                            EventDispatchingCallback* aCallback = nullptr,
-                           nsCOMArray<dom::EventTarget>* aTargets = nullptr);
+                           nsTArray<dom::EventTarget*>* aTargets = nullptr);
 
   /**
    * Dispatches an event.
    * If aDOMEvent is not nullptr, it is used for dispatching
    * (aEvent can then be nullptr) and (if aDOMEvent is not |trusted| already),
    * the |trusted| flag is set based on the UniversalXPConnect capability.
    * Otherwise this works like EventDispatcher::Dispatch.
    * @note Use this method when dispatching nsIDOMEvent.
--- a/dom/events/EventListenerService.cpp
+++ b/dom/events/EventListenerService.cpp
@@ -166,21 +166,21 @@ NS_IMETHODIMP
 EventListenerService::GetEventTargetChainFor(nsIDOMEventTarget* aEventTarget,
                                              uint32_t* aCount,
                                              nsIDOMEventTarget*** aOutArray)
 {
   *aCount = 0;
   *aOutArray = nullptr;
   NS_ENSURE_ARG(aEventTarget);
   WidgetEvent event(true, NS_EVENT_NULL);
-  nsCOMArray<EventTarget> targets;
+  nsTArray<EventTarget*> targets;
   nsresult rv = EventDispatcher::Dispatch(aEventTarget, nullptr, &event,
                                           nullptr, nullptr, nullptr, &targets);
   NS_ENSURE_SUCCESS(rv, rv);
-  int32_t count = targets.Count();
+  int32_t count = targets.Length();
   if (count == 0) {
     return NS_OK;
   }
 
   *aOutArray =
     static_cast<nsIDOMEventTarget**>(
       nsMemory::Alloc(sizeof(nsIDOMEventTarget*) * count));
   NS_ENSURE_TRUE(*aOutArray, NS_ERROR_OUT_OF_MEMORY);
--- a/dom/events/TextComposition.cpp
+++ b/dom/events/TextComposition.cpp
@@ -219,16 +219,27 @@ TextComposition::DispatchCompositionEven
   // composition string empty or didn't have clause information), we don't
   // need to dispatch redundant DOM text event.
   if (dispatchDOMTextEvent &&
       aCompositionEvent->message != NS_COMPOSITION_CHANGE &&
       !mIsComposing && mLastData == aCompositionEvent->mData) {
     dispatchEvent = dispatchDOMTextEvent = false;
   }
 
+  // widget may dispatch redundant NS_COMPOSITION_CHANGE event
+  // which modifies neither composition string, clauses nor caret
+  // position.  In such case, we shouldn't dispatch DOM events.
+  if (dispatchDOMTextEvent &&
+      aCompositionEvent->message == NS_COMPOSITION_CHANGE &&
+      mLastData == aCompositionEvent->mData &&
+      mRanges && aCompositionEvent->mRanges &&
+      mRanges->Equals(*aCompositionEvent->mRanges)) {
+    dispatchEvent = dispatchDOMTextEvent = false;
+  }
+
   if (dispatchDOMTextEvent) {
     if (!MaybeDispatchCompositionUpdate(aCompositionEvent)) {
       return;
     }
   }
 
   if (dispatchEvent) {
     // If the composition event should cause a DOM text event, we should
--- a/dom/geolocation/nsGeolocationSettings.cpp
+++ b/dom/geolocation/nsGeolocationSettings.cpp
@@ -219,28 +219,36 @@ nsGeolocationSettings::HandleGeolocation
   }
 
   mAlaEnabled = aVal.toBoolean();
 }
 
 void
 nsGeolocationSettings::HandleGeolocationPerOriginSettingsChange(const JS::Value& aVal)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   if (!aVal.isObject()) {
     return;
   }
 
   // clear the hash table
   mPerOriginSettings.Clear();
 
-  // enumerate the array
-  AutoJSAPI jsapi;
-  jsapi.Init();
-  JSContext* cx = jsapi.cx();
-  JS::Rooted<JSObject*> obj(cx, &aVal.toObject());
+  // root the object and get the global
+  JS::Rooted<JSObject*> obj(nsContentUtils::RootingCx(), &aVal.toObject());
+  MOZ_ASSERT(obj);
+  nsIGlobalObject* global = xpc::NativeGlobal(obj);
+  NS_ENSURE_TRUE_VOID(global && global->GetGlobalJSObject());
+
+  // because the spec requires calling getters when enumerating the key of a
+  // dictionary
+  AutoEntryScript aes(global);
+  aes.TakeOwnershipOfErrorReporting();
+  JSContext *cx = aes.cx();
   JS::AutoIdArray ids(cx, JS_Enumerate(cx, obj));
 
   // if we get no ids then the exception list is empty and we can return here.
   if (!ids)
       return;
 
   // go through all of the objects in the exceptions dictionary
   for (size_t i = 0; i < ids.length(); i++) {
--- a/dom/html/HTMLShadowElement.cpp
+++ b/dom/html/HTMLShadowElement.cpp
@@ -141,16 +141,18 @@ HTMLShadowElement::BindToTree(nsIDocumen
 
     containingShadow->SetInsertionPointChanged();
   }
 
   if (mIsInsertionPoint && containingShadow) {
     // Propagate BindToTree calls to projected shadow root children.
     ShadowRoot* projectedShadow = containingShadow->GetOlderShadowRoot();
     if (projectedShadow) {
+      projectedShadow->SetIsComposedDocParticipant(IsInComposedDoc());
+
       for (nsIContent* child = projectedShadow->GetFirstChild(); child;
            child = child->GetNextSibling()) {
         rv = child->BindToTree(nullptr, projectedShadow,
                                projectedShadow->GetBindingParent(),
                                aCompileEventHandlers);
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
@@ -168,16 +170,18 @@ HTMLShadowElement::UnbindFromTree(bool a
     // Propagate UnbindFromTree call to previous projected shadow
     // root children.
     ShadowRoot* projectedShadow = oldContainingShadow->GetOlderShadowRoot();
     if (projectedShadow) {
       for (nsIContent* child = projectedShadow->GetFirstChild(); child;
            child = child->GetNextSibling()) {
         child->UnbindFromTree(true, false);
       }
+
+      projectedShadow->SetIsComposedDocParticipant(false);
     }
   }
 
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 
   if (oldContainingShadow && !GetContainingShadow() && mIsInsertionPoint) {
     nsTArray<HTMLShadowElement*>& shadowDescendants =
       oldContainingShadow->ShadowDescendants();
--- a/dom/html/HTMLStyleElement.cpp
+++ b/dom/html/HTMLStyleElement.cpp
@@ -152,25 +152,24 @@ HTMLStyleElement::BindToTree(nsIDocument
 
   return rv;  
 }
 
 void
 HTMLStyleElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   nsCOMPtr<nsIDocument> oldDoc = GetUncomposedDoc();
-  nsCOMPtr<nsIDocument> oldComposedDoc = GetComposedDoc();
   ShadowRoot* oldShadow = GetContainingShadow();
 
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 
-  if (GetContainingShadow() && !oldComposedDoc) {
-    // The style is in a shadow tree and was already not
-    // in the composed document. Thus the sheet does not
-    // need to be updated.
+  if (oldShadow && GetContainingShadow()) {
+    // The style is in a shadow tree and is still in the
+    // shadow tree. Thus the sheets in the shadow DOM
+    // do not need to be updated.
     return;
   }
 
   UpdateStyleSheetInternal(oldDoc, oldShadow);
 }
 
 nsresult
 HTMLStyleElement::SetAttr(int32_t aNameSpaceID, nsIAtom* aName,
--- a/dom/html/nsBrowserElement.cpp
+++ b/dom/html/nsBrowserElement.cpp
@@ -14,101 +14,22 @@
 #include "mozilla/dom/ToJSValue.h"
 
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsFrameLoader.h"
 #include "nsIDOMDOMRequest.h"
 #include "nsIDOMElement.h"
 #include "nsINode.h"
-#include "nsIObserver.h"
-#include "nsIObserverService.h"
 #include "nsIPrincipal.h"
-#include "nsWeakReference.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 
-static const char kRemoteBrowserPending[] = "remote-browser-pending";
-static const char kInprocessBrowserShown[] = "inprocess-browser-shown";
-
-class nsBrowserElement::BrowserShownObserver : public nsIObserver
-                                             , public nsSupportsWeakReference
-{
-public:
-  explicit BrowserShownObserver(nsBrowserElement* aBrowserElement);
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIOBSERVER
-  void AddObserver();
-  void RemoveObserver();
-private:
-  virtual ~BrowserShownObserver();
-
-  // Weak reference to the browser element. nsBrowserElement has a
-  // reference to us. nsBrowserElement's destructor is responsible to
-  // null out this weak reference via RemoveObserver()
-  nsBrowserElement* mBrowserElement;
-};
-
-NS_IMPL_ISUPPORTS(nsBrowserElement::BrowserShownObserver, nsIObserver, nsISupportsWeakReference)
-
-nsBrowserElement::BrowserShownObserver::BrowserShownObserver(nsBrowserElement* aBrowserElement)
-  : mBrowserElement(aBrowserElement)
-{
-}
-
-nsBrowserElement::BrowserShownObserver::~BrowserShownObserver()
-{
-  RemoveObserver();
-}
-
-NS_IMETHODIMP
-nsBrowserElement::BrowserShownObserver::Observe(nsISupports* aSubject,
-                                                const char* aTopic,
-                                                const char16_t* aData)
-{
-  NS_ENSURE_TRUE(mBrowserElement, NS_OK);
-
-  if (!strcmp(aTopic, kRemoteBrowserPending) ||
-      !strcmp(aTopic, kInprocessBrowserShown)) {
-    nsCOMPtr<nsIFrameLoader> frameLoader = do_QueryInterface(aSubject);
-    nsCOMPtr<nsIFrameLoader> myFrameLoader = mBrowserElement->GetFrameLoader();
-    // The browser element API needs the frameloader to
-    // initialize. We still use the observer to get notified when the
-    // frameloader is created. So we check if the frameloader created
-    // is ours, then initialize the browser element API.
-    if (frameLoader && frameLoader == myFrameLoader) {
-      mBrowserElement->InitBrowserElementAPI();
-    }
-  }
-  return NS_OK;
-}
-
-void
-nsBrowserElement::BrowserShownObserver::AddObserver()
-{
-  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-  if (obs) {
-    obs->AddObserver(this, kRemoteBrowserPending, true);
-    obs->AddObserver(this, kInprocessBrowserShown, true);
-  }
-}
-
-void
-nsBrowserElement::BrowserShownObserver::RemoveObserver()
-{
-  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-  if (obs) {
-    obs->RemoveObserver(this, kRemoteBrowserPending);
-    obs->RemoveObserver(this, kInprocessBrowserShown);
-  }
-  mBrowserElement = nullptr;
-}
-
 bool
 nsBrowserElement::IsBrowserElementOrThrow(ErrorResult& aRv)
 {
   if (mBrowserElementAPI) {
     return true;
   }
   aRv.Throw(NS_ERROR_DOM_INVALID_NODE_TYPE_ERR);
   return false;
@@ -140,28 +61,16 @@ nsBrowserElement::InitBrowserElementAPI(
   }
 
   mBrowserElementAPI = do_CreateInstance("@mozilla.org/dom/browser-element-api;1");
   if (mBrowserElementAPI) {
     mBrowserElementAPI->SetFrameLoader(frameLoader);
   }
 }
 
-nsBrowserElement::nsBrowserElement()
-  : mOwnerIsWidget(false)
-{
-  mObserver = new BrowserShownObserver(this);
-  mObserver->AddObserver();
-}
-
-nsBrowserElement::~nsBrowserElement()
-{
-  mObserver->RemoveObserver();
-}
-
 void
 nsBrowserElement::SetVisible(bool aVisible, ErrorResult& aRv)
 {
   NS_ENSURE_TRUE_VOID(IsBrowserElementOrThrow(aRv));
 
   nsresult rv = mBrowserElementAPI->SetVisible(aVisible);
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/dom/html/nsBrowserElement.h
+++ b/dom/html/nsBrowserElement.h
@@ -8,17 +8,16 @@
 #define nsBrowserElement_h
 
 #include "mozilla/dom/BindingDeclarations.h"
 
 #include "nsCOMPtr.h"
 #include "nsIBrowserElementAPI.h"
 
 class nsFrameLoader;
-class nsIObserver;
 
 namespace mozilla {
 
 namespace dom {
 struct BrowserElementDownloadOptions;
 class BrowserElementNextPaintEventCallback;
 class DOMRequest;
 } // namespace dom
@@ -26,18 +25,18 @@ class DOMRequest;
 class ErrorResult;
 
 /**
  * A helper class for browser-element frames
  */
 class nsBrowserElement
 {
 public:
-  nsBrowserElement();
-  virtual ~nsBrowserElement();
+  nsBrowserElement() : mOwnerIsWidget(false) {}
+  virtual ~nsBrowserElement() {}
 
   void SetVisible(bool aVisible, ErrorResult& aRv);
   already_AddRefed<dom::DOMRequest> GetVisible(ErrorResult& aRv);
   void SetActive(bool aActive, ErrorResult& aRv);
   bool GetActive(ErrorResult& aRv);
 
   void SendMouseEvent(const nsAString& aType,
                       uint32_t aX,
@@ -89,24 +88,20 @@ public:
   already_AddRefed<dom::DOMRequest> SetInputMethodActive(bool isActive,
                                                          ErrorResult& aRv);
 
   void SetNFCFocus(bool isFocus,
                    ErrorResult& aRv);
 
 protected:
   NS_IMETHOD_(already_AddRefed<nsFrameLoader>) GetFrameLoader() = 0;
+  void InitBrowserElementAPI();
   nsCOMPtr<nsIBrowserElementAPI> mBrowserElementAPI;
 
 private:
-  void InitBrowserElementAPI();
   bool IsBrowserElementOrThrow(ErrorResult& aRv);
   bool IsNotWidgetOrThrow(ErrorResult& aRv);
   bool mOwnerIsWidget;
-
-  class BrowserShownObserver;
-  friend class BrowserShownObserver;
-  nsRefPtr<BrowserShownObserver> mObserver;
 };
 
 } // namespace mozilla
 
 #endif // nsBrowserElement_h
--- a/dom/html/nsGenericHTMLFrameElement.cpp
+++ b/dom/html/nsGenericHTMLFrameElement.cpp
@@ -494,8 +494,16 @@ nsGenericHTMLFrameElement::DisallowCreat
 NS_IMETHODIMP
 nsGenericHTMLFrameElement::AllowCreateFrameLoader()
 {
   MOZ_ASSERT(!mFrameLoader);
   MOZ_ASSERT(mFrameLoaderCreationDisallowed);
   mFrameLoaderCreationDisallowed = false;
   return NS_OK;
 }
+
+NS_IMETHODIMP
+nsGenericHTMLFrameElement::InitializeBrowserAPI()
+{
+  MOZ_ASSERT(mFrameLoader);
+  InitBrowserElementAPI();
+  return NS_OK;
+}
--- a/dom/interfaces/html/nsIMozBrowserFrame.idl
+++ b/dom/interfaces/html/nsIMozBrowserFrame.idl
@@ -80,9 +80,15 @@ interface nsIMozBrowserFrame : nsIDOMMoz
 
   /**
    * Create a remote (i.e., out-of-process) frame loader attached to the given
    * tab parent.
    *
    * It is an error to call this method if we already have a frame loader.
    */
   void createRemoteFrameLoader(in nsITabParent aTabParent);
+
+  /**
+   * Initialize the API, and add frame message listener to listen to API
+   * invocations.
+   */
+  [noscript] void initializeBrowserAPI();
 };
--- a/dom/ipc/ProcessPriorityManager.cpp
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -466,16 +466,23 @@ ProcessPriorityManagerImpl::Observe(
 
   return NS_OK;
 }
 
 already_AddRefed<ParticularProcessPriorityManager>
 ProcessPriorityManagerImpl::GetParticularProcessPriorityManager(
   ContentParent* aContentParent)
 {
+#ifdef MOZ_NUWA_PROCESS
+  // Do not attempt to change the priority of the Nuwa process
+  if (aContentParent->IsNuwaProcess()) {
+    return nullptr;
+  }
+#endif
+
   nsRefPtr<ParticularProcessPriorityManager> pppm;
   uint64_t cpId = aContentParent->ChildID();
   mParticularManagers.Get(cpId, &pppm);
   if (!pppm) {
     pppm = new ParticularProcessPriorityManager(aContentParent);
     pppm->Init();
     mParticularManagers.Put(cpId, pppm);
 
@@ -489,17 +496,19 @@ ProcessPriorityManagerImpl::GetParticula
 void
 ProcessPriorityManagerImpl::SetProcessPriority(ContentParent* aContentParent,
                                                ProcessPriority aPriority,
                                                uint32_t aBackgroundLRU)
 {
   MOZ_ASSERT(aContentParent);
   nsRefPtr<ParticularProcessPriorityManager> pppm =
     GetParticularProcessPriorityManager(aContentParent);
-  pppm->SetPriorityNow(aPriority, aBackgroundLRU);
+  if (pppm) {
+    pppm->SetPriorityNow(aPriority, aBackgroundLRU);
+  }
 }
 
 void
 ProcessPriorityManagerImpl::ObserveContentParentCreated(
   nsISupports* aContentParent)
 {
   // Do nothing; it's sufficient to get the PPPM.  But assign to nsRefPtr so we
   // don't leak the already_AddRefed object.
@@ -527,28 +536,27 @@ ProcessPriorityManagerImpl::ObserveConte
   NS_ENSURE_TRUE_VOID(props);
 
   uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN;
   props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID);
   NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN);
 
   nsRefPtr<ParticularProcessPriorityManager> pppm;
   mParticularManagers.Get(childID, &pppm);
-  MOZ_ASSERT(pppm);
   if (pppm) {
     pppm->ShutDown();
-  }
 
-  mParticularManagers.Remove(childID);
+    mParticularManagers.Remove(childID);
 
-  if (mHighPriorityChildIDs.Contains(childID)) {
-    mHighPriorityChildIDs.RemoveEntry(childID);
+    if (mHighPriorityChildIDs.Contains(childID)) {
+      mHighPriorityChildIDs.RemoveEntry(childID);
 
-    // We just lost a high-priority process; reset everyone's CPU priorities.
-    ResetAllCPUPriorities();
+      // We just lost a high-priority process; reset everyone's CPU priorities.
+      ResetAllCPUPriorities();
+    }
   }
 }
 
 void
 ProcessPriorityManagerImpl::ResetAllCPUPriorities( void )
 {
   nsTArray<nsRefPtr<ParticularProcessPriorityManager> > pppms;
   mParticularManagers.EnumerateRead(
@@ -801,32 +809,35 @@ ParticularProcessPriorityManager::OnAudi
 }
 
 void
 ParticularProcessPriorityManager::OnRemoteBrowserFrameShown(nsISupports* aSubject)
 {
   nsCOMPtr<nsIFrameLoader> fl = do_QueryInterface(aSubject);
   NS_ENSURE_TRUE_VOID(fl);
 
-  // Ignore notifications that aren't from a BrowserOrApp
-  bool isBrowserOrApp;
-  fl->GetOwnerIsBrowserOrAppFrame(&isBrowserOrApp);
-  if (!isBrowserOrApp) {
-    return;
-  }
-
   TabParent* tp = TabParent::GetFrom(fl);
   NS_ENSURE_TRUE_VOID(tp);
 
   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
   if (tp->Manager() != mContentParent) {
     return;
   }
 
-  ResetPriority();
+  // Ignore notifications that aren't from a BrowserOrApp
+  bool isBrowserOrApp;
+  fl->GetOwnerIsBrowserOrAppFrame(&isBrowserOrApp);
+  if (isBrowserOrApp) {
+    ResetPriority();
+  }
+
+  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+  if (os) {
+    os->RemoveObserver(this, "remote-browser-shown");
+  }
 }
 
 void
 ParticularProcessPriorityManager::OnTabParentDestroyed(nsISupports* aSubject)
 {
   nsCOMPtr<nsITabParent> tp = do_QueryInterface(aSubject);
   NS_ENSURE_TRUE_VOID(tp);
 
@@ -1034,23 +1045,16 @@ ParticularProcessPriorityManager::SetPri
   SetPriorityNow(aPriority, ComputeCPUPriority(aPriority), aBackgroundLRU);
 }
 
 void
 ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority,
                                                  ProcessCPUPriority aCPUPriority,
                                                  uint32_t aBackgroundLRU)
 {
-#ifdef MOZ_NUWA_PROCESS
-  // Do not attempt to change the priority of the Nuwa process
-  if (mContentParent->IsNuwaProcess()) {
-    return;
-  }
-#endif
-
   if (aPriority == PROCESS_PRIORITY_UNKNOWN) {
     MOZ_ASSERT(false);
     return;
   }
 
   if (aBackgroundLRU > 0 &&
       aPriority == PROCESS_PRIORITY_BACKGROUND &&
       mPriority == PROCESS_PRIORITY_BACKGROUND) {
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -642,21 +642,32 @@ TabParent::RecvCreateWindow(PBrowserPare
                             NS_ConvertUTF16toUTF8(aName).get(),
                             NS_ConvertUTF16toUTF8(aFeatures).get(), aCalledFromJS,
                             false, false, this, nullptr, getter_AddRefs(window));
   NS_ENSURE_SUCCESS(rv, false);
 
   nsCOMPtr<nsPIDOMWindow> pwindow = do_QueryInterface(window);
   NS_ENSURE_TRUE(pwindow, false);
 
-  nsRefPtr<nsIDocShell> newDocShell = pwindow->GetDocShell();
-  NS_ENSURE_TRUE(newDocShell, false);
-
-  nsCOMPtr<nsITabParent> newRemoteTab = newDocShell->GetOpenedRemote();
-  NS_ENSURE_TRUE(newRemoteTab, false);
+  nsCOMPtr<nsIDocShell> windowDocShell = pwindow->GetDocShell();
+  NS_ENSURE_TRUE(windowDocShell, false);
+
+  nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
+  windowDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
+
+  nsCOMPtr<nsIXULWindow> xulWin = do_GetInterface(treeOwner);
+  NS_ENSURE_TRUE(xulWin, false);
+
+  nsCOMPtr<nsIXULBrowserWindow> xulBrowserWin;
+  xulWin->GetXULBrowserWindow(getter_AddRefs(xulBrowserWin));
+  NS_ENSURE_TRUE(xulBrowserWin, false);
+
+  nsCOMPtr<nsITabParent> newRemoteTab;
+  rv = xulBrowserWin->ForceInitialBrowserRemote(getter_AddRefs(newRemoteTab));
+  NS_ENSURE_SUCCESS(rv, false);
 
   MOZ_ASSERT(TabParent::GetFrom(newRemoteTab) == newTab);
 
   aFrameScripts->SwapElements(newTab->mDelayedFrameScripts);
   return true;
 }
 
 TabParent* TabParent::sNextTabParent;
--- a/dom/media/MediaDecoderReader.cpp
+++ b/dom/media/MediaDecoderReader.cpp
@@ -309,16 +309,15 @@ MediaDecoderReader::Shutdown()
   // out mTaskQueue, since otherwise any remaining tasks could crash when they
   // invoke GetTaskQueue()->IsCurrentThreadIn().
   if (mTaskQueue && !mTaskQueueIsBorrowed) {
     // If we own our task queue, shutdown ends when the task queue is done.
     p = mTaskQueue->BeginShutdown();
   } else {
     // If we don't own our task queue, we resolve immediately (though
     // asynchronously).
-    p = new ShutdownPromise(__func__);
-    p->Resolve(true, __func__);
+    p = ShutdownPromise::CreateAndResolve(true, __func__);
   }
 
   return p;
 }
 
 } // namespace mozilla
--- a/dom/media/MediaPromise.h
+++ b/dom/media/MediaPromise.h
@@ -57,38 +57,52 @@ template<typename T> class MediaPromiseH
 template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
 class MediaPromise
 {
 public:
   typedef ResolveValueT ResolveValueType;
   typedef RejectValueT RejectValueType;
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaPromise)
+
+protected:
+  // MediaPromise is the public type, and never constructed directly. Construct
+  // a MediaPromise::Private, defined below.
   explicit MediaPromise(const char* aCreationSite)
     : mCreationSite(aCreationSite)
     , mMutex("MediaPromise Mutex")
     , mHaveConsumer(false)
   {
     PROMISE_LOG("%s creating MediaPromise (%p)", mCreationSite, this);
   }
 
+public:
+  // MediaPromise::Private allows us to separate the public interface (upon which
+  // consumers of the promise may invoke methods like Then()) from the private
+  // interface (upon which the creator of the promise may invoke Resolve() or
+  // Reject()). APIs should create and store a MediaPromise::Private (usually
+  // via a MediaPromiseHolder), and return a MediaPromise to consumers.
+  //
+  // NB: We can include the definition of this class inline once B2G ICS is gone.
+  class Private;
+
   static nsRefPtr<MediaPromise>
   CreateAndResolve(ResolveValueType aResolveValue, const char* aResolveSite)
   {
-    nsRefPtr<MediaPromise> p = new MediaPromise(aResolveSite);
+    nsRefPtr<typename MediaPromise::Private> p = new MediaPromise::Private(aResolveSite);
     p->Resolve(aResolveValue, aResolveSite);
-    return p;
+    return Move(p);
   }
 
   static nsRefPtr<MediaPromise>
   CreateAndReject(RejectValueType aRejectValue, const char* aRejectSite)
   {
-    nsRefPtr<MediaPromise> p = new MediaPromise(aRejectSite);
+    nsRefPtr<typename MediaPromise::Private> p = new MediaPromise::Private(aRejectSite);
     p->Reject(aRejectValue, aRejectSite);
-    return p;
+    return Move(p);
   }
 
   class Consumer
   {
   public:
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Consumer)
 
     void Disconnect()
@@ -323,66 +337,48 @@ public:
   void Then(TargetType* aResponseTarget, const char* aCallSite, ThisType* aThisVal,
             ResolveMethodType aResolveMethod, RejectMethodType aRejectMethod)
   {
     nsRefPtr<Consumer> c =
       RefableThen(aResponseTarget, aCallSite, aThisVal, aResolveMethod, aRejectMethod);
     return;
   }
 
-  void ChainTo(already_AddRefed<MediaPromise> aChainedPromise, const char* aCallSite)
+  void ChainTo(already_AddRefed<Private> aChainedPromise, const char* aCallSite)
   {
     MutexAutoLock lock(mMutex);
     MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveConsumer);
     mHaveConsumer = true;
-    nsRefPtr<MediaPromise> chainedPromise = aChainedPromise;
+    nsRefPtr<Private> chainedPromise = aChainedPromise;
     PROMISE_LOG("%s invoking Chain() [this=%p, chainedPromise=%p, isPending=%d]",
                 aCallSite, this, chainedPromise.get(), (int) IsPending());
     if (!IsPending()) {
       ForwardTo(chainedPromise);
     } else {
       mChainedPromises.AppendElement(chainedPromise);
     }
   }
 
-  void Resolve(ResolveValueType aResolveValue, const char* aResolveSite)
-  {
-    MutexAutoLock lock(mMutex);
-    MOZ_ASSERT(IsPending());
-    PROMISE_LOG("%s resolving MediaPromise (%p created at %s)", aResolveSite, this, mCreationSite);
-    mResolveValue.emplace(aResolveValue);
-    DispatchAll();
-  }
-
-  void Reject(RejectValueType aRejectValue, const char* aRejectSite)
-  {
-    MutexAutoLock lock(mMutex);
-    MOZ_ASSERT(IsPending());
-    PROMISE_LOG("%s rejecting MediaPromise (%p created at %s)", aRejectSite, this, mCreationSite);
-    mRejectValue.emplace(aRejectValue);
-    DispatchAll();
-  }
-
 protected:
   bool IsPending() { return mResolveValue.isNothing() && mRejectValue.isNothing(); }
   void DispatchAll()
   {
     mMutex.AssertCurrentThreadOwns();
     for (size_t i = 0; i < mThenValues.Length(); ++i) {
       mThenValues[i]->Dispatch(this);
     }
     mThenValues.Clear();
 
     for (size_t i = 0; i < mChainedPromises.Length(); ++i) {
       ForwardTo(mChainedPromises[i]);
     }
     mChainedPromises.Clear();
   }
 
-  void ForwardTo(MediaPromise* aOther)
+  void ForwardTo(Private* aOther)
   {
     MOZ_ASSERT(!IsPending());
     if (mResolveValue.isSome()) {
       aOther->Resolve(mResolveValue.ref(), "<chained promise>");
     } else {
       aOther->Reject(mRejectValue.ref(), "<chained promise>");
     }
   }
@@ -395,20 +391,46 @@ protected:
     MOZ_ASSERT(mChainedPromises.IsEmpty());
   };
 
   const char* mCreationSite; // For logging
   Mutex mMutex;
   Maybe<ResolveValueType> mResolveValue;
   Maybe<RejectValueType> mRejectValue;
   nsTArray<nsRefPtr<ThenValueBase>> mThenValues;
-  nsTArray<nsRefPtr<MediaPromise>> mChainedPromises;
+  nsTArray<nsRefPtr<Private>> mChainedPromises;
   bool mHaveConsumer;
 };
 
+template<typename ResolveValueT, typename RejectValueT, bool IsExclusive>
+class MediaPromise<ResolveValueT, RejectValueT, IsExclusive>::Private
+  : public MediaPromise<ResolveValueT, RejectValueT, IsExclusive>
+{
+public:
+  explicit Private(const char* aCreationSite) : MediaPromise(aCreationSite) {}
+
+  void Resolve(ResolveValueT aResolveValue, const char* aResolveSite)
+  {
+    MutexAutoLock lock(mMutex);
+    MOZ_ASSERT(IsPending());
+    PROMISE_LOG("%s resolving MediaPromise (%p created at %s)", aResolveSite, this, mCreationSite);
+    mResolveValue.emplace(aResolveValue);
+    DispatchAll();
+  }
+
+  void Reject(RejectValueT aRejectValue, const char* aRejectSite)
+  {
+    MutexAutoLock lock(mMutex);
+    MOZ_ASSERT(IsPending());
+    PROMISE_LOG("%s rejecting MediaPromise (%p created at %s)", aRejectSite, this, mCreationSite);
+    mRejectValue.emplace(aRejectValue);
+    DispatchAll();
+  }
+};
+
 /*
  * Class to encapsulate a promise for a particular role. Use this as the member
  * variable for a class whose method returns a promise.
  */
 template<typename PromiseType>
 class MediaPromiseHolder
 {
 public:
@@ -417,40 +439,40 @@ public:
 
   ~MediaPromiseHolder() { MOZ_ASSERT(!mPromise); }
 
   already_AddRefed<PromiseType> Ensure(const char* aMethodName) {
     if (mMonitor) {
       mMonitor->AssertCurrentThreadOwns();
     }
     if (!mPromise) {
-      mPromise = new PromiseType(aMethodName);
+      mPromise = new (typename PromiseType::Private)(aMethodName);
     }
-    nsRefPtr<PromiseType> p = mPromise;
+    nsRefPtr<PromiseType> p = mPromise.get();
     return p.forget();
   }
 
   // Provide a Monitor that should always be held when accessing this instance.
   void SetMonitor(Monitor* aMonitor) { mMonitor = aMonitor; }
 
   bool IsEmpty()
   {
     if (mMonitor) {
       mMonitor->AssertCurrentThreadOwns();
     }
     return !mPromise;
   }
 
-  already_AddRefed<PromiseType> Steal()
+  already_AddRefed<typename PromiseType::Private> Steal()
   {
     if (mMonitor) {
       mMonitor->AssertCurrentThreadOwns();
     }
 
-    nsRefPtr<PromiseType> p = mPromise;
+    nsRefPtr<typename PromiseType::Private> p = mPromise;
     mPromise = nullptr;
     return p.forget();
   }
 
   void Resolve(typename PromiseType::ResolveValueType aResolveValue,
                const char* aMethodName)
   {
     if (mMonitor) {
@@ -485,17 +507,17 @@ public:
   {
     if (!IsEmpty()) {
       Reject(aRejectValue, aMethodName);
     }
   }
 
 private:
   Monitor* mMonitor;
-  nsRefPtr<PromiseType> mPromise;
+  nsRefPtr<typename PromiseType::Private> mPromise;
 };
 
 /*
  * Class to encapsulate a MediaPromise::Consumer reference. Use this as the member
  * variable for a class waiting on a media promise.
  */
 template<typename PromiseType>
 class MediaPromiseConsumerHolder
@@ -585,41 +607,41 @@ protected:
   Arg1Type mArg1;
   Arg2Type mArg2;
 };
 
 template<typename PromiseType>
 class ProxyRunnable : public nsRunnable
 {
 public:
-  ProxyRunnable(PromiseType* aProxyPromise, MethodCallBase<PromiseType>* aMethodCall)
+  ProxyRunnable(typename PromiseType::Private* aProxyPromise, MethodCallBase<PromiseType>* aMethodCall)
     : mProxyPromise(aProxyPromise), mMethodCall(aMethodCall) {}
 
   NS_IMETHODIMP Run()
   {
     nsRefPtr<PromiseType> p = mMethodCall->Invoke();
     mMethodCall = nullptr;
     p->ChainTo(mProxyPromise.forget(), "<Proxy Promise>");
     return NS_OK;
   }
 
 private:
-  nsRefPtr<PromiseType> mProxyPromise;
+  nsRefPtr<typename PromiseType::Private> mProxyPromise;
   nsAutoPtr<MethodCallBase<PromiseType>> mMethodCall;
 };
 
 template<typename PromiseType, typename TargetType>
 static nsRefPtr<PromiseType>
 ProxyInternal(TargetType* aTarget, MethodCallBase<PromiseType>* aMethodCall, const char* aCallerName)
 {
-  nsRefPtr<PromiseType> p = new PromiseType(aCallerName);
+  nsRefPtr<typename PromiseType::Private> p = new (typename PromiseType::Private)(aCallerName);
   nsRefPtr<ProxyRunnable<PromiseType>> r = new ProxyRunnable<PromiseType>(p, aMethodCall);
   nsresult rv = detail::DispatchMediaPromiseRunnable(aTarget, r);
   MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
-  return p;
+  return Move(p);
 }
 
 } // namespace detail
 
 template<typename PromiseType, typename TargetType, typename ThisType>
 static nsRefPtr<PromiseType>
 ProxyMediaCall(TargetType* aTarget, ThisType* aThisVal, const char* aCallerName,
                nsRefPtr<PromiseType>(ThisType::*aMethod)())
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -77,16 +77,17 @@ CopySessions(const nsAString& aKey,
 }
 
 static PLDHashOperator
 CloseSessions(const nsAString& aKey,
               nsRefPtr<MediaKeySession>& aSession,
               void* aClosure)
 {
   aSession->OnClosed();
+  ((MediaKeys*)aClosure)->Release();
   return PL_DHASH_NEXT;
 }
 
 void
 MediaKeys::Terminated()
 {
   KeySessionHashMap keySessions;
   // Remove entries during iteration will screw it. Make a copy first.
@@ -106,17 +107,19 @@ MediaKeys::Terminated()
 void
 MediaKeys::Shutdown()
 {
   if (mProxy) {
     mProxy->Shutdown();
     mProxy = nullptr;
   }
 
-  mPromises.Enumerate(&RejectPromises, nullptr);
+  nsRefPtr<MediaKeys> kungFuDeathGrip = this;
+
+  mPromises.Enumerate(&RejectPromises, this);
   mPromises.Clear();
 }
 
 nsPIDOMWindow*
 MediaKeys::GetParentObject() const
 {
   return mParent;
 }
@@ -164,26 +167,32 @@ MediaKeys::MakePromise(ErrorResult& aRv)
 }
 
 PromiseId
 MediaKeys::StorePromise(Promise* aPromise)
 {
   static uint32_t sEMEPromiseCount = 1;
   MOZ_ASSERT(aPromise);
   uint32_t id = sEMEPromiseCount++;
+
+  // Keep MediaKeys alive for the lifetime of its promises. Any still-pending
+  // promises are rejected in Shutdown().
+  AddRef();
+
   mPromises.Put(id, aPromise);
   return id;
 }
 
 already_AddRefed<Promise>
 MediaKeys::RetrievePromise(PromiseId aId)
 {
   MOZ_ASSERT(mPromises.Contains(aId));
   nsRefPtr<Promise> promise;
   mPromises.Remove(aId, getter_AddRefs(promise));
+  Release();
   return promise.forget();
 }
 
 void
 MediaKeys::RejectPromise(PromiseId aId, nsresult aExceptionCode)
 {
   nsRefPtr<Promise> promise(RetrievePromise(aId));
   if (!promise) {
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -551,20 +551,17 @@ nsRefPtr<MediaDecoderReader::VideoDataPr
 MP4Reader::RequestVideoData(bool aSkipToNextKeyframe,
                             int64_t aTimeThreshold)
 {
   MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
   VLOG("RequestVideoData skip=%d time=%lld", aSkipToNextKeyframe, aTimeThreshold);
 
   if (mShutdown) {
     NS_WARNING("RequestVideoData on shutdown MP4Reader!");
-    MonitorAutoLock lock(mVideo.mMonitor);
-    nsRefPtr<VideoDataPromise> p = mVideo.mPromise.Ensure(__func__);
-    p->Reject(CANCELED, __func__);
-    return p;
+    return VideoDataPromise::CreateAndReject(CANCELED, __func__);
   }
 
   MOZ_ASSERT(HasVideo() && mPlatform && mVideo.mDecoder);
 
   bool eos = false;
   if (ShouldSkip(aSkipToNextKeyframe, aTimeThreshold)) {
     uint32_t parsed = 0;
     eos = !SkipVideoDemuxToNextKeyFrame(aTimeThreshold, parsed);
@@ -585,24 +582,24 @@ MP4Reader::RequestVideoData(bool aSkipTo
   return p;
 }
 
 nsRefPtr<MediaDecoderReader::AudioDataPromise>
 MP4Reader::RequestAudioData()
 {
   MOZ_ASSERT(GetTaskQueue()->IsCurrentThreadIn());
   VLOG("RequestAudioData");
+  if (mShutdown) {
+    NS_WARNING("RequestAudioData on shutdown MP4Reader!");
+    return AudioDataPromise::CreateAndReject(CANCELED, __func__);
+  }
+
   MonitorAutoLock lock(mAudio.mMonitor);
   nsRefPtr<AudioDataPromise> p = mAudio.mPromise.Ensure(__func__);
-  if (!mShutdown) {
-    ScheduleUpdate(kAudio);
-  } else {
-    NS_WARNING("RequestAudioData on shutdown MP4Reader!");
-    p->Reject(CANCELED, __func__);
-  }
+  ScheduleUpdate(kAudio);
   return p;
 }
 
 void
 MP4Reader::ScheduleUpdate(TrackType aTrack)
 {
   auto& decoder = GetDecoderData(aTrack);
   decoder.mMonitor.AssertCurrentThreadOwns();
--- a/dom/media/gmp-plugin/gmp-test-decryptor.cpp
+++ b/dom/media/gmp-plugin/gmp-test-decryptor.cpp
@@ -266,27 +266,30 @@ private:
 
 static const string OpenAgainRecordId = "open-again-record-id";
 
 class OpenedSecondTimeContinuation : public OpenContinuation {
 public:
   explicit OpenedSecondTimeContinuation(GMPRecord* aRecord,
                                         TestManager* aTestManager,
                                         const string& aTestID)
-    : mRecord(aRecord), mTestmanager(aTestManager), mTestID(aTestID) {}
+    : mRecord(aRecord), mTestmanager(aTestManager), mTestID(aTestID) {
+    MOZ_ASSERT(aRecord);
+  }
 
   virtual void OpenComplete(GMPErr aStatus, GMPRecord* aRecord) MOZ_OVERRIDE {
     if (GMP_SUCCEEDED(aStatus)) {
       FakeDecryptor::Message("FAIL OpenSecondTimeContinuation should not be able to re-open record.");
     }
-
+    if (aRecord) {
+      aRecord->Close();
+    }
     // Succeeded, open should have failed.
     mTestmanager->EndTest(mTestID);
     mRecord->Close();
-    delete this;
   }
 
 private:
   GMPRecord* mRecord;
   TestManager* const mTestmanager;
   const string mTestID;
 };
 
@@ -296,22 +299,24 @@ public:
                               TestManager* aTestManager,
                               const string& aTestID)
     : mID(aID), mTestmanager(aTestManager), mTestID(aTestID) {}
 
   virtual void OpenComplete(GMPErr aStatus, GMPRecord* aRecord) MOZ_OVERRIDE {
     if (GMP_FAILED(aStatus)) {
       FakeDecryptor::Message("FAIL OpenAgainContinuation to open record initially.");
       mTestmanager->EndTest(mTestID);
+      if (aRecord) {
+        aRecord->Close();
+      }
       return;
     }
 
     auto cont = new OpenedSecondTimeContinuation(aRecord, mTestmanager, mTestID);
     GMPOpenRecord(mID, cont);
-    delete this;
   }
 
 private:
   const string mID;
   TestManager* const mTestmanager;
   const string mTestID;
 };
 
--- a/dom/media/gmp-plugin/gmp-test-storage.cpp
+++ b/dom/media/gmp-plugin/gmp-test-storage.cpp
@@ -166,54 +166,68 @@ GMPErr
 GMPRunOnMainThread(GMPTask* aTask)
 {
   MOZ_ASSERT(g_platform_api);
   return g_platform_api->runonmainthread(aTask);
 }
 
 class OpenRecordClient : public GMPRecordClient {
 public:
-  GMPErr Init(GMPRecord* aRecord,
-              OpenContinuation* aContinuation) {
-    mRecord = aRecord;
-    mContinuation = aContinuation;
-    return mRecord->Open();
+  /*
+   * This function will take the memory ownership of the parameters and
+   * delete them when done.
+   */
+  static void Open(const std::string& aRecordName,
+            OpenContinuation* aContinuation) {
+    MOZ_ASSERT(aContinuation);
+    (new OpenRecordClient(aContinuation))->Do(aRecordName);
   }
 
   virtual void OpenComplete(GMPErr aStatus) MOZ_OVERRIDE {
-    mContinuation->OpenComplete(aStatus, mRecord);
-    delete this;
+    Done(aStatus);
   }
 
   virtual void ReadComplete(GMPErr aStatus,
                             const uint8_t* aData,
-                            uint32_t aDataSize) MOZ_OVERRIDE { }
+                            uint32_t aDataSize) MOZ_OVERRIDE {
+    MOZ_CRASH("Should not reach here.");
+  }
 
-  virtual void WriteComplete(GMPErr aStatus) MOZ_OVERRIDE { }
+  virtual void WriteComplete(GMPErr aStatus) MOZ_OVERRIDE {
+    MOZ_CRASH("Should not reach here.");
+  }
 
 private:
+  explicit OpenRecordClient(OpenContinuation* aContinuation)
+    : mRecord(nullptr), mContinuation(aContinuation) {}
+
+  void Do(const std::string& aName) {
+    auto err = GMPOpenRecord(aName.c_str(), aName.size(), &mRecord, this);
+    if (GMP_FAILED(err) ||
+        GMP_FAILED(err = mRecord->Open())) {
+      Done(err);
+    }
+  }
+
+  void Done(GMPErr err) {
+    // mContinuation is responsible for closing mRecord.
+    mContinuation->OpenComplete(err, mRecord);
+    delete mContinuation;
+    delete this;
+  }
+
   GMPRecord* mRecord;
   OpenContinuation* mContinuation;
 };
 
-GMPErr
+void
 GMPOpenRecord(const std::string& aRecordName,
               OpenContinuation* aContinuation)
 {
-  MOZ_ASSERT(aContinuation);
-  GMPRecord* record;
-  OpenRecordClient* client = new OpenRecordClient();
-  auto err = GMPOpenRecord(aRecordName.c_str(),
-                           aRecordName.size(),
-                           &record,
-                           client);
-  if (GMP_FAILED(err)) {
-    return err;
-  }
-  return client->Init(record, aContinuation);
+  OpenRecordClient::Open(aRecordName, aContinuation);
 }
 
 GMPErr
 GMPEnumRecordNames(RecvGMPRecordIteratorPtr aRecvIteratorFunc,
                    void* aUserArg)
 {
   return g_platform_api->getrecordenumerator(aRecvIteratorFunc, aUserArg);
 }
--- a/dom/media/gmp-plugin/gmp-test-storage.h
+++ b/dom/media/gmp-plugin/gmp-test-storage.h
@@ -47,17 +47,17 @@ GMPErr
 GMPRunOnMainThread(GMPTask* aTask);
 
 class OpenContinuation {
 public:
   virtual ~OpenContinuation() {}
   virtual void OpenComplete(GMPErr aStatus, GMPRecord* aRecord) = 0;
 };
 
-GMPErr
+void
 GMPOpenRecord(const std::string& aRecordName,
               OpenContinuation* aContinuation);
 
 GMPErr
 GMPEnumRecordNames(RecvGMPRecordIteratorPtr aRecvIteratorFunc,
                    void* aUserArg);
 
 #endif // TEST_GMP_STORAGE_H__
--- a/dom/media/gmp/GMPDecryptorParent.cpp
+++ b/dom/media/gmp/GMPDecryptorParent.cpp
@@ -264,21 +264,19 @@ GMPDecryptorParent::RecvSessionError(con
   return true;
 }
 
 bool
 GMPDecryptorParent::RecvKeyStatusChanged(const nsCString& aSessionId,
                                          InfallibleTArray<uint8_t>&& aKeyId,
                                          const GMPMediaKeyStatus& aStatus)
 {
-  if (!mIsOpen) {
-    NS_WARNING("Trying to use a dead GMP decrypter!");
-    return false;
+  if (mIsOpen) {
+    mCallback->KeyStatusChanged(aSessionId, aKeyId, aStatus);
   }
-  mCallback->KeyStatusChanged(aSessionId, aKeyId, aStatus);
   return true;
 }
 
 bool
 GMPDecryptorParent::RecvSetCaps(const uint64_t& aCaps)
 {
   if (!mIsOpen) {
     NS_WARNING("Trying to use a dead GMP decrypter!");
--- a/dom/media/gmp/GMPStorageChild.cpp
+++ b/dom/media/gmp/GMPStorageChild.cpp
@@ -104,16 +104,21 @@ GMPStorageChild::CreateRecord(const nsCS
   MonitorAutoLock lock(mMonitor);
 
   if (mShutdown) {
     NS_WARNING("GMPStorage used after it's been shutdown!");
     return GMPClosedErr;
   }
 
   MOZ_ASSERT(aRecordName.Length() && aOutRecord);
+
+  if (HasRecord(aRecordName)) {
+    return GMPRecordInUse;
+  }
+
   nsRefPtr<GMPRecordImpl> record(new GMPRecordImpl(this, aRecordName, aClient));
   mRecords.Put(aRecordName, record); // Addrefs
 
   // The GMPRecord holds a self reference until the GMP calls Close() on
   // it. This means the object is always valid (even if neutered) while
   // the GMP expects it to be.
   record.forget(aOutRecord);
 
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -185,86 +185,88 @@ TrackBuffer::AppendData(LargeDataBuffer*
   int64_t start = 0, end = 0;
   bool gotMedia = mParser->ParseStartAndEndTimestamps(aData, start, end);
   bool gotInit = mParser->HasCompleteInitData();
 
   if (newInitData) {
     if (!gotInit) {
       // We need a new decoder, but we can't initialize it yet.
       nsRefPtr<SourceBufferDecoder> decoder =
-        NewDecoder(aTimestampOffset - mAdjustedTimestamp);
+        NewDecoder(aTimestampOffset);
       // The new decoder is stored in mDecoders/mCurrentDecoder, so we
       // don't need to do anything with 'decoder'. It's only a placeholder.
       if (!decoder) {
         mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
         return p;
       }
     } else {
-      if (!decoders.NewDecoder(aTimestampOffset - mAdjustedTimestamp)) {
+      if (!decoders.NewDecoder(aTimestampOffset)) {
         mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
         return p;
       }
     }
   } else if (!hadCompleteInitData && gotInit) {
     MOZ_ASSERT(mCurrentDecoder);
     // Queue pending decoder for initialization now that we have a full
     // init segment.
     decoders.AppendElement(mCurrentDecoder);
   }
 
   if (gotMedia) {
-    if (mLastEndTimestamp &&
+    if (mParser->IsMediaSegmentPresent(aData) && mLastEndTimestamp &&
         (!mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp.value()) ||
          mLastTimestampOffset != aTimestampOffset ||
          mDecoderPerSegment ||
          (mCurrentDecoder && mCurrentDecoder->WasTrimmed()))) {
       MSE_DEBUG("TrackBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
                 this, mLastStartTimestamp, mLastEndTimestamp.value(), start, end);
 
       if (!newInitData) {
         // This data is earlier in the timeline than data we have already
         // processed or not continuous, so we must create a new decoder
         // to handle the decoding.
         if (!hadCompleteInitData ||
-            !decoders.NewDecoder(aTimestampOffset - mAdjustedTimestamp)) {
+            !decoders.NewDecoder(aTimestampOffset)) {
           mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
           return p;
         }
         MSE_DEBUG("TrackBuffer(%p)::AppendData: Decoder marked as initialized.", this);
         AppendDataToCurrentResource(oldInit, 0);
       }
       mLastStartTimestamp = start;
     } else {
       MSE_DEBUG("TrackBuffer(%p)::AppendData: Segment last=[%lld, %lld] [%lld, %lld]",
                 this, mLastStartTimestamp, mLastEndTimestamp ? mLastEndTimestamp.value() : 0, start, end);
     }
     mLastEndTimestamp.reset();
     mLastEndTimestamp.emplace(end);
   }
 
-  if (gotMedia && start > 0 &&
-      (start < FUZZ_TIMESTAMP_OFFSET || start < mAdjustedTimestamp)) {
+  if (gotMedia && start != mAdjustedTimestamp &&
+      ((start < 0 && -start < FUZZ_TIMESTAMP_OFFSET && start < mAdjustedTimestamp) ||
+       (start > 0 && (start < FUZZ_TIMESTAMP_OFFSET || start < mAdjustedTimestamp)))) {
     AdjustDecodersTimestampOffset(mAdjustedTimestamp - start);
     mAdjustedTimestamp = start;
   }
 
   if (!AppendDataToCurrentResource(aData, end - start)) {
     mInitializationPromise.Reject(NS_ERROR_FAILURE, __func__);
     return p;
   }
 
   if (decoders.Length()) {
     // We're going to have to wait for the decoder to initialize, the promise
     // will be resolved once initialization completes.
     return p;
-  } else if (gotMedia) {
-    // Tell our reader that we have more data to ensure that playback starts if
-    // required when data is appended.
-    mParentDecoder->GetReader()->MaybeNotifyHaveData();
   }
+
+  // Tell our reader that we have more data to ensure that playback starts if
+  // required when data is appended.
+  mParentDecoder->GetReader()->MaybeNotifyHaveData();
+
   mInitializationPromise.Resolve(gotMedia, __func__);
   return p;
 }
 
 bool
 TrackBuffer::AppendDataToCurrentResource(LargeDataBuffer* aData, uint32_t aDuration)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -450,17 +452,18 @@ TrackBuffer::Buffered(dom::TimeRanges* a
 already_AddRefed<SourceBufferDecoder>
 TrackBuffer::NewDecoder(int64_t aTimestampOffset)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mParentDecoder);
 
   DiscardCurrentDecoder();
 
-  nsRefPtr<SourceBufferDecoder> decoder = mParentDecoder->CreateSubDecoder(mType, aTimestampOffset);
+  nsRefPtr<SourceBufferDecoder> decoder =
+    mParentDecoder->CreateSubDecoder(mType, aTimestampOffset - mAdjustedTimestamp);
   if (!decoder) {
     return nullptr;
   }
   ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
   mCurrentDecoder = decoder;
   mDecoders.AppendElement(decoder);
 
   mLastStartTimestamp = 0;
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -1867,17 +1867,17 @@ nsNPObjWrapper::GetNewOrUsed(NPP npp, JS
   if (!sNPObjWrappers.IsInitialized()) {
     // No hash yet (or any more), initialize it.
     if (!CreateNPObjWrapperTable()) {
       return nullptr;
     }
   }
 
   NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
-    (PL_DHashTableAdd(&sNPObjWrappers, npobj, fallible));
+    (PL_DHashTableAdd(&sNPObjWrappers, npobj));
 
   if (!entry) {
     // Out of memory
     JS_ReportOutOfMemory(cx);
 
     return nullptr;
   }
 
@@ -2030,17 +2030,17 @@ static NPP
 LookupNPP(NPObject *npobj)
 {
   if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
     nsJSObjWrapper* o = static_cast<nsJSObjWrapper*>(npobj);
     return o->mNpp;
   }
 
   NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
-    (PL_DHashTableAdd(&sNPObjWrappers, npobj, fallible));
+    (PL_DHashTableAdd(&sNPObjWrappers, npobj));
 
   if (!entry) {
     return nullptr;
   }
 
   NS_ASSERTION(entry->mNpp, "Live NPObject entry w/o an NPP!");
 
   return entry->mNpp;
--- a/dom/system/gonk/NetworkManager.js
+++ b/dom/system/gonk/NetworkManager.js
@@ -129,22 +129,61 @@ try {
 
 function defineLazyRegExp(obj, name, pattern) {
   obj.__defineGetter__(name, function() {
     delete obj[name];
     return obj[name] = new RegExp(pattern);
   });
 }
 
+function NetworkInterfaceLinks()
+{
+  this.resetLinks();
+}
+NetworkInterfaceLinks.prototype = {
+  linkRoutes: null,
+  gateways: null,
+  interfaceName: null,
+  extraRoutes: null,
+
+  setLinks: function(linkRoutes, gateways, interfaceName) {
+    this.linkRoutes = linkRoutes;
+    this.gateways = gateways;
+    this.interfaceName = interfaceName;
+  },
+
+  resetLinks: function() {
+    this.linkRoutes = [];
+    this.gateways = [];
+    this.interfaceName = "";
+    this.extraRoutes = [];
+  },
+
+  compareGateways: function(gateways) {
+    if (this.gateways.length != gateways.length) {
+      return false;
+    }
+
+    for (let i = 0; i < this.gateways.length; i++) {
+      if (this.gateways[i] != gateways[i]) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+};
+
 /**
  * This component watches for network interfaces changing state and then
  * adjusts routes etc. accordingly.
  */
 function NetworkManager() {
   this.networkInterfaces = {};
+  this.networkInterfaceLinks = {};
   Services.obs.addObserver(this, TOPIC_XPCOM_SHUTDOWN, false);
   Services.obs.addObserver(this, TOPIC_MOZSETTINGS_CHANGED, false);
 
   try {
     this._manageOfflineStatus =
       Services.prefs.getBoolPref(PREF_MANAGE_OFFLINE_STATUS);
   } catch(ex) {
     // Ignore.
@@ -298,16 +337,17 @@ NetworkManager.prototype = {
                                  Cr.NS_ERROR_INVALID_ARG);
     }
     let networkId = this.getNetworkId(network);
     if (networkId in this.networkInterfaces) {
       throw Components.Exception("Network with that type already registered!",
                                  Cr.NS_ERROR_INVALID_ARG);
     }
     this.networkInterfaces[networkId] = network;
+    this.networkInterfaceLinks[networkId] = new NetworkInterfaceLinks();
 
     if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
       debug("Force setting " + SETTINGS_DUN_REQUIRED + " to true.");
       this.tetheringSettings[SETTINGS_DUN_REQUIRED] = true;
     }
 
     Services.obs.notifyObservers(network, TOPIC_INTERFACE_REGISTERED, null);
     debug("Network '" + networkId + "' registered.");
@@ -315,17 +355,20 @@ NetworkManager.prototype = {
 
   _addSubnetRoutes: function(network) {
     let ips = {};
     let prefixLengths = {};
     let length = network.getAddresses(ips, prefixLengths);
     for (let i = 0; i < length; i++) {
       debug('Adding subnet routes: ' + ips.value[i] + '/' + prefixLengths.value[i]);
       gNetworkService.modifyRoute(Ci.nsINetworkService.MODIFY_ROUTE_ADD,
-                                  network.name, ips.value[i], prefixLengths.value[i]);
+                                  network.name, ips.value[i], prefixLengths.value[i])
+        .catch((aError) => {
+          debug("_addSubnetRoutes error: " + aError);
+        });
     }
   },
 
   updateNetworkInterface: function(network) {
     if (!(network instanceof Ci.nsINetworkInterface)) {
       throw Components.Exception("Argument must be nsINetworkInterface.",
                                  Cr.NS_ERROR_INVALID_ARG);
     }
@@ -341,18 +384,26 @@ NetworkManager.prototype = {
     // something through netd, so we add createNetwork/destroyNetwork
     // to deal with that explicitly.
 
     switch (network.state) {
       case Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED:
         gNetworkService.createNetwork(network.name, () => {
           // Add host route for data calls
           if (this.isNetworkTypeMobile(network.type)) {
-            gNetworkService.removeHostRoutes(network.name);
-            this.setHostRoutes(network);
+            let currentInterfaceLinks = this.networkInterfaceLinks[networkId];
+            let newLinkRoutes = network.getDnses().concat(network.httpProxyHost);
+            // If gateways have changed, remove all old routes first.
+            this._handleGateways(networkId, network.getGateways())
+              .then(() => this._updateRoutes(currentInterfaceLinks.linkRoutes,
+                                             newLinkRoutes,
+                                             network.getGateways(), network.name))
+              .then(() => currentInterfaceLinks.setLinks(newLinkRoutes,
+                                                         network.getGateways(),
+                                                         network.name));
           }
 
           // Remove pre-created default route and let setAndConfigureActive()
           // to set default route only on preferred network
           gNetworkService.removeDefaultRoute(network);
 
           // Dun type is a special case where we add the default route to a
           // secondary table.
@@ -381,17 +432,17 @@ NetworkManager.prototype = {
           Services.obs.notifyObservers(network, TOPIC_CONNECTION_STATE_CHANGED,
                                        this.convertConnectionType(network));
         });
 
         break;
       case Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED:
         // Remove host route for data calls
         if (this.isNetworkTypeMobile(network.type)) {
-          this.removeHostRoutes(network);
+          this._cleanupAllHostRoutes(networkId);
         }
         // Remove secondary default route for dun.
         if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
           this.removeSecondaryDefaultRoute(network);
         }
         // Remove routing table in /proc/net/route
         if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI) {
           gNetworkService.resetRoutingTable(network);
@@ -430,31 +481,40 @@ NetworkManager.prototype = {
       throw Components.Exception("Argument must be nsINetworkInterface.",
                                  Cr.NS_ERROR_INVALID_ARG);
     }
     let networkId = this.getNetworkId(network);
     if (!(networkId in this.networkInterfaces)) {
       throw Components.Exception("No network with that type registered.",
                                  Cr.NS_ERROR_INVALID_ARG);
     }
+
+    // This is for in case a network gets unregistered without being
+    // DISCONNECTED.
+    if (this.isNetworkTypeMobile(network.type)) {
+      this._cleanupAllHostRoutes(networkId);
+    }
+
     delete this.networkInterfaces[networkId];
 
     if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
       this.tetheringSettings[SETTINGS_DUN_REQUIRED] =
         libcutils.property_get("ro.tethering.dun_required") === "1";
     }
 
     Services.obs.notifyObservers(network, TOPIC_INTERFACE_UNREGISTERED, null);
     debug("Network '" + networkId + "' unregistered.");
   },
 
   _manageOfflineStatus: true,
 
   networkInterfaces: null,
 
+  networkInterfaceLinks: null,
+
   _dataDefaultServiceId: null,
 
   _preferredNetworkType: DEFAULT_PREFERRED_NETWORK_TYPE,
   get preferredNetworkType() {
     return this._preferredNetworkType;
   },
   set preferredNetworkType(val) {
     if ([Ci.nsINetworkInterface.NETWORK_TYPE_WIFI,
@@ -472,17 +532,34 @@ NetworkManager.prototype = {
          Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE].indexOf(val) == -1) {
       throw "Invalid network type";
     }
 
     this._overriddenActive = network;
     this.setAndConfigureActive();
   },
 
-  _updateRoutes: function(doAdd, ipAddresses, networkName, gateways) {
+  _updateRoutes: function(oldLinks, newLinks, gateways, interfaceName) {
+    // Returns items that are in base but not in target.
+    function getDifference(base, target) {
+      return base.filter(function(i) { return target.indexOf(i) < 0; });
+    }
+
+    let addedLinks = getDifference(newLinks, oldLinks);
+    let removedLinks = getDifference(oldLinks, newLinks);
+
+    if (addedLinks.length === 0 && removedLinks.length === 0) {
+      return Promise.resolve();
+    }
+
+    return this._setHostRoutes(false, removedLinks, interfaceName, gateways)
+      .then(this._setHostRoutes(true, addedLinks, interfaceName, gateways));
+  },
+
+  _setHostRoutes: function(doAdd, ipAddresses, networkName, gateways) {
     let getMaxPrefixLength = (aIp) => {
       return aIp.match(this.REGEXP_IPV4) ? IPV4_MAX_PREFIX_LENGTH : IPV6_MAX_PREFIX_LENGTH;
     }
 
     let promises = [];
 
     ipAddresses.forEach((aIpAddress) => {
       let gateway = this.selectGateway(gateways, aIpAddress);
@@ -512,56 +589,115 @@ NetworkManager.prototype = {
   },
 
   addHostRoute: function(network, host) {
     if (!this.isValidatedNetwork(network)) {
       return Promise.reject("Invalid network interface.");
     }
 
     return this.resolveHostname(network, host)
-      .then((ipAddresses) => this._updateRoutes(true,
-                                                ipAddresses,
-                                                network.name,
-                                                network.getGateways()));
+      .then((ipAddresses) => {
+        let promises = [];
+        let networkId = this.getNetworkId(network);
+
+        ipAddresses.forEach((aIpAddress) => {
+          let promise =
+            this._setHostRoutes(true, [aIpAddress], network.name, network.getGateways())
+              .then(() => this.networkInterfaceLinks[networkId].extraRoutes.push(aIpAddress));
+
+          promises.push(promise);
+        });
+
+        return Promise.all(promises);
+      });
   },
 
   removeHostRoute: function(network, host) {
     if (!this.isValidatedNetwork(network)) {
       return Promise.reject("Invalid network interface.");
     }
 
     return this.resolveHostname(network, host)
-      .then((ipAddresses) => this._updateRoutes(false,
-                                                ipAddresses,
-                                                network.name,
-                                                network.getGateways()));
+      .then((ipAddresses) => {
+        let promises = [];
+        let networkId = this.getNetworkId(network);
+
+        ipAddresses.forEach((aIpAddress) => {
+          let found = this.networkInterfaceLinks[networkId].extraRoutes.indexOf(aIpAddress);
+          if (found < 0) {
+            return; // continue
+          }
+
+          let promise =
+            this._setHostRoutes(false, [aIpAddress], network.name, network.getGateways())
+              .then(() => {
+                this.networkInterfaceLinks[networkId].extraRoutes.splice(found, 1);
+              }, () => {
+                // We should remove it even if the operation failed.
+                this.networkInterfaceLinks[networkId].extraRoutes.splice(found, 1);
+              });
+          promises.push(promise);
+        });
+
+        return Promise.all(promises);
+      });
   },
 
   isNetworkTypeSecondaryMobile: function(type) {
     return (type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS ||
             type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_SUPL ||
             type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_IMS ||
             type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN);
   },
 
   isNetworkTypeMobile: function(type) {
     return (type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE ||
             this.isNetworkTypeSecondaryMobile(type));
   },
 
-  setHostRoutes: function(network) {
-    let hosts = network.getDnses().concat(network.httpProxyHost);
+  _handleGateways: function(networkId, gateways) {
+    let currentNetworkLinks = this.networkInterfaceLinks[networkId];
+    if (currentNetworkLinks.gateways.length == 0 ||
+        currentNetworkLinks.compareGateways(gateways)) {
+      return Promise.resolve();
+    }
 
-    return this._updateRoutes(true, hosts, network.name, network.getGateways());
+    let currentExtraRoutes = currentNetworkLinks.extraRoutes;
+    return this._cleanupAllHostRoutes(networkId)
+      .then(() => {
+        // If gateways have changed, re-add extra host routes with new gateways.
+        if (currentExtraRoutes.length > 0) {
+          this._setHostRoutes(true,
+                              currentExtraRoutes,
+                              currentNetworkLinks.interfaceName,
+                              gateways)
+          .then(() => {
+            currentNetworkLinks.extraRoutes = currentExtraRoutes;
+          });
+        }
+      });
   },
 
-  removeHostRoutes: function(network) {
-    let hosts = network.getDnses().concat(network.httpProxyHost);
+  _cleanupAllHostRoutes: function(networkId) {
+    let currentNetworkLinks = this.networkInterfaceLinks[networkId];
+    let hostRoutes = currentNetworkLinks.linkRoutes.concat(
+      currentNetworkLinks.extraRoutes);
+
+    if (hostRoutes.length === 0) {
+      return Promise.resolve();
+    }
 
-    return this._updateRoutes(false, hosts, network.name, network.getGateways());
+    return this._setHostRoutes(false,
+                               hostRoutes,
+                               currentNetworkLinks.interfaceName,
+                               currentNetworkLinks.gateways)
+      .catch((aError) => {
+        debug("Error (" + aError + ") on _cleanupAllHostRoutes, keep proceeding.");
+      })
+      .then(() => currentNetworkLinks.resetLinks());
   },
 
   selectGateway: function(gateways, host) {
     for (let i = 0; i < gateways.length; i++) {
       let gateway = gateways[i];
       if (gateway.match(this.REGEXP_IPV4) && host.match(this.REGEXP_IPV4) ||
           gateway.indexOf(":") != -1 && host.indexOf(":") != -1) {
         return gateway;
--- a/dom/system/gonk/NetworkService.js
+++ b/dom/system/gonk/NetworkService.js
@@ -51,16 +51,76 @@ function isError(code) {
   let type = netdResponseType(code);
   return (type !== NETD_COMMAND_PROCEEDING && type !== NETD_COMMAND_OKAY);
 }
 
 function debug(msg) {
   dump("-*- NetworkService: " + msg + "\n");
 }
 
+function Task(id, params, setupFunction) {
+  this.id = id;
+  this.params = params;
+  this.setupFunction = setupFunction;
+}
+
+function NetworkWorkerRequestQueue(networkService) {
+  this.networkService = networkService;
+  this.tasks = [];
+}
+NetworkWorkerRequestQueue.prototype = {
+  runQueue: function() {
+    if (this.tasks.length === 0) {
+      return;
+    }
+
+    let task = this.tasks[0];
+    if (DEBUG) debug("run task id: " + task.id);
+
+    if (typeof task.setupFunction === 'function') {
+      // If setupFunction returns false, skip sending to Network Worker but call
+      // handleWorkerMessage() directly with task id, as if the response was
+      // returned from Network Worker.
+      if (!task.setupFunction()) {
+        this.networkService.handleWorkerMessage({id: task.id});
+        return;
+      }
+    }
+
+    gNetworkWorker.postMessage(task.params);
+  },
+
+  enqueue: function(id, params, setupFunction) {
+    if (DEBUG) debug("enqueue id: " + id);
+    this.tasks.push(new Task(id, params, setupFunction));
+
+    if (this.tasks.length === 1) {
+      this.runQueue();
+    }
+  },
+
+  dequeue: function(id) {
+    if (DEBUG) debug("dequeue id: " + id);
+
+    if (!this.tasks.length || this.tasks[0].id != id) {
+      if (DEBUG) debug("Id " + id + " is not on top of the queue");
+      return;
+    }
+
+    this.tasks.shift();
+    if (this.tasks.length > 0) {
+      // Run queue on the next tick.
+      Services.tm.currentThread.dispatch(() => {
+        this.runQueue();
+      }, Ci.nsIThread.DISPATCH_NORMAL);
+    }
+  }
+};
+
+
 /**
  * This component watches for network interfaces changing state and then
  * adjusts routes etc. accordingly.
  */
 function NetworkService() {
   if(DEBUG) debug("Starting net_worker.");
 
   let self = this;
@@ -71,41 +131,52 @@ function NetworkService() {
         self.handleWorkerMessage(event);
       }
     };
     gNetworkWorker.start(networkListener);
   }
   // Callbacks to invoke when a reply arrives from the net_worker.
   this.controlCallbacks = Object.create(null);
 
+  this.addedRoutes = new Map();
+  this.netWorkerRequestQueue = new NetworkWorkerRequestQueue(this);
   this.shutdown = false;
   Services.obs.addObserver(this, "xpcom-shutdown", false);
 }
 
 NetworkService.prototype = {
   classID:   NETWORKSERVICE_CID,
   classInfo: XPCOMUtils.generateCI({classID: NETWORKSERVICE_CID,
                                     contractID: NETWORKSERVICE_CONTRACTID,
                                     classDescription: "Network Service",
                                     interfaces: [Ci.nsINetworkService]}),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsINetworkService]),
 
   // Helpers
 
+  addedRoutes: null,
   idgen: 0,
-  controlMessage: function(params, callback) {
+  controlMessage: function(params, callback, setupFunction) {
     if (this.shutdown) {
       return;
     }
 
+    let id = this.idgen++;
+    params.id = id;
     if (callback) {
-      let id = this.idgen++;
-      params.id = id;
       this.controlCallbacks[id] = callback;
     }
+
+    // For now, we use setupFunction to determine if this command needs to be
+    // queued or not.
+    if (setupFunction) {
+      this.netWorkerRequestQueue.enqueue(id, params, setupFunction);
+      return;
+    }
+
     if (gNetworkWorker) {
       gNetworkWorker.postMessage(params);
     }
   },
 
   handleWorkerMessage: function(response) {
     if(DEBUG) debug("NetworkManager received message from worker: " + JSON.stringify(response));
     let id = response.id;
@@ -113,16 +184,18 @@ NetworkService.prototype = {
       Services.obs.notifyObservers(null, response.topic, response.reason);
       return;
     }
     let callback = this.controlCallbacks[id];
     if (callback) {
       callback.call(this, response);
       delete this.controlCallbacks[id];
     }
+
+    this.netWorkerRequestQueue.dequeue(id);
   },
 
   // nsINetworkService
 
   getNetworkInterfaceStats: function(networkName, callback) {
     if(DEBUG) debug("getNetworkInterfaceStats for " + networkName);
 
     let file = new FileUtils.File("/proc/net/dev");
@@ -302,57 +375,84 @@ NetworkService.prototype = {
     let options = {
       cmd: "removeDefaultRoute",
       ifname: network.name,
       gateways: gateways
     };
     this.controlMessage(options);
   },
 
+  _routeToString: function(interfaceName, host, prefixLength, gateway) {
+    return host + "-" + prefixLength + "-" + gateway + "-" + interfaceName;
+  },
+
   modifyRoute: function(action, interfaceName, host, prefixLength, gateway) {
     let command;
 
     switch (action) {
       case Ci.nsINetworkService.MODIFY_ROUTE_ADD:
         command = 'addHostRoute';
         break;
       case Ci.nsINetworkService.MODIFY_ROUTE_REMOVE:
         command = 'removeHostRoute';
         break;
       default:
         if (DEBUG) debug('Unknown action: ' + action);
         return Promise.reject();
     }
 
+    let route = this._routeToString(interfaceName, host, prefixLength, gateway);
+    let setupFunc = () => {
+      let count = this.addedRoutes.get(route);
+      if (DEBUG) debug(command + ": " + route + " -> " + count);
+
+      // Return false if there is no need to send the command to network worker.
+      if ((action == Ci.nsINetworkService.MODIFY_ROUTE_ADD && count) ||
+          (action == Ci.nsINetworkService.MODIFY_ROUTE_REMOVE &&
+           (!count || count > 1))) {
+        return false;
+      }
+
+      return true;
+    };
+
     if (DEBUG) debug(command + " " + host + " on " + interfaceName);
-    let deferred = Promise.defer();
     let options = {
       cmd: command,
       ifname: interfaceName,
       gateway: gateway,
       prefixLength: prefixLength,
       ip: host
     };
-    this.controlMessage(options, function(data) {
-      if (data.error) {
-        deferred.reject(data.reason);
-        return;
-      }
-      deferred.resolve();
+
+    return new Promise((aResolve, aReject) => {
+      this.controlMessage(options, (data) => {
+        let count = this.addedRoutes.get(route);
+
+        // Remove route from addedRoutes on success or failure.
+        if (action == Ci.nsINetworkService.MODIFY_ROUTE_REMOVE) {
+          if (count > 1) {
+            this.addedRoutes.set(route, count - 1);
+          } else {
+            this.addedRoutes.delete(route);
+          }
+        }
+
+        if (data.error) {
+          aReject(data.reason);
+          return;
+        }
+
+        if (action == Ci.nsINetworkService.MODIFY_ROUTE_ADD) {
+          this.addedRoutes.set(route, count ? count + 1 : 1);
+        }
+
+        aResolve();
+      }, setupFunc);
     });
-    return deferred.promise;
-  },
-
-  removeHostRoutes: function(ifname) {
-    if(DEBUG) debug("Going to remove all host routes on " + ifname);
-    let options = {
-      cmd: "removeHostRoutes",
-      ifname: ifname,
-    };
-    this.controlMessage(options);
   },
 
   addSecondaryRoute: function(ifname, route) {
     if(DEBUG) debug("Going to add route to secondary table on " + ifname);
     let options = {
       cmd: "addSecondaryRoute",
       ifname: ifname,
       ip: route.ip,
--- a/dom/system/gonk/NetworkUtils.cpp
+++ b/dom/system/gonk/NetworkUtils.cpp
@@ -1480,17 +1480,16 @@ void NetworkUtils::ExecuteCommand(Networ
     #define BUILD_ENTRY(c) {#c, &NetworkUtils::c}
 
     BUILD_ENTRY(removeNetworkRoute),
     BUILD_ENTRY(setDNS),
     BUILD_ENTRY(setDefaultRoute),
     BUILD_ENTRY(removeDefaultRoute),
     BUILD_ENTRY(addHostRoute),
     BUILD_ENTRY(removeHostRoute),
-    BUILD_ENTRY(removeHostRoutes),
     BUILD_ENTRY(addSecondaryRoute),
     BUILD_ENTRY(removeSecondaryRoute),
     BUILD_ENTRY(setNetworkInterfaceAlarm),
     BUILD_ENTRY(enableNetworkInterfaceAlarm),
     BUILD_ENTRY(disableNetworkInterfaceAlarm),
     BUILD_ENTRY(setWifiOperationMode),
     BUILD_ENTRY(setDhcpServer),
     BUILD_ENTRY(setWifiTethering),
@@ -1950,16 +1949,21 @@ CommandResult NetworkUtils::addHostRoute
   return CommandResult::Pending();
 }
 
 /**
  * Add host route for given network interface.
  */
 CommandResult NetworkUtils::addHostRouteLegacy(NetworkParams& aOptions)
 {
+  if (aOptions.mGateway.IsEmpty()) {
+    ERROR("addHostRouteLegacy does not support empty gateway.");
+    return EINVAL;
+  }
+
   NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname);
   NS_ConvertUTF16toUTF8 autoHostname(aOptions.mIp);
   NS_ConvertUTF16toUTF8 autoGateway(aOptions.mGateway);
   int type, prefix;
 
   type = getIpType(autoHostname.get());
   if (type != AF_INET && type != AF_INET6) {
     return EAFNOSUPPORT;
@@ -2021,37 +2025,16 @@ CommandResult NetworkUtils::removeHostRo
     return EINVAL;
   }
 
   prefix = type == AF_INET ? 32 : 128;
   return mNetUtils->do_ifc_remove_route(autoIfname.get(), autoHostname.get(),
                                         prefix, autoGateway.get());
 }
 
-/**
- * Remove the routes associated with the named interface.
- */
-CommandResult NetworkUtils::removeHostRoutes(NetworkParams& aOptions)
-{
-  if (SDK_VERSION < 20) {
-    return removeHostRoutesLegacy(aOptions);
-  }
-
-  NU_DBG("Don't know how to remove host routes on a interface");
-  return SUCCESS;
-}
-
-/**
- * Remove the routes associated with the named interface.
- */
-CommandResult NetworkUtils::removeHostRoutesLegacy(NetworkParams& aOptions)
-{
-  return mNetUtils->do_ifc_remove_host_routes(GET_CHAR(mIfname));
-}
-
 CommandResult NetworkUtils::removeNetworkRoute(NetworkParams& aOptions)
 {
   if (SDK_VERSION < 20) {
     return removeNetworkRouteLegacy(aOptions);
   }
 
   static CommandFunc COMMAND_CHAIN[] = {
     clearAddrForInterface,
--- a/dom/system/gonk/NetworkUtils.h
+++ b/dom/system/gonk/NetworkUtils.h
@@ -286,17 +286,16 @@ private:
   CommandResult dhcpRequest(NetworkParams& aOptions);
   CommandResult enableInterface(NetworkParams& aOptions);
   CommandResult disableInterface(NetworkParams& aOptions);
   CommandResult resetConnections(NetworkParams& aOptions);
   CommandResult setDefaultRoute(NetworkParams& aOptions);
   CommandResult addHostRoute(NetworkParams& aOptions);
   CommandResult removeDefaultRoute(NetworkParams& aOptions);
   CommandResult removeHostRoute(NetworkParams& aOptions);
-  CommandResult removeHostRoutes(NetworkParams& aOptions);
   CommandResult removeNetworkRoute(NetworkParams& aOptions);
   CommandResult setDNS(NetworkParams& aOptions);
   CommandResult addSecondaryRoute(NetworkParams& aOptions);
   CommandResult removeSecondaryRoute(NetworkParams& aOptions);
   CommandResult setNetworkInterfaceAlarm(NetworkParams& aOptions);
   CommandResult enableNetworkInterfaceAlarm(NetworkParams& aOptions);
   CommandResult disableNetworkInterfaceAlarm(NetworkParams& aOptions);
   CommandResult setWifiOperationMode(NetworkParams& aOptions);
@@ -305,17 +304,16 @@ private:
   CommandResult setUSBTethering(NetworkParams& aOptions);
   CommandResult enableUsbRndis(NetworkParams& aOptions);
   CommandResult updateUpStream(NetworkParams& aOptions);
   CommandResult createNetwork(NetworkParams& aOptions);
   CommandResult destroyNetwork(NetworkParams& aOptions);
 
   CommandResult addHostRouteLegacy(NetworkParams& aOptions);
   CommandResult removeHostRouteLegacy(NetworkParams& aOptions);
-  CommandResult removeHostRoutesLegacy(NetworkParams& aOptions);
   CommandResult setDefaultRouteLegacy(NetworkParams& aOptions);
   CommandResult removeDefaultRouteLegacy(NetworkParams& aOptions);
   CommandResult removeNetworkRouteLegacy(NetworkParams& aOptions);
 
 
   /**
    * function pointer array holds all netd commands should be executed
    * in sequence to accomplish a given command by other module.
--- a/dom/system/gonk/nsINetworkService.idl
+++ b/dom/system/gonk/nsINetworkService.idl
@@ -154,17 +154,17 @@ interface nsIDhcpRequestCallback : nsISu
    */
   void dhcpRequestResult(in boolean success, in jsval dhcpInfo);
 };
 
 
 /**
  * Provide network services.
  */
-[scriptable, uuid(138ab267-0007-4c3e-8bfc-a7e1be0b5a6f)]
+[scriptable, uuid(e40dd966-cb04-4dc7-ac4b-6382769c00b9)]
 interface nsINetworkService : nsISupports
 {
   const long MODIFY_ROUTE_ADD    = 0;
   const long MODIFY_ROUTE_REMOVE = 1;
 
   /**
    * Enable or disable Wifi Tethering
    *
@@ -341,24 +341,16 @@ interface nsINetworkService : nsISupport
    */
   jsval modifyRoute(in long      action,
                     in DOMString interfaceName,
                     in DOMString host,
                     in long      prefixLength,
                     [optional] in DOMString gateway);
 
   /**
-   * Remove all host routes.
-   *
-   * @param interfaceName
-   *        The interface name we want remove from the routing table.
-   */
-  void removeHostRoutes(in DOMString interfaceName);
-
-  /**
    * Add route to secondary routing table.
    *
    * @param interfaceName
    *        The network interface for this route.
    * @param route
    *        The route info should have the following fields:
    *        .ip: destination ip address
    *        .prefix: destination prefix
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -77,148 +77,16 @@ let RILQUIRKS_HAVE_QUERY_ICC_LOCK_RETRY_
 let RILQUIRKS_SEND_STK_PROFILE_DOWNLOAD;
 
 // Ril quirk to attach data registration on demand.
 let RILQUIRKS_DATA_REGISTRATION_ON_DEMAND;
 
 // Ril quirk to control the uicc/data subscription.
 let RILQUIRKS_SUBSCRIPTION_CONTROL;
 
-function BufObject(aContext) {
-  this.context = aContext;
-}
-BufObject.prototype = {
-  context: null,
-
-  mToken: 0,
-  mTokenRequestMap: null,
-
-  init: function() {
-    this._init();
-
-    // This gets incremented each time we send out a parcel.
-    this.mToken = 1;
-
-    // Maps tokens we send out with requests to the request type, so that
-    // when we get a response parcel back, we know what request it was for.
-    this.mTokenRequestMap = new Map();
-  },
-
-  /**
-   * Process one parcel.
-   */
-  processParcel: function() {
-    let response_type = this.readInt32();
-
-    let request_type, options;
-    if (response_type == RESPONSE_TYPE_SOLICITED) {
-      let token = this.readInt32();
-      let error = this.readInt32();
-
-      options = this.mTokenRequestMap.get(token);
-      if (!options) {
-        if (DEBUG) {
-          this.context.debug("Suspicious uninvited request found: " +
-                             token + ". Ignored!");
-        }
-        return;
-      }
-
-      this.mTokenRequestMap.delete(token);
-      request_type = options.rilRequestType;
-
-      options.rilRequestError = error;
-      if (DEBUG) {
-        this.context.debug("Solicited response for request type " + request_type +
-                           ", token " + token + ", error " + error);
-      }
-    } else if (response_type == RESPONSE_TYPE_UNSOLICITED) {
-      request_type = this.readInt32();
-      if (DEBUG) {
-        this.context.debug("Unsolicited response for request type " + request_type);
-      }
-    } else {
-      if (DEBUG) {
-        this.context.debug("Unknown response type: " + response_type);
-      }
-      return;
-    }
-
-    this.context.RIL.handleParcel(request_type, this.readAvailable, options);
-  },
-
-  /**
-   * Start a new outgoing parcel.
-   *
-   * @param type
-   *        Integer specifying the request type.
-   * @param options [optional]
-   *        Object containing information about the request, e.g. the
-   *        original main thread message object that led to the RIL request.
-   */
-  newParcel: function(type, options) {
-    if (DEBUG) {
-      this.context.debug("New outgoing parcel of type " + type +
-                         ", token " + this.mToken);
-    }
-
-    // We're going to leave room for the parcel size at the beginning.
-    this.outgoingIndex = this.PARCEL_SIZE_SIZE;
-    this.writeInt32(this._reMapRequestType(type));
-    this.writeInt32(this.mToken);
-
-    if (!options) {
-      options = {};
-    }
-    options.rilRequestType = type;
-    options.rilRequestError = null;
-    this.mTokenRequestMap.set(this.mToken, options);
-    this.mToken++;
-    return this.mToken;
-  },
-
-  simpleRequest: function(type, options) {
-    this.newParcel(type, options);
-    this.sendParcel();
-  },
-
-  onSendParcel: function(parcel) {
-    postRILMessage(this.context.clientId, parcel);
-  },
-
-  /**
-   * Remapping the request type to different values based on RIL version.
-   * We only have to do this for SUBSCRIPTION right now, so I just make it
-   * simple. A generic logic or structure could be discussed if we have more
-   * use cases, especially the cases from different partners.
-   */
-  _reMapRequestType: function(type) {
-    let newType = type;
-    switch (type) {
-      case REQUEST_SET_UICC_SUBSCRIPTION:
-      case REQUEST_SET_DATA_SUBSCRIPTION:
-        if (this.context.RIL.version < 9) {
-          // Shift the CAF's proprietary parcels. Please see
-          // https://www.codeaurora.org/cgit/quic/la/platform/hardware/ril/tree/include/telephony/ril.h?h=b2g_jb_3.2
-          newType = type - 1;
-        }
-        break;
-    }
-
-    return newType;
-  }
-};
-
-(function() {
-  let base = require("resource://gre/modules/workers/worker_buf.js").Buf;
-  for (let p in base) {
-    BufObject.prototype[p] = base[p];
-  }
-})();
-
 const TELEPHONY_REQUESTS = [
   REQUEST_GET_CURRENT_CALLS,
   REQUEST_ANSWER,
   REQUEST_CONFERENCE,
   REQUEST_DIAL,
   REQUEST_DIAL_EMERGENCY_CALL,
   REQUEST_HANGUP,
   REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND,
--- a/dom/tests/mochitest/webcomponents/mochitest.ini
+++ b/dom/tests/mochitest/webcomponents/mochitest.ini
@@ -5,16 +5,17 @@ support-files =
 [test_bug900724.html]
 [test_bug1017896.html]
 [test_content_element.html]
 [test_custom_element_adopt_callbacks.html]
 [test_custom_element_callback_innerhtml.html]
 [test_custom_element_clone_callbacks.html]
 [test_custom_element_clone_callbacks_extended.html]
 [test_custom_element_import_node_created_callback.html]
+[test_custom_element_in_shadow.html]
 [test_nested_content_element.html]
 [test_dest_insertion_points.html]
 [test_dest_insertion_points_shadow.html]
 [test_fallback_dest_insertion_points.html]
 [test_detached_style.html]
 [test_dynamic_content_element_matching.html]
 [test_document_register.html]
 [test_document_register_base_queue.html]
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_custom_element_in_shadow.html
@@ -0,0 +1,132 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1087460
+-->
+<head>
+  <title>Test for custom element callbacks in shadow DOM.</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1087460">Bug 1087460</a>
+<div id="container"></div>
+
+<script>
+
+// Test callback for custom element when used after registration.
+
+var createdCallbackCount = 0;
+var attachedCallbackCount = 0;
+var detachedCallbackCount = 0;
+var attributeChangedCallbackCount = 0;
+
+var p1 = Object.create(HTMLElement.prototype);
+
+p1.createdCallback = function() {
+  createdCallbackCount++;
+};
+
+p1.attachedCallback = function() {
+  attachedCallbackCount++;
+};
+
+p1.detachedCallback = function() {
+  detachedCallbackCount++;
+};
+
+p1.attributeChangedCallback = function(name, oldValue, newValue) {
+  attributeChangedCallbackCount++;
+};
+
+document.registerElement("x-foo", { prototype: p1 });
+
+var container = document.getElementById("container");
+var shadow = container.createShadowRoot();
+
+is(createdCallbackCount, 0, "createdCallback should not be called more than once in this test.");
+var customElem = document.createElement("x-foo");
+is(createdCallbackCount, 1, "createdCallback should be called after creating custom element.");
+
+is(attributeChangedCallbackCount, 0, "attributeChangedCallback should not be called after just creating an element.");
+customElem.setAttribute("data-foo", "bar");
+is(attributeChangedCallbackCount, 1, "attributeChangedCallback should be called after setting an attribute.");
+
+is(attachedCallbackCount, 0, "attachedCallback should not be called on an element that is not in a document/composed document.");
+shadow.appendChild(customElem);
+is(attachedCallbackCount, 1, "attachedCallback should be called after attaching custom element to the composed document.");
+
+is(detachedCallbackCount, 0, "detachedCallback should not be called without detaching custom element.");
+shadow.removeChild(customElem);
+is(detachedCallbackCount, 1, "detachedCallback should be called after detaching custom element from the composed document.");
+
+// Test callback for custom element already in the composed doc when created.
+
+createdCallbackCount = 0;
+attachedCallbackCount = 0;
+detachedCallbackCount = 0;
+attributeChangedCallbackCount = 0;
+
+shadow.innerHTML = "<x-foo></x-foo>";
+is(createdCallbackCount, 1, "createdCallback should be called after creating a custom element.");
+is(attachedCallbackCount, 1, "attachedCallback should be called after creating an element in the composed document.");
+
+shadow.innerHTML = "";
+is(detachedCallbackCount, 1, "detachedCallback should be called after detaching custom element from the composed document.");
+
+// Test callback for custom element in shadow DOM when host attached/detached to/from document.
+
+createdCallbackCount = 0;
+attachedCallbackCount = 0;
+detachedCallbackCount = 0;
+attributeChangedCallbackCount = 0;
+
+var host = document.createElement("div");
+shadow = host.createShadowRoot();
+customElem = document.createElement("x-foo");
+
+is(attachedCallbackCount, 0, "attachedCallback should not be called on newly created element.");
+shadow.appendChild(customElem);
+is(attachedCallbackCount, 0, "attachedCallback should not be called on attaching to a tree that is not in the composed document.");
+
+is(attachedCallbackCount, 0, "detachedCallback should not be called.");
+shadow.removeChild(customElem);
+is(detachedCallbackCount, 0, "detachedCallback should not be called when detaching from a tree that is not in the composed document.");
+
+shadow.appendChild(customElem);
+is(attachedCallbackCount, 0, "attachedCallback should still not be called after reattaching to a shadow tree that is not in the composed document.");
+
+container.appendChild(host);
+is(attachedCallbackCount, 1, "attachedCallback should be called after host is inserted into document.");
+
+container.removeChild(host);
+is(detachedCallbackCount, 1, "detachedCallback should be called after host is removed from document.");
+
+// Test callback for custom element for upgraded element.
+
+createdCallbackCount = 0;
+attachedCallbackCount = 0;
+detachedCallbackCount = 0;
+attributeChangedCallbackCount = 0;
+
+shadow = container.shadowRoot;
+shadow.innerHTML = "<x-bar></x-bar>";
+
+var p2 = Object.create(HTMLElement.prototype);
+
+p2.createdCallback = function() {
+  createdCallbackCount++;
+};
+
+p2.attachedCallback = function() {
+  attachedCallbackCount++;
+};
+
+document.registerElement("x-bar", { prototype: p2 });
+is(createdCallbackCount, 1, "createdCallback should be called after registering element.");
+is(attachedCallbackCount, 1, "attachedCallback should be called after upgrading element in composed document.");
+
+</script>
+
+</body>
+</html>
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -1991,17 +1991,17 @@ ServiceWorkerManager::CreateServiceWorke
   // Alternatively we could persist the original load group values and use
   // them here.
   rv = NS_NewLoadGroup(getter_AddRefs(info.mLoadGroup), info.mPrincipal);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsRefPtr<ServiceWorker> serviceWorker;
-  RuntimeService* rs = RuntimeService::GetService();
+  RuntimeService* rs = RuntimeService::GetOrCreateService();
   if (!rs) {
     return NS_ERROR_FAILURE;
   }
 
   AutoJSAPI jsapi;
   jsapi.Init();
   rv = rs->CreateServiceWorkerFromLoadInfo(jsapi.cx(), &info,
                                            NS_ConvertUTF8toUTF16(aScriptSpec),
--- a/dom/workers/test/test_worker_interfaces.js
+++ b/dom/workers/test/test_worker_interfaces.js
@@ -30,17 +30,17 @@ var ecmaGlobals =
     "Float32Array",
     "Float64Array",
     "Function",
     "Infinity",
     "Int16Array",
     "Int32Array",
     "Int8Array",
     "InternalError",
-    {name: "Intl", desktop: true},
+    {name: "Intl", b2g: false, android: false},
     "Iterator",
     "JSON",
     "Map",
     "Math",
     "NaN",
     "Number",
     "Object",
     "Proxy",
@@ -162,31 +162,32 @@ var interfaceNamesInGlobalScope =
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "WorkerLocation",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "WorkerNavigator",
 // IMPORTANT: Do not change this list without review from a DOM peer!
   ];
 // IMPORTANT: Do not change the list above without review from a DOM peer!
 
-function createInterfaceMap(prefMap, permissionMap, version, userAgent) {
+function createInterfaceMap(prefMap, permissionMap, version, userAgent, isB2G) {
   var isNightly = version.endsWith("a1");
   var isRelease = !version.contains("a");
   var isDesktop = !/Mobile|Tablet/.test(userAgent);
-  var isB2G = !isDesktop && !userAgent.contains("Android");
+  var isAndroid = !!navigator.userAgent.contains("Android");
 
   var interfaceMap = {};
 
   function addInterfaces(interfaces)
   {
     for (var entry of interfaces) {
       if (typeof(entry) === "string") {
         interfaceMap[entry] = true;
       } else if ((entry.nightly === !isNightly) ||
                  (entry.desktop === !isDesktop) ||
+                 (entry.android === !isAndroid) ||
                  (entry.b2g === !isB2G) ||
                  (entry.release === !isRelease) ||
                  (entry.pref && !prefMap[entry.pref])  ||
                  (entry.permission && !permissionMap[entry.permission])) {
         interfaceMap[entry.name] = false;
       } else {
         interfaceMap[entry.name] = true;
       }
@@ -194,18 +195,18 @@ function createInterfaceMap(prefMap, per
   }
 
   addInterfaces(ecmaGlobals);
   addInterfaces(interfaceNamesInGlobalScope);
 
   return interfaceMap;
 }
 
-function runTest(prefMap, permissionMap, version, userAgent) {
-  var interfaceMap = createInterfaceMap(prefMap, permissionMap, version, userAgent);
+function runTest(prefMap, permissionMap, version, userAgent, isB2G) {
+  var interfaceMap = createInterfaceMap(prefMap, permissionMap, version, userAgent, isB2G);
   for (var name of Object.getOwnPropertyNames(self)) {
     // An interface name should start with an upper case character.
     if (!/^[A-Z]/.test(name)) {
       continue;
     }
     ok(interfaceMap[name],
        "If this is failing: DANGER, are you sure you want to expose the new interface " + name +
        " to all webpages as a property on the worker? Do not make a change to this file without a " +
@@ -247,14 +248,16 @@ function appendPermissions(permissions, 
 var permissions = [];
 appendPermissions(permissions, ecmaGlobals);
 appendPermissions(permissions, interfaceNamesInGlobalScope);
 
 workerTestGetPrefs(prefs, function(prefMap) {
   workerTestGetPermissions(permissions, function(permissionMap) {
     workerTestGetVersion(function(version) {
       workerTestGetUserAgent(function(userAgent) {
-        runTest(prefMap, permissionMap, version, userAgent);
-        workerTestDone();
+        workerTestGetIsB2G(function(isB2G) {
+          runTest(prefMap, permissionMap, version, userAgent, isB2G);
+          workerTestDone();
+	});
       });
     });
   });
 });
--- a/dom/workers/test/worker_driver.js
+++ b/dom/workers/test/worker_driver.js
@@ -71,16 +71,21 @@ function workerTestExec(script) {
         type: 'returnUserAgent',
         result: navigator.userAgent
       });
     } else if (event.data.type == 'getOSCPU') {
       worker.postMessage({
         type: 'returnOSCPU',
         result: navigator.oscpu
       });
+    } else if (event.data.type == 'getIsB2G') {
+      worker.postMessage({
+        type: 'returnIsB2G',
+        result: SpecialPowers.isB2G
+      });
     }
   }
 
   worker.onerror = function(event) {
     ok(false, 'Worker had an error: ' + event.data);
     SimpleTest.finish();
   };
 
--- a/dom/workers/test/worker_wrapper.js
+++ b/dom/workers/test/worker_wrapper.js
@@ -94,16 +94,29 @@ function workerTestGetOSCPU(cb) {
     removeEventListener('message', workerTestGetOSCPUCB);
     cb(e.data.result);
   });
   postMessage({
     type: 'getOSCPU'
   });
 }
 
+function workerTestGetIsB2G(cb) {
+  addEventListener('message', function workerTestGetIsB2GCB(e) {
+    if (e.data.type !== 'returnIsB2G') {
+      return;
+    }
+    removeEventListener('message', workerTestGetIsB2GCB);
+    cb(e.data.result);
+  });
+  postMessage({
+    type: 'getIsB2G'
+  });
+}
+
 addEventListener('message', function workerWrapperOnMessage(e) {
   removeEventListener('message', workerWrapperOnMessage);
   var data = e.data;
   try {
     importScripts(data.script);
   } catch(e) {
     postMessage({
       type: 'status',
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -211,18 +211,17 @@ XULDocument::~XULDocument()
     // decls never got resolved.
     mForwardReferences.Clear();
     // Likewise for any references we have to IDs where we might
     // look for persisted data:
     mPersistenceIds.Clear();
 
     // Destroy our broadcaster map.
     if (mBroadcasterMap) {
-        PL_DHashTableFinish(mBroadcasterMap);
-        delete mBroadcasterMap;
+        PL_DHashTableDestroy(mBroadcasterMap);
     }
 
     delete mTemplateBuilderTable;
 
     Preferences::UnregisterCallback(XULDocument::DirectionChanged,
                                     "intl.uidirection.", this);
 
     if (mOffThreadCompileStringBuf) {
@@ -764,27 +763,32 @@ XULDocument::AddBroadcastListenerFor(Ele
         PL_DHashVoidPtrKeyStub,
         PL_DHashMatchEntryStub,
         PL_DHashMoveEntryStub,
         ClearBroadcasterMapEntry,
         nullptr
     };
 
     if (! mBroadcasterMap) {
-        mBroadcasterMap = new PLDHashTable();
-        PL_DHashTableInit(mBroadcasterMap, &gOps, sizeof(BroadcasterMapEntry));
+        mBroadcasterMap = PL_NewDHashTable(&gOps, sizeof(BroadcasterMapEntry));
+
+        if (! mBroadcasterMap) {
+            aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+            return;
+        }
     }
 
     BroadcasterMapEntry* entry =
         static_cast<BroadcasterMapEntry*>
                    (PL_DHashTableSearch(mBroadcasterMap, &aBroadcaster));
 
     if (!entry) {
-        entry = static_cast<BroadcasterMapEntry*>
-            (PL_DHashTableAdd(mBroadcasterMap, &aBroadcaster, fallible));
+        entry =
+            static_cast<BroadcasterMapEntry*>
+                       (PL_DHashTableAdd(mBroadcasterMap, &aBroadcaster));
 
         if (! entry) {
             aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
             return;
         }
 
         entry->mBroadcaster = &aBroadcaster;
 
--- a/dom/xul/templates/nsContentSupportMap.h
+++ b/dom/xul/templates/nsContentSupportMap.h
@@ -23,18 +23,17 @@ class nsContentSupportMap {
 public:
     nsContentSupportMap() { Init(); }
     ~nsContentSupportMap() { Finish(); }
 
     nsresult Put(nsIContent* aElement, nsTemplateMatch* aMatch) {
         if (!mMap.IsInitialized())
             return NS_ERROR_NOT_INITIALIZED;
 
-        PLDHashEntryHdr* hdr =
-            PL_DHashTableAdd(&mMap, aElement, mozilla::fallible);
+        PLDHashEntryHdr* hdr = PL_DHashTableAdd(&mMap, aElement);
         if (!hdr)
             return NS_ERROR_OUT_OF_MEMORY;
 
         Entry* entry = static_cast<Entry*>(hdr);
         NS_ASSERTION(entry->mMatch == nullptr, "over-writing entry");
         entry->mContent = aElement;
         entry->mMatch   = aMatch;
         return NS_OK;
--- a/dom/xul/templates/nsTemplateMap.h
+++ b/dom/xul/templates/nsTemplateMap.h
@@ -32,18 +32,17 @@ public:
 
     ~nsTemplateMap() { Finish(); }
 
     void
     Put(nsIContent* aContent, nsIContent* aTemplate) {
         NS_ASSERTION(!PL_DHashTableSearch(&mTable, aContent),
                      "aContent already in map");
 
-        Entry* entry = static_cast<Entry*>
-            (PL_DHashTableAdd(&mTable, aContent, fallible));
+        Entry* entry = static_cast<Entry*>(PL_DHashTableAdd(&mTable, aContent));
 
         if (entry) {
             entry->mContent = aContent;
             entry->mTemplate = aTemplate;
         }
     }
 
     void
--- a/embedding/components/commandhandler/nsCommandParams.cpp
+++ b/embedding/components/commandhandler/nsCommandParams.cpp
@@ -225,18 +225,17 @@ nsCommandParams::GetOrMakeEntry(const ch
 {
   HashEntry *foundEntry =
     (HashEntry *)PL_DHashTableSearch(&mValuesHash, (void *)aName);
   if (foundEntry) { // reuse existing entry
     foundEntry->Reset(entryType);
     return foundEntry;
   }
 
-  foundEntry = static_cast<HashEntry*>
-    (PL_DHashTableAdd(&mValuesHash, (void *)aName, fallible));
+  foundEntry = (HashEntry *)PL_DHashTableAdd(&mValuesHash, (void *)aName);
   if (!foundEntry) {
     return nullptr;
   }
 
   // Use placement new. Our ctor does not clobber keyHash, which is important.
   new (foundEntry) HashEntry(entryType, aName);
   return foundEntry;
 }
--- a/gfx/layers/LayerMetricsWrapper.h
+++ b/gfx/layers/LayerMetricsWrapper.h
@@ -333,16 +333,25 @@ public:
 
   const nsIntRect* GetClipRect() const
   {
     MOZ_ASSERT(IsValid());
 
     return mLayer->GetClipRect();
   }
 
+  bool GetForceDispatchToContentRegion() const {
+    MOZ_ASSERT(IsValid());
+
+    if (mLayer->AsContainerLayer()) {
+      return mLayer->AsContainerLayer()->GetForceDispatchToContentRegion();
+    }
+    return false;
+  }
+
   // Expose an opaque pointer to the layer. Mostly used for printf
   // purposes. This is not intended to be a general-purpose accessor
   // for the underlying layer.
   const void* GetLayer() const
   {
     MOZ_ASSERT(IsValid());
 
     return (void*)mLayer;
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -919,17 +919,18 @@ ContainerLayer::ContainerLayer(LayerMana
     mPreYScale(1.0f),
     mInheritedXScale(1.0f),
     mInheritedYScale(1.0f),
     mPresShellResolution(1.0f),
     mScaleToResolution(false),
     mUseIntermediateSurface(false),
     mSupportsComponentAlphaChildren(false),
     mMayHaveReadbackChild(false),
-    mChildrenChanged(false)
+    mChildrenChanged(false),
+    mForceDispatchToContentRegion(false)
 {
   mContentFlags = 0; // Clear NO_TEXT, NO_TEXT_OVER_TRANSPARENT
 }
 
 ContainerLayer::~ContainerLayer() {}
 
 bool
 ContainerLayer::InsertAfter(Layer* aChild, Layer* aAfter)
@@ -1076,16 +1077,17 @@ ContainerLayer::RepositionChild(Layer* a
 }
 
 void
 ContainerLayer::FillSpecificAttributes(SpecificLayerAttributes& aAttrs)
 {
   aAttrs = ContainerLayerAttributes(mPreXScale, mPreYScale,
                                     mInheritedXScale, mInheritedYScale,
                                     mPresShellResolution, mScaleToResolution,
+                                    mForceDispatchToContentRegion,
                                     reinterpret_cast<uint64_t>(mHMDInfo.get()));
 }
 
 bool
 ContainerLayer::HasMultipleChildren()
 {
   uint32_t count = 0;
   for (Layer* child = GetFirstChild(); child; child = child->GetNextSibling()) {
@@ -1743,16 +1745,19 @@ ContainerLayer::PrintInfo(std::stringstr
     aStream << " [usesTmpSurf]";
   }
   if (1.0 != mPreXScale || 1.0 != mPreYScale) {
     aStream << nsPrintfCString(" [preScale=%g, %g]", mPreXScale, mPreYScale).get();
   }
   if (mScaleToResolution) {
     aStream << nsPrintfCString(" [presShellResolution=%g]", mPresShellResolution).get();
   }
+  if (mForceDispatchToContentRegion) {
+    aStream << " [force-dtc]";
+  }
   if (mHMDInfo) {
     aStream << nsPrintfCString(" [hmd=%p]", mHMDInfo.get()).get();
   }
 }
 
 void
 ContainerLayer::DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent)
 {
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -911,16 +911,20 @@ public:
    * to be ignored. If a layer has a mask layer, and that mask layer's alpha
    * value is zero at the event point, then the layer and its subtree should
    * be ignored.
    * For each layer, if the point is outside its hit region, we ignore the layer
    * and move onto the next. If the point is inside its hit region but
    * outside the dispatch-to-content region, we can initiate a gesture without
    * consulting the content thread. Otherwise we must dispatch the event to
    * content.
+   * Note that if a layer or any ancestor layer returns true for
+   * GetForceDispatchToContentRegion() then we must treat the dispatch-to-content
+   * region as encompassing the hit region, and therefore must consult the
+   * content thread before initiating a gesture.
    */
   /**
    * CONSTRUCTION PHASE ONLY
    * Set the event handling region.
    */
   void SetEventRegions(const EventRegions& aRegions)
   {
     if (mEventRegions != aRegions) {
@@ -1962,16 +1966,30 @@ public:
    * content flag set.
    */
   static bool HasOpaqueAncestorLayer(Layer* aLayer);
 
   void SetChildrenChanged(bool aVal) {
     mChildrenChanged = aVal;
   }
 
+  void SetForceDispatchToContentRegion(bool aVal) {
+    if (mForceDispatchToContentRegion == aVal) {
+      return;
+    }
+
+    MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) ForceDispatchToContentRegion", this));
+    mForceDispatchToContentRegion = aVal;
+    Mutated();
+  }
+
+  bool GetForceDispatchToContentRegion() const {
+    return mForceDispatchToContentRegion;
+  }
+
   /**
    * VR
    */
   void SetVRHMDInfo(gfx::VRHMDInfo* aHMD) { mHMDInfo = aHMD; }
   gfx::VRHMDInfo* GetVRHMDInfo() { return mHMDInfo; }
 
 protected:
   friend class ReadbackProcessor;
@@ -2018,16 +2036,17 @@ protected:
   // Whether the compositor should scale to mPresShellResolution.
   bool mScaleToResolution;
   bool mUseIntermediateSurface;
   bool mSupportsComponentAlphaChildren;
   bool mMayHaveReadbackChild;
   // This is updated by ComputeDifferences. This will be true if we need to invalidate
   // the intermediate surface.
   bool mChildrenChanged;
+  bool mForceDispatchToContentRegion;
   nsRefPtr<gfx::VRHMDInfo> mHMDInfo;
 };
 
 /**
  * A Layer which just renders a solid color in its visible region. It actually
  * can fill any area that contains the visible region, so if you need to
  * restrict the area filled, set a clip region on this layer.
  */
--- a/gfx/layers/TiledLayerBuffer.h
+++ b/gfx/layers/TiledLayerBuffer.h
@@ -7,16 +7,17 @@
 
 // Debug defines
 //#define GFX_TILEDLAYER_DEBUG_OVERLAY
 //#define GFX_TILEDLAYER_PREF_WARNINGS
 
 #include <stdint.h>                     // for uint16_t, uint32_t
 #include <sys/types.h>                  // for int32_t
 #include "gfxPlatform.h"                // for GetTileWidth/GetTileHeight
+#include "mozilla/gfx/Logging.h"        // for gfxCriticalError
 #include "nsDebug.h"                    // for NS_ASSERTION
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsRect.h"                     // for nsIntRect
 #include "nsRegion.h"                   // for nsIntRegion
 #include "nsTArray.h"                   // for nsTArray
 
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
 #include <ui/Fence.h>
@@ -335,31 +336,33 @@ TiledLayerBuffer<Derived, Tile>::Dump(st
       }
       y += h;
     }
     x += w;
   }
 }
 
 template<typename Derived, typename Tile> void
-TiledLayerBuffer<Derived, Tile>::Update(const nsIntRegion& aNewValidRegion,
+TiledLayerBuffer<Derived, Tile>::Update(const nsIntRegion& newValidRegion,
                                         const nsIntRegion& aPaintRegion)
 {
   gfx::IntSize scaledTileSize = GetScaledTileSize();
 
   nsTArray<Tile>  newRetainedTiles;
   nsTArray<Tile>& oldRetainedTiles = mRetainedTiles;
   const nsIntRect oldBound = mValidRegion.GetBounds();
-  const nsIntRect newBound = aNewValidRegion.GetBounds();
+  const nsIntRect newBound = newValidRegion.GetBounds();
   const nsIntPoint oldBufferOrigin(RoundDownToTileEdge(oldBound.x, scaledTileSize.width),
                                    RoundDownToTileEdge(oldBound.y, scaledTileSize.height));
   const nsIntPoint newBufferOrigin(RoundDownToTileEdge(newBound.x, scaledTileSize.width),
                                    RoundDownToTileEdge(newBound.y, scaledTileSize.height));
+
+  // This is the reason we break the style guide with newValidRegion instead
+  // of aNewValidRegion - so that the names match better and code easier to read
   const nsIntRegion& oldValidRegion = mValidRegion;
-  const nsIntRegion& newValidRegion = aNewValidRegion;
   const int oldRetainedHeight = mRetainedHeight;
 
   // Pass 1: Recycle valid content from the old buffer
   // Recycle tiles from the old buffer that contain valid regions.
   // Insert placeholders tiles if we have no valid area for that tile
   // which we will allocate in pass 2.
   // TODO: Add a tile pool to reduce new allocation
   int tileX = 0;
@@ -441,22 +444,33 @@ TiledLayerBuffer<Derived, Tile>::Update(
     if (oldTileCount >= tilesMissing) {
       oldRetainedTiles[i] = AsDerived().GetPlaceholderTile();
       AsDerived().ReleaseTile(oldTile);
     } else {
       oldTileCount ++;
     }
   }
 
-  MOZ_ASSERT(aNewValidRegion.Contains(aPaintRegion), "Painting a region outside the visible region");
+  if (!newValidRegion.Contains(aPaintRegion)) {
+    gfxCriticalError() << "Painting outside visible:"
+		       << " paint " << aPaintRegion.ToString().get()
+                       << " old valid " << oldValidRegion.ToString().get()
+                       << " new valid " << newValidRegion.ToString().get();
+  }
 #ifdef DEBUG
   nsIntRegion oldAndPainted(oldValidRegion);
   oldAndPainted.Or(oldAndPainted, aPaintRegion);
+  if (!oldAndPainted.Contains(newValidRegion)) {
+    gfxCriticalError() << "Not fully painted:"
+		       << " paint " << aPaintRegion.ToString().get()
+                       << " old valid " << oldValidRegion.ToString().get()
+                       << " old painted " << oldAndPainted.ToString().get()
+                       << " new valid " << newValidRegion.ToString().get();
+  }
 #endif
-  MOZ_ASSERT(oldAndPainted.Contains(newValidRegion), "newValidRegion has not been fully painted");
 
   nsIntRegion regionToPaint(aPaintRegion);
 
   // Pass 2: Validate
   // We know at this point that any tile in the new buffer that had valid content
   // from the previous buffer is placed correctly in the new buffer.
   // We know that any tile in the old buffer that isn't a place holder is
   // of no use and can be recycled.
@@ -548,16 +562,16 @@ TiledLayerBuffer<Derived, Tile>::Update(
   AsDerived().PostValidate(aPaintRegion);
   for (unsigned int i = 0; i < mRetainedTiles.Length(); ++i) {
     AsDerived().UnlockTile(mRetainedTiles[i]);
   }
 
   // At this point, oldTileCount should be zero
   MOZ_ASSERT(oldTileCount == 0, "Failed to release old tiles");
 
-  mValidRegion = aNewValidRegion;
+  mValidRegion = newValidRegion;
   mPaintedRegion.Or(mPaintedRegion, aPaintRegion);
 }
 
 } // layers
 } // mozilla
 
 #endif // GFX_TILEDLAYERBUFFER_H
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -315,16 +315,30 @@ APZCTreeManager::RecycleOrCreateNode(Tre
       node->RecycleWith(aApzc);
       return node.forget();
     }
   }
   nsRefPtr<HitTestingTreeNode> node = new HitTestingTreeNode(aApzc, false);
   return node.forget();
 }
 
+static bool
+ShouldForceDispatchToContent(HitTestingTreeNode* aParent,
+                             const LayerMetricsWrapper& aLayer)
+{
+  // Make it so that if the flag is set on the layer tree, it automatically
+  // propagates to all the nodes in the corresponding subtree rooted at that
+  // layer in the hit-test tree. This saves having to walk up the tree every
+  // we want to see if a hit-test node is affected by this flag.
+  if (aParent && aParent->GetForceDispatchToContent()) {
+    return true;
+  }
+  return aLayer.GetForceDispatchToContentRegion();
+}
+
 HitTestingTreeNode*
 APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
                                      const FrameMetrics& aMetrics,
                                      uint64_t aLayersId,
                                      const gfx::Matrix4x4& aAncestorTransform,
                                      HitTestingTreeNode* aParent,
                                      HitTestingTreeNode* aNextSibling,
                                      TreeBuildingState& aState)
@@ -339,17 +353,18 @@ APZCTreeManager::PrepareNodeForLayer(con
     needsApzc = false;
   }
 
   nsRefPtr<HitTestingTreeNode> node = nullptr;
   if (!needsApzc) {
     node = RecycleOrCreateNode(aState, nullptr);
     AttachNodeToTree(node, aParent, aNextSibling);
     node->SetHitTestData(GetEventRegions(aLayer), aLayer.GetTransform(),
-        aLayer.GetClipRect() ? Some(nsIntRegion(*aLayer.GetClipRect())) : Nothing());
+        aLayer.GetClipRect() ? Some(nsIntRegion(*aLayer.GetClipRect())) : Nothing(),
+        ShouldForceDispatchToContent(aParent, aLayer));
     return node;
   }
 
   AsyncPanZoomController* apzc = nullptr;
   // If we get here, aLayer is a scrollable layer and somebody
   // has registered a GeckoContentController for it, so we need to ensure
   // it has an APZC instance to manage its scrolling.
 
@@ -435,17 +450,18 @@ APZCTreeManager::PrepareNodeForLayer(con
         aState.mIsFirstPaint && (aLayersId == aState.mOriginatingLayersId));
 
     // Since this is the first time we are encountering an APZC with this guid,
     // the node holding it must be the primary holder. It may be newly-created
     // or not, depending on whether it went through the newApzc branch above.
     MOZ_ASSERT(node->IsPrimaryHolder() && node->GetApzc() && node->GetApzc()->Matches(guid));
 
     nsIntRegion clipRegion = ComputeClipRegion(state->mController, aLayer);
-    node->SetHitTestData(GetEventRegions(aLayer), aLayer.GetTransform(), Some(clipRegion));
+    node->SetHitTestData(GetEventRegions(aLayer), aLayer.GetTransform(), Some(clipRegion),
+        ShouldForceDispatchToContent(aParent, aLayer));
     apzc->SetAncestorTransform(aAncestorTransform);
 
     PrintAPZCInfo(aLayer, apzc);
 
     // Bind the APZC instance into the tree of APZCs
     AttachNodeToTree(node, aParent, aNextSibling);
 
     // For testing, log the parent scroll id of every APZC that has a
@@ -489,17 +505,18 @@ APZCTreeManager::PrepareNodeForLayer(con
 
     // Even though different layers associated with a given APZC may be at
     // different levels in the layer tree (e.g. one being an uncle of another),
     // we require from Layout that the CSS transforms up to their common
     // ancestor be the same.
     MOZ_ASSERT(aAncestorTransform == apzc->GetAncestorTransform());
 
     nsIntRegion clipRegion = ComputeClipRegion(state->mController, aLayer);
-    node->SetHitTestData(GetEventRegions(aLayer), aLayer.GetTransform(), Some(clipRegion));
+    node->SetHitTestData(GetEventRegions(aLayer), aLayer.GetTransform(), Some(clipRegion),
+        ShouldForceDispatchToContent(aParent, aLayer));
   }
 
   return node;
 }
 
 HitTestingTreeNode*
 APZCTreeManager::UpdateHitTestingTree(TreeBuildingState& aState,
                                       const LayerMetricsWrapper& aLayer,
--- a/gfx/layers/apz/src/HitTestingTreeNode.cpp
+++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp
@@ -16,16 +16,17 @@
 
 namespace mozilla {
 namespace layers {
 
 HitTestingTreeNode::HitTestingTreeNode(AsyncPanZoomController* aApzc,
                                        bool aIsPrimaryHolder)
   : mApzc(aApzc)
   , mIsPrimaryApzcHolder(aIsPrimaryHolder)
+  , mForceDispatchToContent(false)
 {
   if (mIsPrimaryApzcHolder) {
     MOZ_ASSERT(mApzc);
   }
 }
 
 void
 HitTestingTreeNode::RecycleWith(AsyncPanZoomController* aApzc)
@@ -150,21 +151,23 @@ bool
 HitTestingTreeNode::IsPrimaryHolder() const
 {
   return mIsPrimaryApzcHolder;
 }
 
 void
 HitTestingTreeNode::SetHitTestData(const EventRegions& aRegions,
                                    const gfx::Matrix4x4& aTransform,
-                                   const Maybe<nsIntRegion>& aClipRegion)
+                                   const Maybe<nsIntRegion>& aClipRegion,
+                                   bool aForceDispatchToContent)
 {
   mEventRegions = aRegions;
   mTransform = aTransform;
   mClipRegion = aClipRegion;
+  mForceDispatchToContent = aForceDispatchToContent;
 }
 
 bool
 HitTestingTreeNode::IsOutsideClip(const ParentLayerPoint& aPoint) const
 {
   // test against clip rect in ParentLayer coordinate space
   return (mClipRegion.isSome() && !mClipRegion->Contains(aPoint.x, aPoint.y));
 }
@@ -205,30 +208,39 @@ HitTestingTreeNode::HitTest(const Parent
     return HitTestResult::HitNothing;
   }
   LayerIntPoint point = RoundedToInt(pointInLayerPixels.ref());
 
   // test against event regions in Layer coordinate space
   if (!mEventRegions.mHitRegion.Contains(point.x, point.y)) {
     return HitTestResult::HitNothing;
   }
-  if (mEventRegions.mDispatchToContentHitRegion.Contains(point.x, point.y)) {
+  if (mForceDispatchToContent ||
+      mEventRegions.mDispatchToContentHitRegion.Contains(point.x, point.y))
+  {
     return HitTestResult::HitDispatchToContentRegion;
   }
   return HitTestResult::HitLayer;
 }
 
+bool
+HitTestingTreeNode::GetForceDispatchToContent() const
+{
+  return mForceDispatchToContent;
+}
+
 void
 HitTestingTreeNode::Dump(const char* aPrefix) const
 {
   if (mPrevSibling) {
     mPrevSibling->Dump(aPrefix);
   }
-  printf_stderr("%sHitTestingTreeNode (%p) APZC (%p) g=(%s) r=(%s) t=(%s) c=(%s)\n",
+  printf_stderr("%sHitTestingTreeNode (%p) APZC (%p) g=(%s) %sr=(%s) t=(%s) c=(%s)\n",
     aPrefix, this, mApzc.get(), mApzc ? Stringify(mApzc->GetGuid()).c_str() : "",
+    mForceDispatchToContent ? "fdtc " : "",
     Stringify(mEventRegions).c_str(), Stringify(mTransform).c_str(),
     mClipRegion ? Stringify(mClipRegion.ref()).c_str() : "none");
   if (mLastChild) {
     mLastChild->Dump(nsPrintfCString("%s  ", aPrefix).get());
   }
 }
 
 void
--- a/gfx/layers/apz/src/HitTestingTreeNode.h
+++ b/gfx/layers/apz/src/HitTestingTreeNode.h
@@ -76,24 +76,27 @@ public:
   AsyncPanZoomController* GetApzc() const;
   AsyncPanZoomController* GetNearestContainingApzc() const;
   bool IsPrimaryHolder() const;
 
   /* Hit test related methods */
 
   void SetHitTestData(const EventRegions& aRegions,
                       const gfx::Matrix4x4& aTransform,
-                      const Maybe<nsIntRegion>& aClipRegion);
+                      const Maybe<nsIntRegion>& aClipRegion,
+                      bool aForceDispatchToContent);
   bool IsOutsideClip(const ParentLayerPoint& aPoint) const;
   /* Convert aPoint into the LayerPixel space for the layer corresponding to
    * this node. */
   Maybe<LayerPoint> Untransform(const ParentLayerPoint& aPoint) const;
   /* Assuming aPoint is inside the clip region for this node, check which of the
    * event region spaces it falls inside. */
   HitTestResult HitTest(const ParentLayerPoint& aPoint) const;
+  /* Returns the mForceDispatchToContent flag. */
+  bool GetForceDispatchToContent() const;
 
   /* Debug helpers */
   void Dump(const char* aPrefix = "") const;
 
 private:
   void SetApzcParent(AsyncPanZoomController* aApzc);
 
   nsRefPtr<HitTestingTreeNode> mLastChild;
@@ -117,14 +120,19 @@ private:
   gfx::Matrix4x4 mTransform;
 
   /* This is clip rect for L that we wish to use for hit-testing purposes. Note
    * that this may not be exactly the same as the clip rect on layer L because
    * of the touch-sensitive region provided by the GeckoContentController, or
    * because we may use the composition bounds of the layer if the clip is not
    * present. This value is in L's ParentLayerPixels. */
   Maybe<nsIntRegion> mClipRegion;
+
+  /* If this flag is set, then events to this node and the entire subtree under
+   * should always be treated as dispatch-to-content.
+   */
+  bool mForceDispatchToContent;
 };
 
 }
 }
 
 #endif // mozilla_layers_HitTestingTreeNode_h
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -381,16 +381,17 @@ LayerTransactionParent::RecvUpdate(Infal
           return false;
         }
         const ContainerLayerAttributes& attrs =
           specific.get_ContainerLayerAttributes();
         containerLayer->SetPreScale(attrs.preXScale(), attrs.preYScale());
         containerLayer->SetInheritedScale(attrs.inheritedXScale(), attrs.inheritedYScale());
         containerLayer->SetScaleToResolution(attrs.scaleToResolution(),
                                              attrs.presShellResolution());
+        containerLayer->SetForceDispatchToContentRegion(attrs.forceDispatchToContentRegion());
 
         if (attrs.hmdInfo()) {
           if (!IsSameProcess()) {
             NS_WARNING("VR layers currently not supported with cross-process compositing");
             return false;
           }
           containerLayer->SetVRHMDInfo(reinterpret_cast<mozilla::gfx::VRHMDInfo*>(attrs.hmdInfo()));
         }
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -229,16 +229,17 @@ struct PaintedLayerAttributes {
 };
 struct ContainerLayerAttributes {
   float preXScale;
   float preYScale;
   float inheritedXScale;
   float inheritedYScale;
   float presShellResolution;
   bool scaleToResolution;
+  bool forceDispatchToContentRegion;
   // This is a bare pointer; LayerTransactionParent::RecvUpdate prevents this
   // from being used when !IsSameProcess(), but we should make this truly
   // cross process at some point by passing the HMDConfig
   uint64_t hmdInfo;
 };
 struct ColorLayerAttributes     { LayerColor color; nsIntRect bounds; };
 struct CanvasLayerAttributes    { GraphicsFilterType filter; nsIntRect bounds; };
 struct RefLayerAttributes       { int64_t id; };
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -683,18 +683,19 @@ public:
             }
             uint32_t timestamp = strtoul(beginning, nullptr, 10);
             beginning = end + 1;
             if (!(end = strchr(beginning, ';'))) {
                 break;
             }
             uint32_t filesize = strtoul(beginning, nullptr, 10);
 
-            FNCMapEntry* mapEntry = static_cast<FNCMapEntry*>
-                (PL_DHashTableAdd(&mMap, filename.get(), fallible));
+            FNCMapEntry* mapEntry =
+                static_cast<FNCMapEntry*>
+                (PL_DHashTableAdd(&mMap, filename.get()));
             if (mapEntry) {
                 mapEntry->mFilename.Assign(filename);
                 mapEntry->mTimestamp = timestamp;
                 mapEntry->mFilesize = filesize;
                 mapEntry->mFaces.Assign(faceList);
                 // entries from the startupcache are marked "non-existing"
                 // until we have confirmed that the file still exists
                 mapEntry->mFileExists = false;
@@ -731,18 +732,19 @@ public:
 
     virtual void
     CacheFileInfo(const nsCString& aFileName, const nsCString& aFaceList,
                   uint32_t aTimestamp, uint32_t aFilesize)
     {
         if (!mMap.IsInitialized()) {
             return;
         }
-        FNCMapEntry* entry = static_cast<FNCMapEntry*>
-            (PL_DHashTableAdd(&mMap, aFileName.get(), fallible));
+        FNCMapEntry* entry =
+            static_cast<FNCMapEntry*>
+            (PL_DHashTableAdd(&mMap, aFileName.get()));
         if (entry) {
             entry->mFilename.Assign(aFileName);
             entry->mTimestamp = aTimestamp;
             entry->mFilesize = aFilesize;
             entry->mFaces.Assign(aFaceList);
             entry->mFileExists = true;
         }
         mWriteNeeded = true;
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -1179,16 +1179,17 @@ RasterImage::OnImageDataComplete(nsIRequ
 
 void
 RasterImage::NotifyForDecodeOnDrawOnly()
 {
   if (!NS_IsMainThread()) {
     nsCOMPtr<nsIRunnable> runnable =
       NS_NewRunnableMethod(this, &RasterImage::NotifyForDecodeOnDrawOnly);
     NS_DispatchToMainThread(runnable);
+    return;
   }
 
   NotifyProgress(FLAG_DECODE_STARTED | FLAG_ONLOAD_BLOCKED);
 }
 
 nsresult
 RasterImage::OnImageDataAvailable(nsIRequest*,
                                   nsISupports*,
--- a/image/src/SurfaceCache.cpp
+++ b/image/src/SurfaceCache.cpp
@@ -6,17 +6,17 @@
 /**
  * SurfaceCache is a service for caching temporary surfaces in imagelib.
  */
 
 #include "SurfaceCache.h"
 
 #include <algorithm>
 #include "mozilla/Assertions.h"
-#include "mozilla/Attributes.h"  // for MOZ_THIS_IN_INITIALIZER_LIST
+#include "mozilla/Attributes.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Move.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/StaticPtr.h"
 #include "nsIMemoryReporter.h"
 #include "gfx2DGlue.h"
--- a/js/ipc/JavaScriptParent.cpp
+++ b/js/ipc/JavaScriptParent.cpp
@@ -4,18 +4,18 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "JavaScriptParent.h"
 #include "mozilla/dom/ContentParent.h"
 #include "nsJSUtils.h"
 #include "jsfriendapi.h"
-#include "jsproxy.h"
 #include "jswrapper.h"
+#include "js/Proxy.h"
 #include "HeapAPI.h"
 #include "xpcprivate.h"
 #include "mozilla/Casting.h"
 
 using namespace js;
 using namespace JS;
 using namespace mozilla;
 using namespace mozilla::jsipc;
--- a/js/ipc/WrapperOwner.h
+++ b/js/ipc/WrapperOwner.h
@@ -7,33 +7,33 @@
 
 #ifndef mozilla_jsipc_WrapperOwner_h__
 #define mozilla_jsipc_WrapperOwner_h__
 
 #include "JavaScriptShared.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "js/Class.h"
-#include "jsproxy.h"
+#include "js/Proxy.h"
 
 namespace mozilla {
 namespace jsipc {
 
 class WrapperOwner : public virtual JavaScriptShared
 {
   public:
     typedef mozilla::ipc::IProtocolManager<
                        mozilla::ipc::IProtocol>::ActorDestroyReason
            ActorDestroyReason;
 
     explicit WrapperOwner(JSRuntime *rt);
     bool init();
 
     // Standard internal methods.
-    // (The traps should be in the same order like js/src/jsproxy.h)
+    // (The traps should be in the same order like js/Proxy.h)
     bool getOwnPropertyDescriptor(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
                                   JS::MutableHandle<JSPropertyDescriptor> desc);
     bool defineProperty(JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
                         JS::MutableHandle<JSPropertyDescriptor> desc);
     bool ownPropertyKeys(JSContext *cx, JS::HandleObject proxy, JS::AutoIdVector &props);
     bool delete_(JSContext *cx, JS::HandleObject proxy, JS::HandleId id, bool *bp);
     bool preventExtensions(JSContext *cx, JS::HandleObject proxy, bool *succeeded);
     bool isExtensible(JSContext *cx, JS::HandleObject proxy, bool *extensible);
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -54,16 +54,17 @@ namespace JS {
     D(DEBUG_GC)                                 \
     D(COMPARTMENT_REVIVED)                      \
     D(RESET)                                    \
     D(OUT_OF_NURSERY)                           \
     D(EVICT_NURSERY)                            \
     D(FULL_STORE_BUFFER)                        \
     D(SHARED_MEMORY_LIMIT)                      \
     D(PERIODIC_FULL_GC)                         \
+    D(INCREMENTAL_TOO_SLOW)                     \
                                                 \
     /* These are reserved for future use. */    \
     D(RESERVED0)                                \
     D(RESERVED1)                                \
     D(RESERVED2)                                \
     D(RESERVED3)                                \
     D(RESERVED4)                                \
     D(RESERVED5)                                \
@@ -73,17 +74,16 @@ namespace JS {
     D(RESERVED9)                                \
     D(RESERVED10)                               \
     D(RESERVED11)                               \
     D(RESERVED12)                               \
     D(RESERVED13)                               \
     D(RESERVED14)                               \
     D(RESERVED15)                               \
     D(RESERVED16)                               \
-    D(RESERVED17)                               \
                                                 \
     /* Reasons from Firefox */                  \
     D(DOM_WINDOW_UTILS)                         \
     D(COMPONENT_UTILS)                          \
     D(MEM_PRESSURE)                             \
     D(CC_WAITING)                               \
     D(CC_FORCED)                                \
     D(LOAD_END)                                 \
rename from js/src/jsproxy.h
rename to js/public/Proxy.h
--- a/js/src/jsproxy.h
+++ b/js/public/Proxy.h
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef jsproxy_h
-#define jsproxy_h
+#ifndef js_Proxy_h
+#define js_Proxy_h
 
 #include "mozilla/Maybe.h"
 
 #include "jsfriendapi.h"
 
 #include "js/CallNonGenericMethod.h"
 #include "js/Class.h"
 
@@ -674,9 +674,9 @@ inline void assertEnteredPolicy(JSContex
 {}
 #endif
 
 } /* namespace js */
 
 extern JS_FRIEND_API(JSObject *)
 js_InitProxyClass(JSContext *cx, JS::HandleObject obj);
 
-#endif /* jsproxy_h */
+#endif /* js_Proxy_h */
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -175,356 +175,50 @@ static inline char* js_strdup(const char
  *   operations on the FreeOp provided to the finalizer:
  *
  *     FreeOp::{free_,delete_}
  *
  *   The advantage of these operations is that the memory is batched and freed
  *   on another thread.
  */
 
-#define JS_NEW_BODY(allocator, t, parms)                                       \
-    void *memory = allocator(sizeof(t));                                       \
-    return memory ? new(memory) t parms : nullptr;
-#define JS_MAKE_BODY(newname, T, parms)                                        \
-    T *ptr = newname<T> parms;                                                 \
-    return mozilla::UniquePtr<T, JS::DeletePolicy<T>>(ptr);
-
 /*
- * Given a class which should provide 'new' methods, add
- * JS_DECLARE_NEW_METHODS (see JSContext for a usage example). This
- * adds news with up to 12 parameters. Add more versions of new below if
- * you need more than 12 parameters.
+ * Given a class which should provide a 'new' method, add
+ * JS_DECLARE_NEW_METHODS (see js::MallocProvider for an example).
  *
  * Note: Do not add a ; at the end of a use of JS_DECLARE_NEW_METHODS,
  * or the build will break.
  */
 #define JS_DECLARE_NEW_METHODS(NEWNAME, ALLOCATOR, QUALIFIERS)\
-    template <class T>\
-    QUALIFIERS T *NEWNAME() MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T, ())\
-    }\
-\
-    template <class T, class P1>\
-    QUALIFIERS T *NEWNAME(P1 &&p1) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1)))\
-    }\
-\
-    template <class T, class P1, class P2>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7),\
-                     mozilla::Forward<P8>(p8)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7),\
-                     mozilla::Forward<P8>(p8),\
-                     mozilla::Forward<P9>(p9)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7),\
-                     mozilla::Forward<P8>(p8),\
-                     mozilla::Forward<P9>(p9),\
-                     mozilla::Forward<P10>(p10)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10, class P11>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10, P11 &&p11) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7),\
-                     mozilla::Forward<P8>(p8),\
-                     mozilla::Forward<P9>(p9),\
-                     mozilla::Forward<P10>(p10),\
-                     mozilla::Forward<P11>(p11)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10, class P11, class P12>\
-    QUALIFIERS T *NEWNAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10, P11 &&p11, P12 &&p12) MOZ_HEAP_ALLOCATOR {\
-        JS_NEW_BODY(ALLOCATOR, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7),\
-                     mozilla::Forward<P8>(p8),\
-                     mozilla::Forward<P9>(p9),\
-                     mozilla::Forward<P10>(p10),\
-                     mozilla::Forward<P11>(p11),\
-                     mozilla::Forward<P12>(p12)))\
-    }\
+    template <class T, typename... Args> \
+    QUALIFIERS T * \
+    NEWNAME(Args&&... args) MOZ_HEAP_ALLOCATOR { \
+        void *memory = ALLOCATOR(sizeof(T)); \
+        return memory \
+               ? new(memory) T(mozilla::Forward<Args>(args)...) \
+               : nullptr; \
+    }
 
 /*
  * Given a class which should provide 'make' methods, add
- * JS_DECLARE_MAKE_METHODS (see JSContext for a usage example).  This method
- * is functionally the same as JS_DECLARE_NEW_METHODS: it just declares methods
- * that return mozilla::UniquePtr instances that will singly-manage ownership
- * of the created object.  This adds makes with up to 12 parameters.  Add more
- * versions below if you need more than 12 parameters.
+ * JS_DECLARE_MAKE_METHODS (see js::MallocProvider for an example).  This
+ * method is functionally the same as JS_DECLARE_NEW_METHODS: it just declares
+ * methods that return mozilla::UniquePtr instances that will singly-manage
+ * ownership of the created object.
  *
  * Note: Do not add a ; at the end of a use of JS_DECLARE_MAKE_METHODS,
  * or the build will break.
  */
 #define JS_DECLARE_MAKE_METHODS(MAKENAME, NEWNAME, QUALIFIERS)\
-    template <class T> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME() MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T, ())\
-    }\
-\
-    template <class T, class P1> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1)))\
-    }\
-\
-    template <class T, class P1, class P2> \
-    QUALIFIERS \
-mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1, P2 &&p2) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7),\
-                     mozilla::Forward<P8>(p8)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7),\
-                     mozilla::Forward<P8>(p8),\
-                     mozilla::Forward<P9>(p9)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7),\
-                     mozilla::Forward<P8>(p8),\
-                     mozilla::Forward<P9>(p9),\
-                     mozilla::Forward<P10>(p10)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10, class P11> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10, P11 &&p11) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7),\
-                     mozilla::Forward<P8>(p8),\
-                     mozilla::Forward<P9>(p9),\
-                     mozilla::Forward<P10>(p10),\
-                     mozilla::Forward<P11>(p11)))\
-    }\
-\
-    template <class T, class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8, class P9, class P10, class P11, class P12> \
-    QUALIFIERS \
-    mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
-    MAKENAME(P1 &&p1, P2 &&p2, P3 &&p3, P4 &&p4, P5 &&p5, P6 &&p6, P7 &&p7, P8 &&p8, P9 &&p9, P10 &&p10, P11 &&p11, P12 &&p12) MOZ_HEAP_ALLOCATOR {\
-        JS_MAKE_BODY(NEWNAME, T,\
-                    (mozilla::Forward<P1>(p1),\
-                     mozilla::Forward<P2>(p2),\
-                     mozilla::Forward<P3>(p3),\
-                     mozilla::Forward<P4>(p4),\
-                     mozilla::Forward<P5>(p5),\
-                     mozilla::Forward<P6>(p6),\
-                     mozilla::Forward<P7>(p7),\
-                     mozilla::Forward<P8>(p8),\
-                     mozilla::Forward<P9>(p9),\
-                     mozilla::Forward<P10>(p10),\
-                     mozilla::Forward<P11>(p11),\
-                     mozilla::Forward<P12>(p12)))\
-    }\
+    template <class T, typename... Args> \
+    QUALIFIERS mozilla::UniquePtr<T, JS::DeletePolicy<T>> \
+    MAKENAME(Args&&... args) MOZ_HEAP_ALLOCATOR { \
+        T *ptr = NEWNAME<T>(mozilla::Forward<Args>(args)...); \
+        return mozilla::UniquePtr<T, JS::DeletePolicy<T>>(ptr); \
+    }
 
 JS_DECLARE_NEW_METHODS(js_new, js_malloc, static MOZ_ALWAYS_INLINE)
 
 template <class T>
 static MOZ_ALWAYS_INLINE void
 js_delete(T *p)
 {
     if (p) {
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -178,16 +178,124 @@ CaseExpr(ParseNode *pn)
 
 static inline ParseNode *
 CaseBody(ParseNode *pn)
 {
     MOZ_ASSERT(pn->isKind(PNK_CASE) || pn->isKind(PNK_DEFAULT));
     return BinaryRight(pn);
 }
 
+static inline ParseNode *
+BinaryOpLeft(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isBinaryOperation());
+    MOZ_ASSERT(pn->isArity(PN_LIST));
+    MOZ_ASSERT(pn->pn_count == 2);
+    return ListHead(pn);
+}
+
+static inline ParseNode *
+BinaryOpRight(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isBinaryOperation());
+    MOZ_ASSERT(pn->isArity(PN_LIST));
+    MOZ_ASSERT(pn->pn_count == 2);
+    return NextNode(ListHead(pn));
+}
+
+static inline ParseNode *
+BitwiseLeft(ParseNode *pn)
+{
+    return BinaryOpLeft(pn);
+}
+
+static inline ParseNode *
+BitwiseRight(ParseNode *pn)
+{
+    return BinaryOpRight(pn);
+}
+
+static inline ParseNode *
+MultiplyLeft(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isKind(PNK_STAR));
+    return BinaryOpLeft(pn);
+}
+
+static inline ParseNode *
+MultiplyRight(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isKind(PNK_STAR));
+    return BinaryOpRight(pn);
+}
+
+static inline ParseNode *
+AddSubLeft(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isKind(PNK_ADD) || pn->isKind(PNK_SUB));
+    return BinaryOpLeft(pn);
+}
+
+static inline ParseNode *
+AddSubRight(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isKind(PNK_ADD) || pn->isKind(PNK_SUB));
+    return BinaryOpRight(pn);
+}
+
+static inline ParseNode *
+DivOrModLeft(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isKind(PNK_DIV) || pn->isKind(PNK_MOD));
+    return BinaryOpLeft(pn);
+}
+
+static inline ParseNode *
+DivOrModRight(ParseNode *pn)
+{
+    MOZ_ASSERT(pn->isKind(PNK_DIV) || pn->isKind(PNK_MOD));
+    return BinaryOpRight(pn);
+}
+
+static inline ParseNode *
+ComparisonLeft(ParseNode *pn)
+{
+    return BinaryOpLeft(pn);
+}
+
+static inline ParseNode *
+ComparisonRight(ParseNode *pn)
+{
+    return BinaryOpRight(pn);
+}
+
+static inline ParseNode *
+AndOrLeft(ParseNode *pn)
+{
+    return BinaryOpLeft(pn);
+}
+
+static inline ParseNode *
+AndOrRight(ParseNode *pn)
+{
+    return BinaryOpRight(pn);
+}
+
+static inline ParseNode *
+RelationalLeft(ParseNode *pn)
+{
+    return BinaryOpLeft(pn);
+}
+
+static inline ParseNode *
+RelationalRight(ParseNode *pn)
+{
+    return BinaryOpRight(pn);
+}
+
 static inline bool
 IsExpressionStatement(ParseNode *pn)
 {
     return pn->isKind(PNK_SEMI);
 }
 
 static inline ParseNode *
 ExpressionStatementExpr(ParseNode *pn)
@@ -3707,23 +3815,23 @@ CheckGlobalVariableInitConstant(ModuleCo
 }
 
 static bool
 CheckTypeAnnotation(ModuleCompiler &m, ParseNode *coercionNode, AsmJSCoercion *coercion,
                     ParseNode **coercedExpr = nullptr)
 {
     switch (coercionNode->getKind()) {
       case PNK_BITOR: {
-        ParseNode *rhs = BinaryRight(coercionNode);
+        ParseNode *rhs = BitwiseRight(coercionNode);
         uint32_t i;
         if (!IsLiteralInt(m, rhs, &i) || i != 0)
             return m.fail(rhs, "must use |0 for argument/return coercion");
         *coercion = AsmJS_ToInt32;
         if (coercedExpr)
-            *coercedExpr = BinaryLeft(coercionNode);
+            *coercedExpr = BitwiseLeft(coercionNode);
         return true;
       }
       case PNK_POS: {
         *coercion = AsmJS_ToNumber;
         if (coercedExpr)
             *coercedExpr = UnaryKid(coercionNode);
         return true;
       }
@@ -4324,18 +4432,20 @@ IsLiteralOrConstInt(FunctionCompiler &f,
 
     return IsLiteralInt(lit, u32);
 }
 
 static bool
 FoldMaskedArrayIndex(FunctionCompiler &f, ParseNode **indexExpr, int32_t *mask,
                      NeedsBoundsCheck *needsBoundsCheck)
 {
-    ParseNode *indexNode = BinaryLeft(*indexExpr);
-    ParseNode *maskNode = BinaryRight(*indexExpr);
+    MOZ_ASSERT((*indexExpr)->isKind(PNK_BITAND));
+
+    ParseNode *indexNode = BitwiseLeft(*indexExpr);
+    ParseNode *maskNode = BitwiseRight(*indexExpr);
 
     uint32_t mask2;
     if (IsLiteralOrConstInt(f, maskNode, &mask2)) {
         // Flag the access to skip the bounds check if the mask ensures that an 'out of
         // bounds' access can not occur based on the current heap length constraint.
         if (mask2 == 0 ||
             CountLeadingZeroes32(f.m().minHeapLength() - 1) <= CountLeadingZeroes32(mask2)) {
             *needsBoundsCheck = NO_BOUNDS_CHECK;
@@ -4383,26 +4493,27 @@ CheckArrayAccess(FunctionCompiler &f, Pa
 
     // Mask off the low bits to account for the clearing effect of a right shift
     // followed by the left shift implicit in the array access. E.g., H32[i>>2]
     // loses the low two bits.
     int32_t mask = ~(TypedArrayElemSize(*viewType) - 1);
 
     MDefinition *pointerDef;
     if (indexExpr->isKind(PNK_RSH)) {
-        ParseNode *shiftNode = BinaryRight(indexExpr);
-        ParseNode *pointerNode = BinaryLeft(indexExpr);
+        ParseNode *shiftAmountNode = BitwiseRight(indexExpr);
 
         uint32_t shift;
-        if (!IsLiteralInt(f.m(), shiftNode, &shift))
-            return f.failf(shiftNode, "shift amount must be constant");
+        if (!IsLiteralInt(f.m(), shiftAmountNode, &shift))
+            return f.failf(shiftAmountNode, "shift amount must be constant");
 
         unsigned requiredShift = TypedArrayShift(*viewType);
         if (shift != requiredShift)
-            return f.failf(shiftNode, "shift amount must be %u", requiredShift);
+            return f.failf(shiftAmountNode, "shift amount must be %u", requiredShift);
+
+        ParseNode *pointerNode = BitwiseLeft(indexExpr);
 
         if (pointerNode->isKind(PNK_BITAND))
             FoldMaskedArrayIndex(f, &pointerNode, &mask, needsBoundsCheck);
 
         f.enterHeapExpression();
 
         Type pointerType;
         if (!CheckExpr(f, pointerNode, &pointerDef, &pointerType))
@@ -4591,16 +4702,17 @@ CheckAssignName(FunctionCompiler &f, Par
     *type = rhsType;
     return true;
 }
 
 static bool
 CheckAssign(FunctionCompiler &f, ParseNode *assign, MDefinition **def, Type *type)
 {
     MOZ_ASSERT(assign->isKind(PNK_ASSIGN));
+
     ParseNode *lhs = BinaryLeft(assign);
     ParseNode *rhs = BinaryRight(assign);
 
     if (lhs->getKind() == PNK_ELEM)
         return CheckStoreArray(f, lhs, rhs, def, type);
 
     if (lhs->getKind() == PNK_NAME)
         return CheckAssignName(f, lhs, rhs, def, type);
@@ -5089,18 +5201,18 @@ CheckFuncPtrCall(FunctionCompiler &f, Pa
     if (const ModuleCompiler::Global *existing = f.lookupGlobal(name)) {
         if (existing->which() != ModuleCompiler::Global::FuncPtrTable)
             return f.failName(tableNode, "'%s' is not the name of a function-pointer array", name);
     }
 
     if (!indexExpr->isKind(PNK_BITAND))
         return f.fail(indexExpr, "function-pointer table index expression needs & mask");
 
-    ParseNode *indexNode = BinaryLeft(indexExpr);
-    ParseNode *maskNode = BinaryRight(indexExpr);
+    ParseNode *indexNode = BitwiseLeft(indexExpr);
+    ParseNode *maskNode = BitwiseRight(indexExpr);
 
     uint32_t mask;
     if (!IsLiteralInt(f.m(), maskNode, &mask) || mask == UINT32_MAX || !IsPowerOfTwo(mask + 1))
         return f.fail(maskNode, "function-pointer table index mask value must be a power of two minus 1");
 
     MDefinition *indexDef;
     Type indexType;
     if (!CheckExpr(f, indexNode, &indexDef, &indexType))
@@ -5735,32 +5847,22 @@ static bool
 CheckSimdOperationCall(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Global *global,
                        MDefinition **def, Type *type)
 {
     MOZ_ASSERT(global->isSimdOperation());
 
     AsmJSSimdType opType = global->simdOperationType();
 
     switch (global->simdOperation()) {
-      case AsmJSSimdOperation_add:
-        return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Add, def, type);
-      case AsmJSSimdOperation_sub:
-        return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Sub, def, type);
-      case AsmJSSimdOperation_mul:
-        return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Mul, def, type);
-      case AsmJSSimdOperation_div:
-        return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Div, def, type);
-      case AsmJSSimdOperation_max:
-        return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Max, def, type);
-      case AsmJSSimdOperation_min:
-        return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Min, def, type);
-      case AsmJSSimdOperation_maxNum:
-        return CheckSimdBinary(f, call, opType, MSimdBinaryArith::MaxNum, def, type);
-      case AsmJSSimdOperation_minNum:
-        return CheckSimdBinary(f, call, opType, MSimdBinaryArith::MinNum, def, type);
+#define OP_CHECK_CASE_LIST_(OP)                                                         \
+      case AsmJSSimdOperation_##OP:                                                     \
+        return CheckSimdBinary(f, call, opType, MSimdBinaryArith::Op_##OP, def, type);
+      ARITH_COMMONX4_SIMD_OP(OP_CHECK_CASE_LIST_)
+      ARITH_FLOAT32X4_SIMD_OP(OP_CHECK_CASE_LIST_)
+#undef OP_CHECK_CASE_LIST_
 
       case AsmJSSimdOperation_lessThan:
         return CheckSimdBinary(f, call, opType, MSimdBinaryComp::lessThan, def, type);
       case AsmJSSimdOperation_lessThanOrEqual:
         return CheckSimdBinary(f, call, opType, MSimdBinaryComp::lessThanOrEqual, def, type);
       case AsmJSSimdOperation_equal:
         return CheckSimdBinary(f, call, opType, MSimdBinaryComp::equal, def, type);
       case AsmJSSimdOperation_notEqual:
@@ -6282,18 +6384,18 @@ IsValidIntMultiplyConstant(ModuleCompile
 
     MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad literal");
 }
 
 static bool
 CheckMultiply(FunctionCompiler &f, ParseNode *star, MDefinition **def, Type *type)
 {
     MOZ_ASSERT(star->isKind(PNK_STAR));
-    ParseNode *lhs = BinaryLeft(star);
-    ParseNode *rhs = BinaryRight(star);
+    ParseNode *lhs = MultiplyLeft(star);
+    ParseNode *rhs = MultiplyRight(star);
 
     MDefinition *lhsDef;
     Type lhsType;
     if (!CheckExpr(f, lhs, &lhsDef, &lhsType))
         return false;
 
     MDefinition *rhsDef;
     Type rhsType;
@@ -6325,18 +6427,18 @@ CheckMultiply(FunctionCompiler &f, Parse
 
 static bool
 CheckAddOrSub(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type,
               unsigned *numAddOrSubOut = nullptr)
 {
     JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
 
     MOZ_ASSERT(expr->isKind(PNK_ADD) || expr->isKind(PNK_SUB));
-    ParseNode *lhs = BinaryLeft(expr);
-    ParseNode *rhs = BinaryRight(expr);
+    ParseNode *lhs = AddSubLeft(expr);
+    ParseNode *rhs = AddSubRight(expr);
 
     MDefinition *lhsDef, *rhsDef;
     Type lhsType, rhsType;
     unsigned lhsNumAddOrSub, rhsNumAddOrSub;
 
     if (lhs->isKind(PNK_ADD) || lhs->isKind(PNK_SUB)) {
         if (!CheckAddOrSub(f, lhs, &lhsDef, &lhsType, &lhsNumAddOrSub))
             return false;
@@ -6387,18 +6489,19 @@ CheckAddOrSub(FunctionCompiler &f, Parse
         *numAddOrSubOut = numAddOrSub;
     return true;
 }
 
 static bool
 CheckDivOrMod(FunctionCompiler &f, ParseNode *expr, MDefinition **def, Type *type)
 {
     MOZ_ASSERT(expr->isKind(PNK_DIV) || expr->isKind(PNK_MOD));
-    ParseNode *lhs = BinaryLeft(expr);
-    ParseNode *rhs = BinaryRight(expr);
+
+    ParseNode *lhs = DivOrModLeft(expr);
+    ParseNode *rhs = DivOrModRight(expr);
 
     MDefinition *lhsDef, *rhsDef;
     Type lhsType, rhsType;
     if (!CheckExpr(f, lhs, &lhsDef, &lhsType))
         return false;
     if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
         return false;
 
@@ -6441,18 +6544,19 @@ CheckDivOrMod(FunctionCompiler &f, Parse
                    "%s and %s are given", lhsType.toChars(), rhsType.toChars());
 }
 
 static bool
 CheckComparison(FunctionCompiler &f, ParseNode *comp, MDefinition **def, Type *type)
 {
     MOZ_ASSERT(comp->isKind(PNK_LT) || comp->isKind(PNK_LE) || comp->isKind(PNK_GT) ||
                comp->isKind(PNK_GE) || comp->isKind(PNK_EQ) || comp->isKind(PNK_NE));
-    ParseNode *lhs = BinaryLeft(comp);
-    ParseNode *rhs = BinaryRight(comp);
+
+    ParseNode *lhs = ComparisonLeft(comp);
+    ParseNode *rhs = ComparisonRight(comp);
 
     MDefinition *lhsDef, *rhsDef;
     Type lhsType, rhsType;
     if (!CheckExpr(f, lhs, &lhsDef, &lhsType))
         return false;
     if (!CheckExpr(f, rhs, &rhsDef, &rhsType))
         return false;
 
@@ -6479,18 +6583,18 @@ CheckComparison(FunctionCompiler &f, Par
 
     return f.failf(comp, "arguments to a comparison must both be signed, unsigned, floats or doubles; "
                    "%s and %s are given", lhsType.toChars(), rhsType.toChars());
 }
 
 static bool
 CheckBitwise(FunctionCompiler &f, ParseNode *bitwise, MDefinition **def, Type *type)
 {
-    ParseNode *lhs = BinaryLeft(bitwise);
-    ParseNode *rhs = BinaryRight(bitwise);
+    ParseNode *lhs = BitwiseLeft(bitwise);
+    ParseNode *rhs = BitwiseRight(bitwise);
 
     int32_t identityElement;
     bool onlyOnRight;
     switch (bitwise->getKind()) {
       case PNK_BITOR:  identityElement = 0;  onlyOnRight = false; *type = Type::Signed;   break;
       case PNK_BITAND: identityElement = -1; onlyOnRight = false; *type = Type::Signed;   break;
       case PNK_BITXOR: identityElement = 0;  onlyOnRight = false; *type = Type::Signed;   break;
       case PNK_LSH:    identityElement = 0;  onlyOnRight = true;  *type = Type::Signed;   break;
@@ -7203,58 +7307,58 @@ CheckByteLengthCall(ModuleCompiler &m, P
 
     return true;
 }
 
 static bool
 CheckHeapLengthCondition(ModuleCompiler &m, ParseNode *cond, PropertyName *newBufferName,
                          uint32_t *mask, uint32_t *minLength, uint32_t *maxLength)
 {
-    if (!cond->isKind(PNK_OR) || !BinaryLeft(cond)->isKind(PNK_OR))
+    if (!cond->isKind(PNK_OR) || !AndOrLeft(cond)->isKind(PNK_OR))
         return m.fail(cond, "expecting byteLength & K || byteLength <= L || byteLength > M");
 
-    ParseNode *cond1 = BinaryLeft(BinaryLeft(cond));
-    ParseNode *cond2 = BinaryRight(BinaryLeft(cond));
-    ParseNode *cond3 = BinaryRight(cond);
+    ParseNode *cond1 = AndOrLeft(AndOrLeft(cond));
+    ParseNode *cond2 = AndOrRight(AndOrLeft(cond));
+    ParseNode *cond3 = AndOrRight(cond);
 
     if (!cond1->isKind(PNK_BITAND))
         return m.fail(cond1, "expecting byteLength & K");
 
-    if (!CheckByteLengthCall(m, BinaryLeft(cond1), newBufferName))
-        return false;
-
-    ParseNode *maskNode = BinaryRight(cond1);
+    if (!CheckByteLengthCall(m, BitwiseLeft(cond1), newBufferName))
+        return false;
+
+    ParseNode *maskNode = BitwiseRight(cond1);
     if (!IsLiteralInt(m, maskNode, mask))
         return m.fail(maskNode, "expecting integer literal mask");
     if ((*mask & 0xffffff) != 0xffffff)
         return m.fail(maskNode, "mask value must have the bits 0xffffff set");
 
     if (!cond2->isKind(PNK_LE))
         return m.fail(cond2, "expecting byteLength <= L");
 
-    if (!CheckByteLengthCall(m, BinaryLeft(cond2), newBufferName))
-        return false;
-
-    ParseNode *minLengthNode = BinaryRight(cond2);
+    if (!CheckByteLengthCall(m, RelationalLeft(cond2), newBufferName))
+        return false;
+
+    ParseNode *minLengthNode = RelationalRight(cond2);
     uint32_t minLengthExclusive;
     if (!IsLiteralInt(m, minLengthNode, &minLengthExclusive))
         return m.fail(minLengthNode, "expecting integer literal");
     if (minLengthExclusive < 0xffffff)
         return m.fail(minLengthNode, "literal must be >= 0xffffff");
 
     // Add one to convert from exclusive (the branch rejects if ==) to inclusive.
     *minLength = minLengthExclusive + 1;
 
     if (!cond3->isKind(PNK_GT))
         return m.fail(cond3, "expecting byteLength > M");
 
-    if (!CheckByteLengthCall(m, BinaryLeft(cond3), newBufferName))
-        return false;
-
-    ParseNode *maxLengthNode = BinaryRight(cond3);
+    if (!CheckByteLengthCall(m, RelationalLeft(cond3), newBufferName))
+        return false;
+
+    ParseNode *maxLengthNode = RelationalRight(cond3);
     if (!IsLiteralInt(m, maxLengthNode, maxLength))
         return m.fail(maxLengthNode, "expecting integer literal");
     if (*maxLength > 0x80000000)
         return m.fail(maxLengthNode, "literal must be <= 0x80000000");
 
     if (*maxLength < *minLength)
         return m.fail(maxLengthNode, "maximum length must be greater or equal to minimum length");
 
--- a/js/src/builtin/SIMD.h
+++ b/js/src/builtin/SIMD.h
@@ -185,41 +185,47 @@
   INT32X4_SHUFFLE_FUNCTION_LIST(V)
 
 #define FOREACH_INT32X4_SIMD_OP(_)   \
     _(fromFloat32x4)                 \
     _(fromFloat32x4Bits)             \
     _(shiftLeftByScalar)             \
     _(shiftRightArithmeticByScalar)  \
     _(shiftRightLogicalByScalar)
+#define ARITH_FLOAT32X4_SIMD_OP(_)   \
+    _(div)                           \
+    _(max)                           \
+    _(min)                           \
+    _(maxNum)                        \
+    _(minNum)
 #define FOREACH_FLOAT32X4_SIMD_OP(_) \
+    ARITH_FLOAT32X4_SIMD_OP(_)       \
     _(abs)                           \
     _(sqrt)                          \
     _(reciprocal)                    \
     _(reciprocalSqrt)                \
     _(fromInt32x4)                   \
-    _(fromInt32x4Bits)               \
-    _(div)                           \
-    _(max)                           \
-    _(min)                           \
-    _(maxNum)                        \
-    _(minNum)
-#define FOREACH_COMMONX4_SIMD_OP(_)  \
+    _(fromInt32x4Bits)
+#define ARITH_COMMONX4_SIMD_OP(_)    \
     _(add)                           \
     _(sub)                           \
-    _(mul)                           \
+    _(mul)
+#define BITWISE_COMMONX4_SIMD_OP(_)  \
+    _(and)                           \
+    _(or)                            \
+    _(xor)
+#define FOREACH_COMMONX4_SIMD_OP(_)  \
+    ARITH_COMMONX4_SIMD_OP(_)        \
+    BITWISE_COMMONX4_SIMD_OP(_)      \
     _(lessThan)                      \
     _(lessThanOrEqual)               \
     _(equal)                         \
     _(notEqual)                      \
     _(greaterThan)                   \
     _(greaterThanOrEqual)            \
-    _(and)                           \
-    _(or)                            \
-    _(xor)                           \
     _(bitselect)                     \
     _(select)                        \
     _(swizzle)                       \
     _(shuffle)                       \
     _(splat)                         \
     _(withX)                         \
     _(withY)                         \
     _(withZ)                         \
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2034,25 +2034,20 @@ CheckSideEffects(ExclusiveContext *cx, B
                 if (!CheckSideEffects(cx, bce, pn->pn_right, answer))
                     return false;
                 if (!*answer && (!pn->isOp(JSOP_NOP) || !pn2->isConst()))
                     *answer = true;
             }
             return true;
         }
 
-        if (pn->isOp(JSOP_OR) || pn->isOp(JSOP_AND) || pn->isOp(JSOP_STRICTEQ) ||
-            pn->isOp(JSOP_STRICTNE)) {
-            /*
-             * ||, &&, ===, and !== do not convert their operands via
-             * toString or valueOf method calls.
-             */
-            return CheckSideEffects(cx, bce, pn->pn_left, answer) &&
-                   CheckSideEffects(cx, bce, pn->pn_right, answer);
-        }
+        MOZ_ASSERT(!pn->isOp(JSOP_OR), "|| produces a list now");
+        MOZ_ASSERT(!pn->isOp(JSOP_AND), "&& produces a list now");
+        MOZ_ASSERT(!pn->isOp(JSOP_STRICTEQ), "=== produces a list now");
+        MOZ_ASSERT(!pn->isOp(JSOP_STRICTNE), "!== produces a list now");
 
         /*
          * We can't easily prove that neither operand ever denotes an
          * object with a toString or valueOf method.
          */
         *answer = true;
         return true;
 
@@ -4570,17 +4565,20 @@ EmitTry(ExclusiveContext *cx, BytecodeEm
     // Emit jump over catch and/or finally.
     ptrdiff_t catchJump = -1;
     if (EmitBackPatchOp(cx, bce, &catchJump) < 0)
         return false;
 
     ptrdiff_t tryEnd = bce->offset();
 
     // If this try has a catch block, emit it.
-    if (ParseNode *pn2 = pn->pn_kid2) {
+    ParseNode *catchList = pn->pn_kid2;
+    if (catchList) {
+        MOZ_ASSERT(catchList->isKind(PNK_CATCHLIST));
+
         // The emitted code for a catch block looks like:
         //
         // [pushblockscope]             only if any local aliased
         // exception
         // if there is a catchguard:
         //   dup
         // setlocal 0; pop              assign or possibly destructure exception
         // if there is a catchguard:
@@ -4596,17 +4594,17 @@ EmitTry(ExclusiveContext *cx, BytecodeEm
         // [popblockscope]              only if any local aliased
         // goto <end of catch blocks>   non-local; finally applies
         //
         // If there's no catch block without a catchguard, the last <next catch
         // block> points to rethrow code.  This code will [gosub] to the finally
         // code if appropriate, and is also used for the catch-all trynote for
         // capturing exceptions thrown from catch{} blocks.
         //
-        for (ParseNode *pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) {
+        for (ParseNode *pn3 = catchList->pn_head; pn3; pn3 = pn3->pn_next) {
             MOZ_ASSERT(bce->stackDepth == depth);
 
             // Emit the lexical scope and catch body.
             MOZ_ASSERT(pn3->isKind(PNK_LEXICALSCOPE));
             if (!EmitTree(cx, bce, pn3))
                 return false;
 
             // gosub <finally>, if required.
@@ -4671,17 +4669,17 @@ EmitTry(ExclusiveContext *cx, BytecodeEm
         return false;
 
     // Fix up the end-of-try/catch jumps to come here.
     if (!BackPatch(cx, bce, catchJump, bce->code().end(), JSOP_GOTO))
         return false;
 
     // Add the try note last, to let post-order give us the right ordering
     // (first to last for a given nesting level, inner to outer by level).
-    if (pn->pn_kid2 && !bce->tryNoteList.append(JSTRY_CATCH, depth, tryStart, tryEnd))
+    if (catchList && !bce->tryNoteList.append(JSTRY_CATCH, depth, tryStart, tryEnd))
         return false;
 
     // If we've got a finally, mark try+catch region with additional
     // trynote to catch exceptions (re)thrown from a catch block or
     // for the try{}finally{} case.
     if (pn->pn_kid3 && !bce->tryNoteList.append(JSTRY_FINALLY, depth, tryStart, finallyStart))
         return false;
 
@@ -6323,46 +6321,28 @@ EmitCallOrNew(ExclusiveContext *cx, Byte
             return false;
     }
     return true;
 }
 
 static bool
 EmitLogical(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
+    MOZ_ASSERT(pn->isArity(PN_LIST));
+
     /*
      * JSOP_OR converts the operand on the stack to boolean, leaves the original
      * value on the stack and jumps if true; otherwise it falls into the next
      * bytecode, which pops the left operand and then evaluates the right operand.
      * The jump goes around the right operand evaluation.
      *
      * JSOP_AND converts the operand on the stack to boolean and jumps if false;
      * otherwise it falls into the right operand's bytecode.
      */
 
-    if (pn->isArity(PN_BINARY)) {
-        if (!EmitTree(cx, bce, pn->pn_left))
-            return false;
-        ptrdiff_t top = EmitJump(cx, bce, JSOP_BACKPATCH, 0);
-        if (top < 0)
-            return false;
-        if (Emit1(cx, bce, JSOP_POP) < 0)
-            return false;
-        if (!EmitTree(cx, bce, pn->pn_right))
-            return false;
-        ptrdiff_t off = bce->offset();
-        jsbytecode *pc = bce->code(top);
-        SET_JUMP_OFFSET(pc, off - top);
-        *pc = pn->getOp();
-        return true;
-    }
-
-    MOZ_ASSERT(pn->isArity(PN_LIST));
-    MOZ_ASSERT(pn->pn_head->pn_next->pn_next);
-
     /* Left-associative operator chain: avoid too much recursion. */
     ParseNode *pn2 = pn->pn_head;
     if (!EmitTree(cx, bce, pn2))
         return false;
     ptrdiff_t top = EmitJump(cx, bce, JSOP_BACKPATCH, 0);
     if (top < 0)
         return false;
     if (Emit1(cx, bce, JSOP_POP) < 0)
@@ -7130,39 +7110,31 @@ frontend::EmitTree(ExclusiveContext *cx,
       case PNK_GE:
       case PNK_IN:
       case PNK_INSTANCEOF:
       case PNK_LSH:
       case PNK_RSH:
       case PNK_URSH:
       case PNK_STAR:
       case PNK_DIV:
-      case PNK_MOD:
-        if (pn->isArity(PN_LIST)) {
-            /* Left-associative operator chain: avoid too much recursion. */
-            ParseNode *pn2 = pn->pn_head;
-            if (!EmitTree(cx, bce, pn2))
-                return false;
-            JSOp op = pn->getOp();
-            while ((pn2 = pn2->pn_next) != nullptr) {
-                if (!EmitTree(cx, bce, pn2))
-                    return false;
-                if (Emit1(cx, bce, op) < 0)
-                    return false;
-            }
-        } else {
-            /* Binary operators that evaluate both operands unconditionally. */
-            if (!EmitTree(cx, bce, pn->pn_left))
-                return false;
-            if (!EmitTree(cx, bce, pn->pn_right))
-                return false;
-            if (Emit1(cx, bce, pn->getOp()) < 0)
+      case PNK_MOD: {
+        MOZ_ASSERT(pn->isArity(PN_LIST));
+        /* Left-associative operator chain: avoid too much recursion. */
+        ParseNode *subexpr = pn->pn_head;
+        if (!EmitTree(cx, bce, subexpr))
+            return false;
+        JSOp op = pn->getOp();
+        while ((subexpr = subexpr->pn_next) != nullptr) {
+            if (!EmitTree(cx, bce, subexpr))
+                return false;
+            if (Emit1(cx, bce, op) < 0)
                 return false;
         }
         break;
+      }
 
       case PNK_THROW:
       case PNK_TYPEOF:
       case PNK_VOID:
       case PNK_NOT:
       case PNK_BITNOT:
       case PNK_POS:
       case PNK_NEG:
@@ -7194,22 +7166,24 @@ frontend::EmitTree(ExclusiveContext *cx,
       case PNK_GENEXP:
         ok = EmitCallOrNew(cx, bce, pn);
         break;
 
       case PNK_LEXICALSCOPE:
         ok = EmitLexicalScope(cx, bce, pn);
         break;
 
-      case PNK_LET:
+      case PNK_LETBLOCK:
+      case PNK_LETEXPR:
+        ok = EmitLet(cx, bce, pn);
+        break;
+
       case PNK_CONST:
-        MOZ_ASSERT_IF(pn->isKind(PNK_CONST), !pn->isArity(PN_BINARY));
-        ok = pn->isArity(PN_BINARY)
-             ? EmitLet(cx, bce, pn)
-             : EmitVariables(cx, bce, pn, InitializeVars);
+      case PNK_LET:
+        ok = EmitVariables(cx, bce, pn, InitializeVars);
         break;
 
       case PNK_IMPORT:
       case PNK_EXPORT:
       case PNK_EXPORT_FROM:
        // TODO: Implement emitter support for modules
        bce->reportError(nullptr, JSMSG_MODULES_NOT_IMPLEMENTED);
        return false;
--- a/js/src/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -24,77 +24,403 @@ using mozilla::IsNaN;
 using mozilla::IsNegative;
 using mozilla::NegativeInfinity;
 using mozilla::PositiveInfinity;
 using JS::GenericNaN;
 using JS::ToInt32;
 using JS::ToUint32;
 
 static bool
-ContainsVarOrConst(ExclusiveContext *cx, ParseNode *pn, ParseNode **resultp)
+ContainsHoistedDeclaration(ExclusiveContext *cx, ParseNode *node, bool *result);
+
+static bool
+ListContainsHoistedDeclaration(ExclusiveContext *cx, ListNode *list, bool *result)
+{
+    for (ParseNode *node = list->pn_head; node; node = node->pn_next) {
+        if (!ContainsHoistedDeclaration(cx, node, result))
+            return false;
+        if (*result)
+            return true;
+    }
+
+    *result = false;
+    return true;
+}
+
+// Determines whether the given ParseNode contains any declarations whose
+// visibility will extend outside the node itself -- that is, whether the
+// ParseNode contains any var statements.
+//
+// THIS IS NOT A GENERAL-PURPOSE FUNCTION.  It is only written to work in the
+// specific context of deciding that |node|, as one arm of a PNK_IF controlled
+// by a constant condition, contains a declaration that forbids |node| being
+// completely eliminated as dead.
+static bool
+ContainsHoistedDeclaration(ExclusiveContext *cx, ParseNode *node, bool *result)
 {
     JS_CHECK_RECURSION(cx, return false);
 
-    if (!pn) {
-        *resultp = nullptr;
+    // With a better-typed AST, we would have distinct parse node classes for
+    // expressions and for statements and would characterize expressions with
+    // ExpressionKind and statements with StatementKind.  Perhaps someday.  In
+    // the meantime we must characterize every ParseNodeKind, even the
+    // expression/sub-expression ones that, if we handle all statement kinds
+    // correctly, we'll never see.
+    switch (node->getKind()) {
+      // Base case.
+      case PNK_VAR:
+        *result = true;
+        return true;
+
+      // Non-global lexical declarations are block-scoped (ergo not hoistable).
+      // (Global lexical declarations, in addition to being irrelevant here as
+      // ContainsHoistedDeclaration is only used on the arms of an |if|
+      // statement, are handled by PNK_GLOBALCONST and PNK_VAR.)
+      case PNK_LET:
+      case PNK_CONST:
+        MOZ_ASSERT(node->isArity(PN_LIST));
+        *result = false;
+        return true;
+
+      // ContainsHoistedDeclaration is only called on nested nodes, so any
+      // instance of this can't be function statements at body level.  In
+      // SpiderMonkey, a binding induced by a function statement is added when
+      // the function statement is evaluated.  Thus any declaration introduced
+      // by a function statement, as observed by this function, isn't a hoisted
+      // declaration.
+      case PNK_FUNCTION:
+        MOZ_ASSERT(node->isArity(PN_CODE));
+        *result = false;
         return true;
-    }
-    if (pn->isKind(PNK_VAR) || pn->isKind(PNK_CONST)) {
-        *resultp = pn;
+
+      // Statements with no sub-components at all.
+      case PNK_NOP: // induced by function f() {} function f() {}
+      case PNK_DEBUGGER:
+        MOZ_ASSERT(node->isArity(PN_NULLARY));
+        *result = false;
+        return true;
+
+      // Statements containing only an expression have no declarations.
+      case PNK_SEMI:
+      case PNK_THROW:
+        MOZ_ASSERT(node->isArity(PN_UNARY));
+        *result = false;
+        return true;
+
+      case PNK_RETURN:
+      // These two aren't statements in the spec, but we sometimes insert them
+      // in statement lists anyway.
+      case PNK_YIELD_STAR:
+      case PNK_YIELD:
+        MOZ_ASSERT(node->isArity(PN_BINARY));
+        *result = false;
+        return true;
+
+      // Other statements with no sub-statement components.
+      case PNK_BREAK:
+      case PNK_CONTINUE:
+      case PNK_IMPORT:
+      case PNK_IMPORT_SPEC_LIST:
+      case PNK_IMPORT_SPEC:
+      case PNK_EXPORT_FROM:
+      case PNK_EXPORT_SPEC_LIST:
+      case PNK_EXPORT_SPEC:
+      case PNK_EXPORT:
+      case PNK_EXPORT_BATCH_SPEC:
+        *result = false;
         return true;
-    }
-    switch (pn->getArity()) {
-      case PN_LIST:
-        for (ParseNode *pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
-            if (!ContainsVarOrConst(cx, pn2, resultp))
-                return false;
-            if (*resultp)
-                return true;
-        }
-        break;
+
+      // Statements possibly containing hoistable declarations only in the left
+      // half, in ParseNode terms -- the loop body in AST terms.
+      case PNK_DOWHILE:
+        return ContainsHoistedDeclaration(cx, node->pn_left, result);
+
+      // Statements possibly containing hoistable declarations only in the
+      // right half, in ParseNode terms -- the loop body or nested statement
+      // (usually a block statement), in AST terms.
+      case PNK_WHILE:
+      case PNK_WITH:
+        return ContainsHoistedDeclaration(cx, node->pn_right, result);
+
+      case PNK_LABEL:
+        return ContainsHoistedDeclaration(cx, node->pn_expr, result);
+
+      // Statements with more complicated structures.
+
+      // if-statement nodes may have hoisted declarations in their consequent
+      // and alternative components.
+      case PNK_IF: {
+        MOZ_ASSERT(node->isArity(PN_TERNARY));
+
+        ParseNode *consequent = node->pn_kid2;
+        if (!ContainsHoistedDeclaration(cx, consequent, result))
+            return false;
+        if (*result)
+            return true;
+
+        if (ParseNode *alternative = node->pn_kid3)
+            return ContainsHoistedDeclaration(cx, alternative, result);
+
+        *result = false;
+        return true;
+      }
+
+      // Legacy array and generator comprehensions use PNK_IF to represent
+      // conditions specified in the comprehension tail: for example,
+      // [x for (x in obj) if (x)].  The consequent of such PNK_IF nodes is
+      // either PNK_YIELD in a PNK_SEMI statement (generator comprehensions) or
+      // PNK_ARRAYPUSH (array comprehensions) .  The first case is consistent
+      // with normal if-statement structure with consequent/alternative as
+      // statements.  The second case is abnormal and requires that we not
+      // banish PNK_ARRAYPUSH to the unreachable list, handling it explicitly.
+      //
+      // We could require that this one weird PNK_ARRAYPUSH case be packaged in
+      // a PNK_SEMI, for consistency.  That requires careful bytecode emitter
+      // adjustment that seems unwarranted for a deprecated feature.
+      case PNK_ARRAYPUSH:
+        *result = false;
+        return true;
 
-      case PN_TERNARY:
-        if (!ContainsVarOrConst(cx, pn->pn_kid1, resultp))
+      // try-statements have statements to execute, and one or both of a
+      // catch-list and a finally-block.
+      case PNK_TRY: {
+        MOZ_ASSERT(node->isArity(PN_TERNARY));
+        MOZ_ASSERT(node->pn_kid2 || node->pn_kid3,
+                   "must have either catch(es) or finally");
+
+        ParseNode *tryBlock = node->pn_kid1;
+        if (!ContainsHoistedDeclaration(cx, tryBlock, result))
             return false;
-        if (*resultp)
+        if (*result)
             return true;
-        if (!ContainsVarOrConst(cx, pn->pn_kid2, resultp))
-            return false;
-        if (*resultp)
-            return true;
-        return ContainsVarOrConst(cx, pn->pn_kid3, resultp);
+
+        if (ParseNode *catchList = node->pn_kid2) {
+            for (ParseNode *lexicalScope = catchList->pn_head;
+                 lexicalScope;
+                 lexicalScope = lexicalScope->pn_next)
+            {
+                MOZ_ASSERT(lexicalScope->isKind(PNK_LEXICALSCOPE));
+
+                ParseNode *catchNode = lexicalScope->pn_expr;
+                MOZ_ASSERT(catchNode->isKind(PNK_CATCH));
+
+                ParseNode *catchStatements = catchNode->pn_kid3;
+                if (!ContainsHoistedDeclaration(cx, catchStatements, result))
+                    return false;
+                if (*result)
+                    return true;
+            }
+        }
+
+        if (ParseNode *finallyBlock = node->pn_kid3)
+            return ContainsHoistedDeclaration(cx, finallyBlock, result);
+
+        *result = false;
+        return true;
+      }
+
+      // A switch node's left half is an expression; only its right half (a
+      // list of cases/defaults, or a block node) could contain hoisted
+      // declarations.
+      case PNK_SWITCH:
+        MOZ_ASSERT(node->isArity(PN_BINARY));
+        return ContainsHoistedDeclaration(cx, node->pn_right, result);
+
+      // A case/default node's right half is its statements.  A default node's
+      // left half is null; a case node's left half is its expression.
+      case PNK_DEFAULT:
+        MOZ_ASSERT(!node->pn_left);
+      case PNK_CASE:
+        MOZ_ASSERT(node->isArity(PN_BINARY));
+        return ContainsHoistedDeclaration(cx, node->pn_right, result);
 
-      case PN_BINARY:
-      case PN_BINARY_OBJ:
-        // Limit recursion if pn is a binary expression, which can't contain a
-        // var statement.
-        if (!pn->isOp(JSOP_NOP)) {
-            *resultp = nullptr;
-            return true;
+      // PNK_SEQ has two purposes.
+      //
+      // The first is to prepend destructuring operations to the body of a
+      // deprecated function expression closure: irrelevant here, as this
+      // function doesn't recur into PNK_FUNCTION, and this method's sole
+      // caller acts upon statements nested in if-statements not found in
+      // destructuring operations.
+      //
+      // The second is to provide a place for a hoisted declaration to go, in
+      // the bizarre for-in/of loops that have as target a declaration with an
+      // assignment, e.g. |for (var i = 0 in expr)|.  This case is sadly still
+      // relevant, so we can't banish this ParseNodeKind to the unreachable
+      // list and must check every list member.
+      case PNK_SEQ:
+        return ListContainsHoistedDeclaration(cx, &node->as<ListNode>(), result);
+
+      case PNK_FOR: {
+        MOZ_ASSERT(node->isArity(PN_BINARY));
+
+        ParseNode *loopHead = node->pn_left;
+        MOZ_ASSERT(loopHead->isKind(PNK_FORHEAD) ||
+                   loopHead->isKind(PNK_FORIN) ||
+                   loopHead->isKind(PNK_FOROF));
+
+        if (loopHead->isKind(PNK_FORHEAD)) {
+            // for (init?; cond?; update?), with only init possibly containing
+            // a hoisted declaration.  (Note: a lexical-declaration |init| is
+            // (at present) hoisted in SpiderMonkey parlance -- but such
+            // hoisting doesn't extend outside of this statement, so it is not
+            // hoisting in the sense meant by ContainsHoistedDeclaration.)
+            MOZ_ASSERT(loopHead->isArity(PN_TERNARY));
+
+            ParseNode *init = loopHead->pn_kid1;
+            if (init && init->isKind(PNK_VAR)) {
+                *result = true;
+                return true;
+            }
+        } else {
+            MOZ_ASSERT(loopHead->isKind(PNK_FORIN) || loopHead->isKind(PNK_FOROF));
+
+            // for each? (target in ...), where only target may introduce
+            // hoisted declarations.
+            //
+            //   -- or --
+            //
+            // for (target of ...), where only target may introduce hoisted
+            // declarations.
+            //
+            // Either way, if |target| contains a declaration, it's either
+            // |loopHead|'s first kid, *or* that declaration was hoisted to
+            // become a child of an ancestral PNK_SEQ node.  The former case we
+            // detect here.  The latter case is handled by this method
+            // recurring on PNK_SEQ, above.
+            MOZ_ASSERT(loopHead->isArity(PN_TERNARY));
+
+            ParseNode *decl = loopHead->pn_kid1;
+            if (decl && decl->isKind(PNK_VAR)) {
+                *result = true;
+                return true;
+            }
         }
-        if (!ContainsVarOrConst(cx, pn->pn_left, resultp))
-            return false;
-        if (*resultp)
-            return true;
-        return ContainsVarOrConst(cx, pn->pn_right, resultp);
+
+        ParseNode *loopBody = node->pn_right;
+        return ContainsHoistedDeclaration(cx, loopBody, result);
+      }
+
+      case PNK_LETBLOCK: {
+        MOZ_ASSERT(node->isArity(PN_BINARY));
+        MOZ_ASSERT(node->pn_left->isKind(PNK_LET));
+        MOZ_ASSERT(node->pn_right->isKind(PNK_LEXICALSCOPE));
+        return ContainsHoistedDeclaration(cx, node->pn_right, result);
+      }
+
+      case PNK_LEXICALSCOPE: {
+        MOZ_ASSERT(node->isArity(PN_NAME));
+        ParseNode *expr = node->pn_expr;
+
+        if (expr->isKind(PNK_FOR))
+            return ContainsHoistedDeclaration(cx, expr, result);
+
+        MOZ_ASSERT(expr->isKind(PNK_STATEMENTLIST));
+        return ListContainsHoistedDeclaration(cx, &node->pn_expr->as<ListNode>(), result);
+      }
+
+      // List nodes with all non-null children.
+      case PNK_STATEMENTLIST:
+        return ListContainsHoistedDeclaration(cx, &node->as<ListNode>(), result);
 
-      case PN_UNARY:
-        if (!pn->isOp(JSOP_NOP)) {
-            *resultp = nullptr;
-            return true;
-        }
-        return ContainsVarOrConst(cx, pn->pn_kid, resultp);
+      // Grammar sub-components that should never be reached directly by this
+      // method, because some parent component should have asserted itself.
+      case PNK_COMPUTED_NAME:
+      case PNK_SPREAD:
+      case PNK_MUTATEPROTO:
+      case PNK_COLON:
+      case PNK_SHORTHAND:
+      case PNK_CONDITIONAL:
+      case PNK_TYPEOF:
+      case PNK_VOID:
+      case PNK_NOT:
+      case PNK_BITNOT:
+      case PNK_DELETE:
+      case PNK_POS:
+      case PNK_NEG:
+      case PNK_PREINCREMENT:
+      case PNK_POSTINCREMENT:
+      case PNK_PREDECREMENT:
+      case PNK_POSTDECREMENT:
+      case PNK_OR:
+      case PNK_AND:
+      case PNK_BITOR:
+      case PNK_BITXOR:
+      case PNK_BITAND:
+      case PNK_STRICTEQ:
+      case PNK_EQ:
+      case PNK_STRICTNE:
+      case PNK_NE:
+      case PNK_LT:
+      case PNK_LE:
+      case PNK_GT:
+      case PNK_GE:
+      case PNK_INSTANCEOF:
+      case PNK_IN:
+      case PNK_LSH:
+      case PNK_RSH:
+      case PNK_URSH:
+      case PNK_ADD:
+      case PNK_SUB:
+      case PNK_STAR:
+      case PNK_DIV:
+      case PNK_MOD:
+      case PNK_ASSIGN:
+      case PNK_ADDASSIGN:
+      case PNK_SUBASSIGN:
+      case PNK_BITORASSIGN:
+      case PNK_BITXORASSIGN:
+      case PNK_BITANDASSIGN:
+      case PNK_LSHASSIGN:
+      case PNK_RSHASSIGN:
+      case PNK_URSHASSIGN:
+      case PNK_MULASSIGN:
+      case PNK_DIVASSIGN:
+      case PNK_MODASSIGN:
+      case PNK_COMMA:
+      case PNK_ARRAY:
+      case PNK_OBJECT:
+      case PNK_DOT:
+      case PNK_ELEM:
+      case PNK_CALL:
+      case PNK_NAME:
+      case PNK_TEMPLATE_STRING:
+      case PNK_TEMPLATE_STRING_LIST:
+      case PNK_TAGGED_TEMPLATE:
+      case PNK_CALLSITEOBJ:
+      case PNK_STRING:
+      case PNK_REGEXP:
+      case PNK_TRUE:
+      case PNK_FALSE:
+      case PNK_NULL:
+      case PNK_THIS:
+      case PNK_LETEXPR:
+      case PNK_ELISION:
+      case PNK_NUMBER:
+      case PNK_NEW:
+      case PNK_GENERATOR:
+      case PNK_GENEXP:
+      case PNK_ARRAYCOMP:
+      case PNK_ARGSBODY:
+      case PNK_CATCHLIST:
+      case PNK_CATCH:
+      case PNK_FORIN:
+      case PNK_FOROF:
+      case PNK_FORHEAD:
+        MOZ_CRASH("ContainsHoistedDeclaration should have indicated false on "
+                  "some parent node without recurring to test this node");
 
-      case PN_NAME:
-        return ContainsVarOrConst(cx, pn->maybeExpr(), resultp);
+      case PNK_GLOBALCONST:
+        MOZ_CRASH("ContainsHoistedDeclaration is only called on nested nodes where "
+                  "globalconst nodes should never have been generated");
 
-      default:;
+      case PNK_LIMIT: // invalid sentinel value
+        MOZ_CRASH("unexpected PNK_LIMIT in node");
     }
-    *resultp = nullptr;
-    return true;
+
+    MOZ_CRASH("invalid node kind");
 }
 
 /*
  * Fold from one constant type to another.
  * XXX handles only strings and numbers for now
  */
 static bool
 FoldType(ExclusiveContext *cx, ParseNode *pn, ParseNodeKind kind)
@@ -419,33 +745,37 @@ Fold(ExclusiveContext *cx, ParseNode **p
       case PN_NULLARY:
         break;
     }
 
     // The immediate child of a PNK_DELETE node should not be replaced
     // with node indicating a different syntactic form; |delete x| is not
     // the same as |delete (true && x)|. See bug 888002.
     //
-    // pn is the immediate child in question. Its descendents were already
+    // pn is the immediate child in question. Its descendants were already
     // constant-folded above, so we're done.
     if (sc == SyntacticContext::Delete)
         return true;
 
     switch (pn->getKind()) {
       case PNK_IF:
         {
-            ParseNode *decl;
-            if (!ContainsVarOrConst(cx, pn2, &decl))
-                return false;
-            if (decl)
-                break;
-            if (!ContainsVarOrConst(cx, pn3, &decl))
-                return false;
-            if (decl)
-                break;
+            bool result;
+            if (ParseNode *consequent = pn2) {
+                if (!ContainsHoistedDeclaration(cx, consequent, &result))
+                    return false;
+                if (result)
+                    break;
+            }
+            if (ParseNode *alternative = pn3) {
+                if (!ContainsHoistedDeclaration(cx, alternative, &result))
+                    return false;
+                if (result)
+                    break;
+            }
         }
         /* FALL THROUGH */
 
       case PNK_CONDITIONAL:
         /* Reduce 'if (C) T; else E' into T for true C, E for false. */
         switch (pn1->getKind()) {
           case PNK_NUMBER:
             if (pn1->pn_dval == 0 || IsNaN(pn1->pn_dval))
@@ -490,102 +820,57 @@ Fold(ExclusiveContext *cx, ParseNode **p
         }
         if (pn3 && pn3 != pn2)
             handler.freeTree(pn3);
         break;
 
       case PNK_OR:
       case PNK_AND:
         if (sc == SyntacticContext::Condition) {
-            if (pn->isArity(PN_LIST)) {
-                ParseNode **listp = &pn->pn_head;
-                MOZ_ASSERT(*listp == pn1);
-                uint32_t orig = pn->pn_count;
-                do {
-                    Truthiness t = Boolish(pn1);
-                    if (t == Unknown) {
-                        listp = &pn1->pn_next;
-                        continue;
+            ParseNode **listp = &pn->pn_head;
+            MOZ_ASSERT(*listp == pn1);
+            uint32_t orig = pn->pn_count;
+            do {
+                Truthiness t = Boolish(pn1);
+                if (t == Unknown) {
+                    listp = &pn1->pn_next;
+                    continue;
+                }
+                if ((t == Truthy) == pn->isKind(PNK_OR)) {
+                    for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
+                        pn3 = pn2->pn_next;
+                        handler.freeTree(pn2);
+                        --pn->pn_count;
                     }
-                    if ((t == Truthy) == pn->isKind(PNK_OR)) {
-                        for (pn2 = pn1->pn_next; pn2; pn2 = pn3) {
-                            pn3 = pn2->pn_next;
-                            handler.freeTree(pn2);
-                            --pn->pn_count;
-                        }
-                        pn1->pn_next = nullptr;
-                        break;
-                    }
-                    MOZ_ASSERT((t == Truthy) == pn->isKind(PNK_AND));
-                    if (pn->pn_count == 1)
-                        break;
-                    *listp = pn1->pn_next;
-                    handler.freeTree(pn1);
-                    --pn->pn_count;
-                } while ((pn1 = *listp) != nullptr);
+                    pn1->pn_next = nullptr;
+                    break;
+                }
+                MOZ_ASSERT((t == Truthy) == pn->isKind(PNK_AND));
+                if (pn->pn_count == 1)
+                    break;
+                *listp = pn1->pn_next;
+                handler.freeTree(pn1);
+                --pn->pn_count;
+            } while ((pn1 = *listp) != nullptr);
 
-                // We may have to change arity from LIST to BINARY.
-                pn1 = pn->pn_head;
-                if (pn->pn_count == 2) {
-                    pn2 = pn1->pn_next;
-                    pn1->pn_next = nullptr;
-                    MOZ_ASSERT(!pn2->pn_next);
-                    pn->setArity(PN_BINARY);
-                    pn->pn_left = pn1;
-                    pn->pn_right = pn2;
-                } else if (pn->pn_count == 1) {
-                    ReplaceNode(pnp, pn1);
-                    pn = pn1;
-                } else if (orig != pn->pn_count) {
-                    // Adjust list tail.
-                    pn2 = pn1->pn_next;
-                    for (; pn1; pn2 = pn1, pn1 = pn1->pn_next)
-                        ;
-                    pn->pn_tail = &pn2->pn_next;
-                }
-            } else {
-                Truthiness t = Boolish(pn1);
-                if (t != Unknown) {
-                    if ((t == Truthy) == pn->isKind(PNK_OR)) {
-                        handler.freeTree(pn2);
-                        ReplaceNode(pnp, pn1);
-                        pn = pn1;
-                    } else {
-                        MOZ_ASSERT((t == Truthy) == pn->isKind(PNK_AND));
-                        handler.freeTree(pn1);
-                        ReplaceNode(pnp, pn2);
-                        pn = pn2;
-                    }
-                }
+            // We may have to replace a one-element list with its element.
+            pn1 = pn->pn_head;
+            if (pn->pn_count == 1) {
+                ReplaceNode(pnp, pn1);
+                pn = pn1;
+            } else if (orig != pn->pn_count) {
+                // Adjust list tail.
+                pn2 = pn1->pn_next;
+                for (; pn1; pn2 = pn1, pn1 = pn1->pn_next)
+                    continue;
+                pn->pn_tail = &pn2->pn_next;
             }
         }
         break;
 
-      case PNK_SUBASSIGN:
-      case PNK_BITORASSIGN:
-      case PNK_BITXORASSIGN:
-      case PNK_BITANDASSIGN:
-      case PNK_LSHASSIGN:
-      case PNK_RSHASSIGN:
-      case PNK_URSHASSIGN:
-      case PNK_MULASSIGN:
-      case PNK_DIVASSIGN:
-      case PNK_MODASSIGN:
-        /*
-         * Compound operators such as *= should be subject to folding, in case
-         * the left-hand side is constant, and so that the decompiler produces
-         * the same string that you get from decompiling a script or function
-         * compiled from that same string.  += is special and so must be
-         * handled below.
-         */
-        goto do_binary_op;
-
-      case PNK_ADDASSIGN:
-        MOZ_ASSERT(pn->isOp(JSOP_ADD));
-        /* FALL THROUGH */
       case PNK_ADD:
         if (pn->isArity(PN_LIST)) {
             bool folded = false;
 
             pn2 = pn1->pn_next;
             if (pn1->isKind(PNK_NUMBER)) {
                 // Fold addition of numeric literals: (1 + 2 + x === 3 + x).
                 // Note that we can only do this the front of the list:
@@ -694,58 +979,52 @@ Fold(ExclusiveContext *cx, ParseNode **p
             pn->setOp(JSOP_STRING);
             pn->setArity(PN_NULLARY);
             handler.freeTree(pn1);
             handler.freeTree(pn2);
             break;
         }
 
         /* Can't concatenate string literals, let's try numbers. */
-        goto do_binary_op;
+        if (!FoldType(cx, pn1, PNK_NUMBER) || !FoldType(cx, pn2, PNK_NUMBER))
+            return false;
+        if (pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER)) {
+            if (!FoldBinaryNumeric(cx, pn->getOp(), pn1, pn2, pn))
+                return false;
+        }
+        break;
 
       case PNK_SUB:
       case PNK_STAR:
       case PNK_LSH:
       case PNK_RSH:
       case PNK_URSH:
       case PNK_DIV:
       case PNK_MOD:
-      do_binary_op:
-        if (pn->isArity(PN_LIST)) {
-            MOZ_ASSERT(pn->pn_count > 2);
-            for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
-                if (!FoldType(cx, pn2, PNK_NUMBER))
-                    return false;
-            }
-            for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
-                /* XXX fold only if all operands convert to number */
-                if (!pn2->isKind(PNK_NUMBER))
-                    break;
-            }
-            if (!pn2) {
-                JSOp op = pn->getOp();
+        MOZ_ASSERT(pn->getArity() == PN_LIST);
+        MOZ_ASSERT(pn->pn_count >= 2);
+        for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
+            if (!FoldType(cx, pn2, PNK_NUMBER))
+                return false;
+        }
+        for (pn2 = pn1; pn2; pn2 = pn2->pn_next) {
+            /* XXX fold only if all operands convert to number */
+            if (!pn2->isKind(PNK_NUMBER))
+                break;
+        }
+        if (!pn2) {
+            JSOp op = pn->getOp();
 
-                pn2 = pn1->pn_next;
+            pn2 = pn1->pn_next;
+            pn3 = pn2->pn_next;
+            if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn))
+                return false;
+            while ((pn2 = pn3) != nullptr) {
                 pn3 = pn2->pn_next;
-                if (!FoldBinaryNumeric(cx, op, pn1, pn2, pn))
-                    return false;
-                while ((pn2 = pn3) != nullptr) {
-                    pn3 = pn2->pn_next;
-                    if (!FoldBinaryNumeric(cx, op, pn, pn2, pn))
-                        return false;
-                }
-            }
-        } else {
-            MOZ_ASSERT(pn->isArity(PN_BINARY));
-            if (!FoldType(cx, pn1, PNK_NUMBER) ||
-                !FoldType(cx, pn2, PNK_NUMBER)) {
-                return false;
-            }
-            if (pn1->isKind(PNK_NUMBER) && pn2->isKind(PNK_NUMBER)) {
-                if (!FoldBinaryNumeric(cx, pn->getOp(), pn1, pn2, pn))
+                if (!FoldBinaryNumeric(cx, op, pn, pn2, pn))
                     return false;
             }
         }
         break;
 
       case PNK_TYPEOF:
       case PNK_VOID:
       case PNK_NOT:
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -22,17 +22,16 @@ class Parser;
 class SyntaxParseHandler;
 
 // Parse handler used when generating a full parse tree for all code which the
 // parser encounters.
 class FullParseHandler
 {
     ParseNodeAllocator allocator;
     TokenStream &tokenStream;
-    bool foldConstants;
 
     ParseNode *allocParseNode(size_t size) {
         MOZ_ASSERT(size == sizeof(ParseNode));
         return static_cast<ParseNode *>(allocator.allocNode());
     }
 
     ParseNode *cloneNode(const ParseNode &other) {
         ParseNode *node = allocParseNode(sizeof(ParseNode));
@@ -66,21 +65,20 @@ class FullParseHandler
 
     /* new_ methods for creating parse nodes. These report OOM on context. */
     JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
 
     typedef ParseNode *Node;
     typedef Definition *DefinitionNode;
 
     FullParseHandler(ExclusiveContext *cx, LifoAlloc &alloc,
-                     TokenStream &tokenStream, bool foldConstants,
-                     Parser<SyntaxParseHandler> *syntaxParser, LazyScript *lazyOuterFunction)
+                     TokenStream &tokenStream, Parser<SyntaxParseHandler> *syntaxParser,
+                     LazyScript *lazyOuterFunction)
       : allocator(cx, alloc),
         tokenStream(tokenStream),
-        foldConstants(foldConstants),
         lazyOuterFunction_(lazyOuterFunction),
         lazyInnerFunctionIndex(0),
         syntaxParser(syntaxParser)
     {}
 
     static ParseNode *null() { return nullptr; }
 
     ParseNode *freeTree(ParseNode *pn) { return allocator.freeTree(pn); }
@@ -212,20 +210,20 @@ class FullParseHandler
                          JSOp op = JSOP_NOP) {
         return new_<BinaryNode>(kind, op, left->pn_pos, left, (ParseNode *) nullptr);
     }
     ParseNode *newBinary(ParseNodeKind kind, ParseNode *left, ParseNode *right,
                          JSOp op = JSOP_NOP) {
         TokenPos pos(left->pn_pos.begin, right->pn_pos.end);
         return new_<BinaryNode>(kind, op, pos, left, right);
     }
-    ParseNode *newBinaryOrAppend(ParseNodeKind kind, ParseNode *left, ParseNode *right,
-                                 ParseContext<FullParseHandler> *pc, JSOp op = JSOP_NOP)
+    ParseNode *appendOrCreateList(ParseNodeKind kind, ParseNode *left, ParseNode *right,
+                                  ParseContext<FullParseHandler> *pc, JSOp op = JSOP_NOP)
     {
-        return ParseNode::newBinaryOrAppend(kind, op, left, right, this, pc, foldConstants);
+        return ParseNode::appendOrCreateList(kind, op, left, right, this, pc);
     }
 
     ParseNode *newTernary(ParseNodeKind kind,
                           ParseNode *first, ParseNode *second, ParseNode *third,
                           JSOp op = JSOP_NOP)
     {
         return new_<TernaryNode>(kind, op, first, second, third);
     }
@@ -535,20 +533,36 @@ class FullParseHandler
 
     ParseNode *newLexicalScope(ObjectBox *blockBox) {
         return new_<LexicalScopeNode>(blockBox, pos());
     }
     void setLexicalScopeBody(ParseNode *block, ParseNode *body) {
         block->pn_expr = body;
     }
 
+    ParseNode *newLetExpression(ParseNode *vars, ParseNode *block, const TokenPos &pos) {
+        ParseNode *letExpr = newBinary(PNK_LETEXPR, vars, block);
+        if (!letExpr)
+            return nullptr;
+        letExpr->pn_pos = pos;
+        return letExpr;
+    }
+
+    ParseNode *newLetBlock(ParseNode *vars, ParseNode *block, const TokenPos &pos) {
+        ParseNode *letBlock = newBinary(PNK_LETBLOCK, vars, block);
+        if (!letBlock)
+            return nullptr;
+        letBlock->pn_pos = pos;
+        return letBlock;
+    }
+
     ParseNode *newAssignment(ParseNodeKind kind, ParseNode *lhs, ParseNode *rhs,
                              ParseContext<FullParseHandler> *pc, JSOp op)
     {
-        return newBinaryOrAppend(kind, lhs, rhs, pc, op);
+        return newBinary(kind, lhs, rhs, op);
     }
 
     bool isUnparenthesizedYieldExpression(ParseNode *node) {
         return node->isKind(PNK_YIELD) && !node->isInParens();
     }
 
     bool isUnparenthesizedCommaExpression(ParseNode *node) {
         return node->isKind(PNK_COMMA) && !node->isInParens();
@@ -595,16 +609,19 @@ class FullParseHandler
         return new_<ListNode>(kind, op, pos());
     }
 
     /* New list with one initial child node. kid must be non-null. */
     ParseNode *newList(ParseNodeKind kind, ParseNode *kid, JSOp op = JSOP_NOP) {
         return new_<ListNode>(kind, op, kid);
     }
 
+    ParseNode *newCatchList() {
+        return new_<ListNode>(PNK_CATCHLIST, JSOP_NOP, pos());
+    }
 
     ParseNode *newCommaExpressionList(ParseNode *kid) {
         return newList(PNK_COMMA, kid, JSOP_NOP);
     }
 
     void addList(ParseNode *list, ParseNode *kid) {
         list->append(kid);
     }
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -240,66 +240,48 @@ ParseNodeAllocator::allocNode()
 
     void *p = alloc.alloc(sizeof (ParseNode));
     if (!p)
         js_ReportOutOfMemory(cx);
     return p;
 }
 
 ParseNode *
-ParseNode::append(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
-                  FullParseHandler *handler)
+ParseNode::appendOrCreateList(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
+                              FullParseHandler *handler, ParseContext<FullParseHandler> *pc)
 {
-    if (!left || !right)
-        return nullptr;
-
-    MOZ_ASSERT(left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC));
+    // The asm.js specification is written in ECMAScript grammar terms that
+    // specify *only* a binary tree.  It's a royal pain to implement the asm.js
+    // spec to act upon n-ary lists as created below.  So for asm.js, form a
+    // binary tree of lists exactly as ECMAScript would by skipping the
+    // following optimization.
+    if (!pc->useAsmOrInsideUseAsm()) {
+        // Left-associative trees of a given operator (e.g. |a + b + c|) are
+        // binary trees in the spec: (+ (+ a b) c) in Lisp terms.  Recursively
+        // processing such a tree, exactly implemented that way, would blow the
+        // the stack.  We use a list node that uses O(1) stack to represent
+        // such operations: (+ a b c).
+        if (left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC)) {
+            ListNode *list = &left->as<ListNode>();
 
-    ListNode *list;
-    if (left->pn_arity == PN_LIST) {
-        list = &left->as<ListNode>();
-    } else {
-        ParseNode *pn1 = left->pn_left, *pn2 = left->pn_right;
-        list = handler->new_<ListNode>(kind, op, pn1);
-        if (!list)
-            return nullptr;
-        list->append(pn2);
+            list->append(right);
+            list->pn_pos.end = right->pn_pos.end;
+
+            return list;
+        }
     }
 
+    ParseNode *list = handler->new_<ListNode>(kind, op, left);
+    if (!list)
+        return nullptr;
+
     list->append(right);
-    list->pn_pos.end = right->pn_pos.end;
-
     return list;
 }
 
-ParseNode *
-ParseNode::newBinaryOrAppend(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
-                             FullParseHandler *handler, ParseContext<FullParseHandler> *pc,
-                             bool foldConstants)
-{
-    if (!left || !right)
-        return nullptr;
-
-    /*
-     * Ensure that the parse tree is faithful to the source when "use asm" (for
-     * the purpose of type checking).
-     */
-    if (pc->useAsmOrInsideUseAsm())
-        return handler->new_<BinaryNode>(kind, op, left, right);
-
-    /*
-     * Flatten a left-associative (left-heavy) tree of a given operator into
-     * a list to reduce js::FoldConstants and js::frontend::EmitTree recursion.
-     */
-    if (left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC))
-        return append(kind, op, left, right, handler);
-
-    return handler->new_<BinaryNode>(kind, op, left, right);
-}
-
 const char *
 Definition::kindString(Kind kind)
 {
     static const char * const table[] = {
         "", js_var_str, js_const_str, js_const_str, js_let_str, "argument", js_function_str, "unknown"
     };
 
     MOZ_ASSERT(unsigned(kind) <= unsigned(ARG));
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -98,17 +98,16 @@ class UpvarCookie
     F(CALLSITEOBJ) \
     F(REGEXP) \
     F(TRUE) \
     F(FALSE) \
     F(NULL) \
     F(THIS) \
     F(FUNCTION) \
     F(IF) \
-    F(ELSE) \
     F(SWITCH) \
     F(CASE) \
     F(DEFAULT) \
     F(WHILE) \
     F(DOWHILE) \
     F(FOR) \
     F(BREAK) \
     F(CONTINUE) \
@@ -117,27 +116,28 @@ class UpvarCookie
     F(GLOBALCONST) \
     F(WITH) \
     F(RETURN) \
     F(NEW) \
     F(DELETE) \
     F(TRY) \
     F(CATCH) \
     F(CATCHLIST) \
-    F(FINALLY) \
     F(THROW) \
     F(DEBUGGER) \
     F(GENERATOR) \
     F(YIELD) \
     F(YIELD_STAR) \
     F(GENEXP) \
     F(ARRAYCOMP) \
     F(ARRAYPUSH) \
     F(LEXICALSCOPE) \
     F(LET) \
+    F(LETBLOCK) \
+    F(LETEXPR) \
     F(IMPORT) \
     F(IMPORT_SPEC_LIST) \
     F(IMPORT_SPEC) \
     F(EXPORT) \
     F(EXPORT_FROM) \
     F(EXPORT_SPEC_LIST) \
     F(EXPORT_SPEC) \
     F(EXPORT_BATCH_SPEC) \
@@ -282,20 +282,21 @@ enum ParseNodeKind
  *                            to left of 'of'; if pn_kid1, then this
  *                            is a clone of pn_kid1->pn_head
  *                          pn_kid3: expr to right of 'of'
  * PNK_FORHEAD  ternary     pn_kid1:  init expr before first ';' or nullptr
  *                          pn_kid2:  cond expr before second ';' or nullptr
  *                          pn_kid3:  update expr after second ';' or nullptr
  * PNK_THROW    unary       pn_op: JSOP_THROW, pn_kid: exception
  * PNK_TRY      ternary     pn_kid1: try block
- *                          pn_kid2: null or PNK_CATCHLIST list of
- *                          PNK_LEXICALSCOPE nodes, each with pn_expr pointing
- *                          to a PNK_CATCH node
+ *                          pn_kid2: null or PNK_CATCHLIST list
  *                          pn_kid3: null or finally block
+ * PNK_CATCHLIST list       pn_head: list of PNK_LEXICALSCOPE nodes, one per
+ *                                   catch-block, each with pn_expr pointing
+ *                                   to a PNK_CATCH node
  * PNK_CATCH    ternary     pn_kid1: PNK_NAME, PNK_ARRAY, or PNK_OBJECT catch var node
  *                                   (PNK_ARRAY or PNK_OBJECT if destructuring)
  *                          pn_kid2: null or the catch guard expression
  *                          pn_kid3: catch block statements
  * PNK_BREAK    name        pn_atom: label or null
  * PNK_CONTINUE name        pn_atom: label or null
  * PNK_WITH     binary-obj  pn_left: head expr; pn_right: body; pn_binary_obj: StaticWithObject
  * PNK_VAR,     list        pn_head: list of PNK_NAME or PNK_ASSIGN nodes
@@ -504,16 +505,21 @@ class ParseNode
     bool isArity(ParseNodeArity a) const   { return getArity() == a; }
     void setArity(ParseNodeArity a)        { pn_arity = a; }
 
     bool isAssignment() const {
         ParseNodeKind kind = getKind();
         return PNK_ASSIGNMENT_START <= kind && kind <= PNK_ASSIGNMENT_LAST;
     }
 
+    bool isBinaryOperation() const {
+        ParseNodeKind kind = getKind();
+        return PNK_BINOP_FIRST <= kind && kind <= PNK_BINOP_LAST;
+    }
+
     /* Boolean attributes. */
     bool isInParens() const                { return pn_parens; }
     bool isLikelyIIFE() const              { return isInParens(); }
     void setInParens(bool enabled)         { pn_parens = enabled; }
     bool isUsed() const                    { return pn_used; }
     void setUsed(bool enabled)             { pn_used = enabled; }
     bool isDefn() const                    { return pn_defn; }
     void setDefn(bool enabled)             { pn_defn = enabled; }
@@ -635,31 +641,22 @@ class ParseNode
         pn_parens = false;
         MOZ_ASSERT(!pn_used);
         MOZ_ASSERT(!pn_defn);
         pn_next = pn_link = nullptr;
     }
 
   public:
     /*
-     * Append right to left, forming a list node.  |left| must have the given
-     * kind and op, and op must be left-associative.
+     * If |left| is a list of the given kind/left-associative op, append
+     * |right| to it and return |left|.  Otherwise return a [left, right] list.
      */
     static ParseNode *
-    append(ParseNodeKind tt, JSOp op, ParseNode *left, ParseNode *right, FullParseHandler *handler);
-
-    /*
-     * Either append right to left, if left meets the conditions necessary to
-     * append (see append), or form a binary node whose children are right and
-     * left.
-     */
-    static ParseNode *
-    newBinaryOrAppend(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
-                      FullParseHandler *handler, ParseContext<FullParseHandler> *pc,
-                      bool foldConstants);
+    appendOrCreateList(ParseNodeKind kind, JSOp op, ParseNode *left, ParseNode *right,
+                       FullParseHandler *handler, ParseContext<FullParseHandler> *pc);
 
     inline PropertyName *name() const;
     inline JSAtom *atom() const;
 
     /*
      * The pn_expr and lexdef members are arms of an unsafe union. Unless you
      * know exactly what you're doing, use only the following methods to access
      * them. For less overhead and assertions for protection, use pn->expr()
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -511,17 +511,17 @@ Parser<ParseHandler>::Parser(ExclusiveCo
     abortedSyntaxParse(false),
     isUnexpectedEOF_(false),
     sawDeprecatedForEach(false),
     sawDeprecatedDestructuringForIn(false),
     sawDeprecatedLegacyGenerator(false),
     sawDeprecatedExpressionClosure(false),
     sawDeprecatedLetBlock(false),
     sawDeprecatedLetExpression(false),
-    handler(cx, *alloc, tokenStream, foldConstants, syntaxParser, lazyOuterFunction)
+    handler(cx, *alloc, tokenStream, syntaxParser, lazyOuterFunction)
 {
     {
         AutoLockForExclusiveAccess lock(cx);
         cx->perThreadData->addActiveCompilation();
     }
 
     // The Mozilla specific JSOPTION_EXTRA_WARNINGS option adds extra warnings
     // which are not generated if functions are parsed lazily. Note that the
@@ -1225,30 +1225,20 @@ struct BindData
         this->op = op;
         this->isConst = op == JSOP_DEFCONST;
         this->binder = Parser<ParseHandler>::bindVarOrGlobalConst;
     }
 };
 
 template <typename ParseHandler>
 JSFunction *
-Parser<ParseHandler>::newFunction(GenericParseContext *pc, HandleAtom atom,
-                                  FunctionSyntaxKind kind, JSObject *proto)
+Parser<ParseHandler>::newFunction(HandleAtom atom, FunctionSyntaxKind kind, JSObject *proto)
 {
     MOZ_ASSERT_IF(kind == Statement, atom != nullptr);
 
-    /*
-     * Find the global compilation context in order to pre-set the newborn
-     * function's parent slot to pc->sc->as<GlobalObject>()->scopeChain. If the
-     * global context is a compile-and-go one, we leave the pre-set parent
-     * intact; otherwise we clear parent and proto.
-     */
-    while (pc->parent)
-        pc = pc->parent;
-
     RootedFunction fun(context);
     JSFunction::Flags flags = (kind == Expression)
                               ? JSFunction::INTERPRETED_LAMBDA
                               : (kind == Arrow)
                                 ? JSFunction::INTERPRETED_LAMBDA_ARROW
                                 : JSFunction::INTERPRETED;
     gc::AllocKind allocKind = JSFunction::FinalizeKind;
     if (kind == Arrow)
@@ -2184,17 +2174,17 @@ Parser<ParseHandler>::functionDef(Handle
         // If we are off the main thread, the generator meta-objects have
         // already been created by js::StartOffThreadParseScript, so cx will not
         // be necessary.
         JSContext *cx = context->maybeJSContext();
         proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global());
         if (!proto)
             return null();
     }
-    RootedFunction fun(context, newFunction(pc, funName, kind, proto));
+    RootedFunction fun(context, newFunction(funName, kind, proto));
     if (!fun)
         return null();
 
     // Speculatively parse using the directives of the parent parsing context.
     // If a directive is encountered (e.g., "use strict") that changes how the
     // function should have been parsed, we backup and reparse with the new set
     // of directives.
     Directives directives(pc);
@@ -3627,17 +3617,17 @@ Parser<SyntaxParseHandler>::pushLetScope
 
 /*
  * Parse a let block statement or let expression (determined by 'letContext').
  * In both cases, bindings are not hoisted to the top of the enclosing block
  * and thus must be carefully injected between variables() and the let body.
  */
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::letBlock(LetContext letContext)
+Parser<ParseHandler>::deprecatedLetBlockOrExpression(LetContext letContext)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LET));
 
     RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context));
     if (!blockObj)
         return null();
 
     uint32_t begin = pos().begin;
@@ -3650,21 +3640,16 @@ Parser<ParseHandler>::letBlock(LetContex
 
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_LET);
 
     StmtInfoPC stmtInfo(context);
     Node block = pushLetScope(blockObj, &stmtInfo);
     if (!block)
         return null();
 
-    Node pnlet = handler.newBinary(PNK_LET, vars, block);
-    if (!pnlet)
-        return null();
-    handler.setBeginPosition(pnlet, begin);
-
     bool needExprStmt = false;
     if (letContext == LetStatement) {
         bool matched;
         if (!tokenStream.matchToken(&matched, TOK_LC, TokenStream::Operand))
             return null();
         if (!matched) {
             /*
              * Strict mode eliminates a grammar ambiguity with unparenthesized
@@ -3677,26 +3662,26 @@ Parser<ParseHandler>::letBlock(LetContex
              *   // Does this parse as
              *   //   (let (loc = "inner") id)(loc) // "outer"
              *   // or as
              *   //   let (loc = "inner") (id(loc)) // "inner"
              *   let (loc = "inner") id(loc);
              *
              * See bug 569464.
              */
-            if (!report(ParseStrictError, pc->sc->strict, pnlet,
-                        JSMSG_STRICT_CODE_LET_EXPR_STMT))
+            if (!reportWithOffset(ParseStrictError, pc->sc->strict, begin,
+                                  JSMSG_STRICT_CODE_LET_EXPR_STMT))
             {
                 return null();
             }
 
             /*
              * If this is really an expression in let statement guise, then we
-             * need to wrap the PNK_LET node in a PNK_SEMI node so that we pop
-             * the return value of the expression.
+             * need to wrap the PNK_LETEXPR node in a PNK_SEMI node so that we
+             * pop the return value of the expression.
              */
             needExprStmt = true;
             letContext = LetExpression;
         }
     }
 
     Node expr;
     if (letContext == LetStatement) {
@@ -3716,24 +3701,32 @@ Parser<ParseHandler>::letBlock(LetContex
 
         sawDeprecatedLetExpression = true;
         if (!report(ParseWarning, pc->sc->strict, expr, JSMSG_DEPRECATED_LET_EXPRESSION))
             return null();
     }
     handler.setLexicalScopeBody(block, expr);
     PopStatementPC(tokenStream, pc);
 
-    handler.setEndPosition(pnlet, pos().end);
-
-    if (needExprStmt) {
-        if (!MatchOrInsertSemicolon(tokenStream))
-            return null();
-        return handler.newExprStatement(pnlet, pos().end);
-    }
-    return pnlet;
+    TokenPos letPos(begin, pos().end);
+
+    if (letContext == LetExpression) {
+        if (needExprStmt) {
+            if (!MatchOrInsertSemicolon(tokenStream))
+                return null();
+        }
+
+        Node letExpr = handler.newLetExpression(vars, block, letPos);
+        if (!letExpr)
+            return null();
+
+        return needExprStmt ? handler.newExprStatement(letExpr, pos().end) : letExpr;
+    }
+
+    return handler.newLetBlock(vars, block, letPos);
 }
 
 template <typename ParseHandler>
 static bool
 PushBlocklikeStatement(TokenStream &ts, StmtInfoPC *stmt, StmtType type,
                        ParseContext<ParseHandler> *pc)
 {
     PushStatementPC(pc, stmt, type);
@@ -3883,17 +3876,17 @@ Parser<ParseHandler>::variables(ParseNod
 
                 Node init = assignExpr();
                 if (!init)
                     return null();
 
                 if (!bindBeforeInitializer && !checkDestructuring(&data, pn2))
                     return null();
 
-                pn2 = handler.newBinaryOrAppend(PNK_ASSIGN, pn2, init, pc);
+                pn2 = handler.newBinary(PNK_ASSIGN, pn2, init);
                 if (!pn2)
                     return null();
                 handler.addList(pn, pn2);
                 break;
             }
 
             if (tt != TOK_NAME) {
                 if (tt == TOK_YIELD) {
@@ -4102,37 +4095,54 @@ SyntaxParseHandler::Node
 Parser<SyntaxParseHandler>::lexicalDeclaration(bool)
 {
     JS_ALWAYS_FALSE(abortIfSyntaxParser());
     return SyntaxParseHandler::NodeFailure;
 }
 
 template <>
 ParseNode *
-Parser<FullParseHandler>::letStatement()
+Parser<FullParseHandler>::letDeclarationOrBlock()
 {
     handler.disableSyntaxParser();
 
     /* Check for a let statement or let expression. */
-    ParseNode *pn;
     TokenKind tt;
     if (!tokenStream.peekToken(&tt))
         return null();
     if (tt == TOK_LP) {
-        pn = letBlock(LetStatement);
-        MOZ_ASSERT_IF(pn, pn->isKind(PNK_LET) || pn->isKind(PNK_SEMI));
-    } else {
-        pn = lexicalDeclaration(/* isConst = */ false);
-    }
-    return pn;
+        ParseNode *node = deprecatedLetBlockOrExpression(LetStatement);
+        if (!node)
+            return nullptr;
+
+        if (node->isKind(PNK_LETBLOCK)) {
+            MOZ_ASSERT(node->isArity(PN_BINARY));
+        } else {
+            MOZ_ASSERT(node->isKind(PNK_SEMI));
+            MOZ_ASSERT(node->pn_kid->isKind(PNK_LETEXPR));
+            MOZ_ASSERT(node->pn_kid->isArity(PN_BINARY));
+        }
+
+        return node;
+    }
+
+    ParseNode *decl = lexicalDeclaration(/* isConst = */ false);
+    if (!decl)
+        return nullptr;
+
+    // let-declarations at global scope are currently treated as plain old var.
+    // See bug 589199.
+    MOZ_ASSERT(decl->isKind(PNK_LET) || decl->isKind(PNK_VAR));
+    MOZ_ASSERT(decl->isArity(PN_LIST));
+    return decl;
 }
 
 template <>
 SyntaxParseHandler::Node
-Parser<SyntaxParseHandler>::letStatement()
+Parser<SyntaxParseHandler>::letDeclarationOrBlock()
 {
     JS_ALWAYS_FALSE(abortIfSyntaxParser());
     return SyntaxParseHandler::NodeFailure;
 }
 
 template<typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::importDeclaration()
@@ -4660,17 +4670,17 @@ Parser<FullParseHandler>::forStatement()
                 pn1 = variables(PNK_VAR);
             } else if (tt == TOK_LET || tt == TOK_CONST) {
                 handler.disableSyntaxParser();
                 bool constDecl = tt == TOK_CONST;
                 tokenStream.consumeKnownToken(tt);
                 if (!tokenStream.peekToken(&tt))
                     return null();
                 if (tt == TOK_LP) {
-                    pn1 = letBlock(LetExpression);
+                    pn1 = deprecatedLetBlockOrExpression(LetExpression);
                 } else {
                     isForDecl = true;
                     blockObj = StaticBlockObject::create(context);
                     if (!blockObj)
                         return null();
                     pn1 = variables(constDecl ? PNK_CONST : PNK_LET, nullptr, blockObj,
                                     DontHoistVars);
                 }
@@ -4933,21 +4943,17 @@ Parser<FullParseHandler>::forStatement()
             return null();
         pnseq->pn_pos = forLoop->pn_pos;
         pnseq->append(forLoop);
         return pnseq;
     }
     if (forLetImpliedBlock) {
         forLetImpliedBlock->pn_expr = forLoop;
         forLetImpliedBlock->pn_pos = forLoop->pn_pos;
-        ParseNode *let = handler.newBinary(PNK_LET, forLetDecl, forLetImpliedBlock);
-        if (!let)
-            return null();
-        let->pn_pos = forLoop->pn_pos;
-        return let;
+        return handler.newLetBlock(forLetDecl, forLetImpliedBlock, forLoop->pn_pos);
     }
     return forLoop;
 }
 
 template <>
 SyntaxParseHandler::Node
 Parser<SyntaxParseHandler>::forStatement()
 {
@@ -5634,17 +5640,17 @@ Parser<ParseHandler>::tryStatement()
     PopStatementPC(tokenStream, pc);
 
     bool hasUnconditionalCatch = false;
     Node catchList = null();
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
     if (tt == TOK_CATCH) {
-        catchList = handler.newList(PNK_CATCH);
+        catchList = handler.newCatchList();
         if (!catchList)
             return null();
 
         do {
             Node pnblock;
             BindData<ParseHandler> data(context);
 
             /* Check for another catch after unconditional catch. */
@@ -5817,17 +5823,17 @@ Parser<ParseHandler>::statement(bool can
         handler.setListFlag(pn, PNX_POPVAR);
 
         if (!MatchOrInsertSemicolon(tokenStream))
             return null();
         return pn;
       }
 
       case TOK_LET:
-        return letStatement();
+        return letDeclarationOrBlock();
       case TOK_IMPORT:
         return importDeclaration();
       case TOK_EXPORT:
         return exportDeclaration();
       case TOK_SEMI:
         return handler.newEmptyStatement(pos());
       case TOK_IF:
         return ifStatement();
@@ -6072,17 +6078,17 @@ Parser<ParseHandler>::orExpr1(InvokedPre
         // The >= in this condition works because all the operators in question
         // are left-associative; if any were not, the case where two operators
         // have equal precedence would need to be handled specially, and the
         // stack would need to be a Vector.
         while (depth > 0 && Precedence(kindStack[depth - 1]) >= Precedence(pnk)) {
             depth--;
             ParseNodeKind combiningPnk = kindStack[depth];
             JSOp combiningOp = BinaryOpParseNodeKindToJSOp(combiningPnk);
-            pn = handler.newBinaryOrAppend(combiningPnk, nodeStack[depth], pn, pc, combiningOp);
+            pn = handler.appendOrCreateList(combiningPnk, nodeStack[depth], pn, pc, combiningOp);
             if (!pn)
                 return pn;
         }
 
         if (pnk == PNK_LIMIT)
             break;
 
         nodeStack[depth] = pn;
@@ -7057,17 +7063,17 @@ Parser<ParseHandler>::generatorComprehen
     RootedObject proto(context);
     if (comprehensionKind == StarGenerator) {
         JSContext *cx = context->maybeJSContext();
         proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global());
         if (!proto)
             return null();
     }
 
-    RootedFunction fun(context, newFunction(outerpc, /* atom = */ NullPtr(), Expression, proto));
+    RootedFunction fun(context, newFunction(/* atom = */ NullPtr(), Expression, proto));
     if (!fun)
         return null();
 
     // Create box for fun->object early to root it.
     Directives directives(/* strict = */ outerpc->sc->strict);
     FunctionBox *genFunbox = newFunctionBox(genfn, fun, outerpc, directives, comprehensionKind);
     if (!genFunbox)
         return null();
@@ -7906,16 +7912,18 @@ Parser<ParseHandler>::objectLiteral()
         JSOp op = JSOP_INITPROP;
         Node propname;
         switch (ltok) {
           case TOK_NUMBER:
             atom = DoubleToAtom(context, tokenStream.currentToken().number());
             if (!atom)
                 return null();
             propname = newNumber(tokenStream.currentToken());
+            if (!propname)
+                return null();
             break;
 
           case TOK_LB: {
               propname = computedPropertyName(literal);
               if (!propname)
                   return null();
               break;
           }
@@ -8061,16 +8069,18 @@ Parser<ParseHandler>::objectLiteral()
                 if (!tokenStream.checkForKeyword(atom, nullptr))
                     return null();
                 PropertyName *name = handler.isName(propname);
                 MOZ_ASSERT(atom);
                 propname = newName(name);
                 if (!propname)
                     return null();
                 Node ident = identifierName();
+                if (!ident)
+                    return null();
                 if (!handler.addPropertyDefinition(literal, propname, ident, true))
                     return null();
             } else if (tt == TOK_LP) {
                 tokenStream.ungetToken();
                 if (!methodDefinition(literal, propname, Normal, Method,
                                       isGenerator ? StarGenerator : NotGenerator, op)) {
                     return null();
                 }
@@ -8134,17 +8144,17 @@ Parser<ParseHandler>::primaryExpr(TokenK
 
       case TOK_LB:
         return arrayInitializer();
 
       case TOK_LC:
         return objectLiteral();
 
       case TOK_LET:
-        return letBlock(LetExpression);
+        return deprecatedLetBlockOrExpression(LetExpression);
 
       case TOK_LP: {
         TokenKind next;
         if (!tokenStream.peekToken(&next, TokenStream::Operand))
             return null();
         if (next != TOK_RP)
             return parenExprOrGeneratorComprehension();
 
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -438,21 +438,20 @@ class Parser : private JS::AutoGCRooter,
      * Allocate a new parsed object or function container from
      * cx->tempLifoAlloc.
      */
     ObjectBox *newObjectBox(NativeObject *obj);
     FunctionBox *newFunctionBox(Node fn, JSFunction *fun, ParseContext<ParseHandler> *pc,
                                 Directives directives, GeneratorKind generatorKind);
 
     /*
-     * Create a new function object given parse context (pc) and a name (which
-     * is optional if this is a function expression).
+     * Create a new function object given a name (which is optional if this is
+     * a function expression).
      */
-    JSFunction *newFunction(GenericParseContext *pc, HandleAtom atom, FunctionSyntaxKind kind,
-                            JSObject *proto = nullptr);
+    JSFunction *newFunction(HandleAtom atom, FunctionSyntaxKind kind, JSObject *proto = nullptr);
 
     void trace(JSTracer *trc);
 
     bool hadAbortedSyntaxParse() {
         return abortedSyntaxParse;
     }
     void clearAbortedSyntaxParse() {
         abortedSyntaxParse = false;
@@ -553,17 +552,17 @@ class Parser : private JS::AutoGCRooter,
     Node returnStatement();
     Node withStatement();
     Node labeledStatement();
     Node throwStatement();
     Node tryStatement();
     Node debuggerStatement();
 
     Node lexicalDeclaration(bool isConst);
-    Node letStatement();
+    Node letDeclarationOrBlock();
     Node importDeclaration();
     Node exportDeclaration();
     Node expressionStatement(InvokedPrediction invoked = PredictUninvoked);
     Node variables(ParseNodeKind kind, bool *psimple = nullptr,
                    StaticBlockObject *blockObj = nullptr,
                    VarContext varContext = HoistVars);
     Node expr(InvokedPrediction invoked = PredictUninvoked);
     Node assignExpr(InvokedPrediction invoked = PredictUninvoked);
@@ -610,17 +609,17 @@ class Parser : private JS::AutoGCRooter,
     Node comprehensionTail(GeneratorKind comprehensionKind);
     Node comprehensionIf(GeneratorKind comprehensionKind);
     Node comprehensionFor(GeneratorKind comprehensionKind);
     Node comprehension(GeneratorKind comprehensionKind);
     Node arrayComprehension(uint32_t begin);
     Node generatorComprehension(uint32_t begin);
 
     bool argumentList(Node listNode, bool *isSpread);
-    Node letBlock(LetContext letContext);
+    Node deprecatedLetBlockOrExpression(LetContext letContext);
     Node destructuringExpr(BindData<ParseHandler> *data, TokenKind tt);
     Node destructuringExprWithoutYield(BindData<ParseHandler> *data, TokenKind tt, unsigned msg);
 
     Node identifierName();
 
     bool matchLabel(MutableHandle<PropertyName*> label);
 
     bool allowsForEachIn() {
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -92,18 +92,18 @@ class SyntaxParseHandler
                node == NodeUnparenthesizedCommaExpr ||
                node == NodeUnparenthesizedYieldExpr ||
                node == NodeUnparenthesizedAssignment;
     }
 
 
   public:
     SyntaxParseHandler(ExclusiveContext *cx, LifoAlloc &alloc,
-                       TokenStream &tokenStream, bool foldConstants,
-                       Parser<SyntaxParseHandler> *syntaxParser, LazyScript *lazyOuterFunction)
+                       TokenStream &tokenStream, Parser<SyntaxParseHandler> *syntaxParser,
+                       LazyScript *lazyOuterFunction)
       : lastAtom(nullptr),
         tokenStream(tokenStream)
     {}
 
     static Node null() { return NodeFailure; }
 
     void trace(JSTracer *trc) {}
 
@@ -158,18 +158,18 @@ class SyntaxParseHandler
         return NodeGeneric;
     }
 
     Node newBinary(ParseNodeKind kind, JSOp op = JSOP_NOP) { return NodeGeneric; }
     Node newBinary(ParseNodeKind kind, Node left, JSOp op = JSOP_NOP) { return NodeGeneric; }
     Node newBinary(ParseNodeKind kind, Node left, Node right, JSOp op = JSOP_NOP) {
         return NodeGeneric;
     }
-    Node newBinaryOrAppend(ParseNodeKind kind, Node left, Node right,
-                           ParseContext<SyntaxParseHandler> *pc, JSOp op = JSOP_NOP) {
+    Node appendOrCreateList(ParseNodeKind kind, Node left, Node right,
+                            ParseContext<SyntaxParseHandler> *pc, JSOp op = JSOP_NOP) {
         return NodeGeneric;
     }
 
     Node newTernary(ParseNodeKind kind, Node first, Node second, Node third, JSOp op = JSOP_NOP) {
         return NodeGeneric;
     }
 
     // Expressions
@@ -241,16 +241,24 @@ class SyntaxParseHandler
 
     Node newForHead(ParseNodeKind kind, Node decls, Node lhs, Node rhs, const TokenPos &pos) {
         return NodeGeneric;
     }
 
     Node newLexicalScope(ObjectBox *blockbox) { return NodeGeneric; }
     void setLexicalScopeBody(Node block, Node body) {}
 
+    Node newLetExpression(Node vars, Node block, const TokenPos &pos) {
+        return NodeGeneric;
+    }
+
+    Node newLetBlock(Node vars, Node block, const TokenPos &pos) {
+        return NodeGeneric;
+    }
+
     bool finishInitializerAssignment(Node pn, Node init, JSOp op) { return true; }
 
     void setBeginPosition(Node pn, Node oth) {}
     void setBeginPosition(Node pn, uint32_t begin) {}
 
     void setEndPosition(Node pn, Node oth) {}
     void setEndPosition(Node pn, uint32_t end) {}
 
@@ -262,30 +270,34 @@ class SyntaxParseHandler
 
     Node newList(ParseNodeKind kind, JSOp op = JSOP_NOP) {
         return NodeGeneric;
     }
     Node newList(ParseNodeKind kind, Node kid, JSOp op = JSOP_NOP) {
         return NodeGeneric;
     }
 
+    Node newCatchList() {
+        return newList(PNK_CATCHLIST, JSOP_NOP);
+    }
+
     Node newCommaExpressionList(Node kid) {
         return NodeUnparenthesizedCommaExpr;
     }
 
     void addList(Node list, Node kid) {
         MOZ_ASSERT(list == NodeGeneric || list == NodeUnparenthesizedCommaExpr);
     }
 
     Node newAssignment(ParseNodeKind kind, Node lhs, Node rhs,
                        ParseContext<SyntaxParseHandler> *pc, JSOp op)
     {
         if (kind == PNK_ASSIGN)
             return NodeUnparenthesizedAssignment;
-        return newBinaryOrAppend(kind, lhs, rhs, pc, op);
+        return newBinary(kind, lhs, rhs, op);
     }
 
     bool isUnparenthesizedYieldExpression(Node node) {
         return node == NodeUnparenthesizedYieldExpr;
     }
 
     bool isUnparenthesizedCommaExpression(Node node) {
         return node == NodeUnparenthesizedCommaExpr;
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -463,16 +463,23 @@ class GCSchedulingTunables
  *      cusp, sometimes it will trigger a non-incremental GC moments before
  *      returning to the event loop, where it could have done an incremental
  *      GC. Thus, we recently added an incremental version of the above with a
  *      substantially lower threshold, so that we have a soft limit here. If
  *      IGC can collect faster than the allocator generates garbage, even if
  *      the allocator does not return to the event loop frequently, we should
  *      not have to fall back to a non-incremental GC.
  *
+ *      INCREMENTAL_TOO_SLOW
+ *      --------------------
+ *      Do a full, non-incremental GC if we overflow ALLOC_TRIGGER during an
+ *      incremental GC. When in the middle of an incremental GC, we suppress
+ *      our other triggers, so we need a way to backstop the IGC if the
+ *      mutator allocates faster than the IGC can clean things up.
+ *
  *      TOO_MUCH_MALLOC
  *      ---------------
  *      Performs a GC before size[allocated] - size[retained] gets too large
  *      for non-incremental sweeping to be fast in the case that we have
  *      significantly more malloc allocation than GC allocation. This is meant
  *      to complement MAYBEGC triggers. We track this by counting malloced
  *      bytes; the counter gets reset at every GC since we do not always have a
  *      size at the time we call free. Because of this, the malloc heuristic
@@ -840,16 +847,17 @@ class GCRuntime
     friend class ArenaLists;
     Chunk *pickChunk(const AutoLockGC &lock,
                      AutoMaybeStartBackgroundAllocation &maybeStartBGAlloc);
     ArenaHeader *allocateArena(Chunk *chunk, Zone *zone, AllocKind kind, const AutoLockGC &lock);
     inline void arenaAllocatedDuringGC(JS::Zone *zone, ArenaHeader *arena);
 
     template <AllowGC allowGC>
     static void *refillFreeListFromMainThread(JSContext *cx, AllocKind thingKind);
+    static void *tryRefillFreeListFromMainThread(JSContext *cx, AllocKind thingKind);
     static void *refillFreeListOffMainThread(ExclusiveContext *cx, AllocKind thingKind);
 
     /*
      * Return the list of chunks that can be released outside the GC lock.
      * Must be called either during the GC or with the GC lock taken.
      */
     ChunkPool expireEmptyChunkPool(bool shrinkBuffers, const AutoLockGC &lock);
     void freeEmptyChunks(JSRuntime *rt, const AutoLockGC &lock);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1127303.js
@@ -0,0 +1,9 @@
+
+function test1() {}
+function test() { test1.call(this); }
+var length = 30 * 1024 - 1;
+var obj = new test();
+for(var i = 0 ; i < length ; i++) {
+  obj.next = new (function  (   )  {  }  )  ();
+  obj = obj.next;
+}
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -9162,23 +9162,28 @@ GetTemplateObjectForNative(JSContext *cx
         RootedObject proto(cx, args[0].toObjectOrNull());
         res.set(ObjectCreateImpl(cx, proto, TenuredObject));
         if (!res)
             return false;
         return true;
     }
 
     if (JitSupportsSimd()) {
-        if (native == js::simd_int32x4_add || native == js::simd_int32x4_and) {
+#define ADD_INT32X4_SIMD_OP_NAME_(OP) || native == js::simd_int32x4_##OP
+       if (false
+           ARITH_COMMONX4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_)
+           BITWISE_COMMONX4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_))
+       {
             Rooted<TypeDescr *> descr(cx, &Int32x4::GetTypeDescr(*cx->global()));
             res.set(TypedObject::createZeroed(cx, descr, 0, gc::TenuredHeap));
             if (!res)
                 return false;
             return true;
-        }
+       }
+#undef ADD_INT32X4_SIMD_OP_NAME_
     }
 
     return true;
 }
 
 static bool
 GetTemplateObjectForClassHook(JSContext *cx, JSNative hook, CallArgs &args,
                               MutableHandleObject templateObject)
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -3,30 +3,30 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/IonCaches.h"
 
 #include "mozilla/TemplateLib.h"
 
-#include "jsproxy.h"
 #include "jstypes.h"
 
 #include "builtin/TypedObject.h"
 #include "jit/BaselineIC.h"
 #include "jit/Ion.h"
 #include "jit/JitcodeMap.h"
 #include "jit/JitSpewer.h"
 #include "jit/Linker.h"
 #include "jit/Lowering.h"
 #ifdef JS_ION_PERF
 # include "jit/PerfSpewer.h"
 #endif
 #include "jit/VMFunctions.h"
+#include "js/Proxy.h"
 #include "vm/Shape.h"
 
 #include "jit/JitFrames-inl.h"
 #include "vm/Interpreter-inl.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
 using namespace js::jit;
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -252,20 +252,27 @@ IonBuilder::inlineNativeCall(CallInfo &c
     if (native == testingFunc_assertFloat32)
         return inlineAssertFloat32(callInfo);
 
     // Bound function
     if (native == js::CallOrConstructBoundFunction)
         return inlineBoundFunction(callInfo, target);
 
     // Simd functions
-    if (native == js::simd_int32x4_add)
-        return inlineSimdInt32x4BinaryArith(callInfo, native, MSimdBinaryArith::Add);
-    if (native == js::simd_int32x4_and)
-        return inlineSimdInt32x4BinaryBitwise(callInfo, native, MSimdBinaryBitwise::and_);
+#define INLINE_INT32X4_SIMD_ARITH_(OP)                                                      \
+    if (native == js::simd_int32x4_##OP)                                                    \
+        return inlineSimdInt32x4BinaryArith(callInfo, native, MSimdBinaryArith::Op_##OP);
+    ARITH_COMMONX4_SIMD_OP(INLINE_INT32X4_SIMD_ARITH_)
+#undef INLINE_INT32X4_SIMD_ARITH_
+
+#define INLINE_INT32X4_SIMD_BITWISE_(OP)                                                    \
+    if (native == js::simd_int32x4_##OP)                                                    \
+        return inlineSimdInt32x4BinaryBitwise(callInfo, native, MSimdBinaryBitwise::OP##_);
+    BITWISE_COMMONX4_SIMD_OP(INLINE_INT32X4_SIMD_BITWISE_)
+#undef INLINE_INT32X4_SIMD_BITWISE_
 
     return InliningStatus_NotInlined;
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineNativeGetter(CallInfo &callInfo, JSFunction *target)
 {
     MOZ_ASSERT(target->isNative());
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -10,16 +10,17 @@
  */
 
 #ifndef jit_MIR_h
 #define jit_MIR_h
 
 #include "mozilla/Array.h"
 #include "mozilla/DebugOnly.h"
 
+#include "builtin/SIMD.h"
 #include "jit/AtomicOp.h"
 #include "jit/FixedList.h"
 #include "jit/InlineList.h"
 #include "jit/JitAllocPolicy.h"
 #include "jit/MacroAssembler.h"
 #include "jit/MOpcodes.h"
 #include "jit/TypedObjectPrediction.h"
 #include "jit/TypePolicy.h"
@@ -1953,51 +1954,43 @@ class MSimdBinaryComp
 };
 
 class MSimdBinaryArith
   : public MBinaryInstruction,
     public MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdSameAsReturnedTypePolicy<1> >::Data
 {
   public:
     enum Operation {
-        Add,
-        Sub,
-        Mul,
-        Div,
-        Min,
-        Max,
-        MinNum,
-        MaxNum
+#define OP_LIST_(OP) Op_##OP,
+        ARITH_COMMONX4_SIMD_OP(OP_LIST_)
+        ARITH_FLOAT32X4_SIMD_OP(OP_LIST_)
+#undef OP_LIST_
     };
 
     static const char* OperationName(Operation op) {
         switch (op) {
-          case Add:    return "Add";
-          case Sub:    return "Sub";
-          case Mul:    return "Mul";
-          case Div:    return "Div";
-          case Min:    return "Min";
-          case Max:    return "Max";
-          case MinNum: return "MinNum";
-          case MaxNum: return "MaxNum";
+#define OP_CASE_LIST_(OP) case Op_##OP: return #OP;
+          ARITH_COMMONX4_SIMD_OP(OP_CASE_LIST_)
+          ARITH_FLOAT32X4_SIMD_OP(OP_CASE_LIST_)
+#undef OP_CASE_LIST_
         }
         MOZ_CRASH("unexpected operation");
     }
 
   private:
     Operation operation_;
 
     MSimdBinaryArith(MDefinition *left, MDefinition *right, Operation op, MIRType type)
       : MBinaryInstruction(left, right), operation_(op)
     {
-        MOZ_ASSERT_IF(type == MIRType_Int32x4, op == Add || op == Sub || op == Mul);
+        MOZ_ASSERT_IF(type == MIRType_Int32x4, op == Op_add || op == Op_sub || op == Op_mul);
         MOZ_ASSERT(IsSimdType(type));
         setResultType(type);
         setMovable();
-        if (op == Add || op == Mul || op == Min || op == Max)
+        if (op == Op_add || op == Op_mul || op == Op_min || op == Op_max)
             setCommutative();
     }
 
   public:
     INSTRUCTION_HEADER(SimdBinaryArith)
     static MSimdBinaryArith *New(TempAllocator &alloc, MDefinition *left, MDefinition *right,
                                  Operation op, MIRType t)
     {
--- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp
@@ -2652,23 +2652,23 @@ void
 CodeGeneratorX86Shared::visitSimdBinaryArithIx4(LSimdBinaryArithIx4 *ins)
 {
     FloatRegister lhs = ToFloatRegister(ins->lhs());
     Operand rhs = ToOperand(ins->rhs());
     FloatRegister output = ToFloatRegister(ins->output());
 
     MSimdBinaryArith::Operation op = ins->operation();
     switch (op) {
-      case MSimdBinaryArith::Add:
+      case MSimdBinaryArith::Op_add:
         masm.vpaddd(rhs, lhs, output);
         return;
-      case MSimdBinaryArith::Sub:
+      case MSimdBinaryArith::Op_sub:
         masm.vpsubd(rhs, lhs, output);
         return;
-      case MSimdBinaryArith::Mul: {
+      case MSimdBinaryArith::Op_mul: {
         if (AssemblerX86Shared::HasSSE41()) {
             masm.vpmulld(rhs, lhs, output);
             return;
         }
 
         masm.loadAlignedInt32x4(rhs, ScratchSimdReg);
         masm.vpmuludq(lhs, ScratchSimdReg, ScratchSimdReg);
         // ScratchSimdReg contains (Rx, _, Rz, _) where R is the resulting vector.
@@ -2679,76 +2679,76 @@ CodeGeneratorX86Shared::visitSimdBinaryA
         masm.vpmuludq(temp, lhs, lhs);
         // lhs contains (Ry, _, Rw, _) where R is the resulting vector.
 
         masm.vshufps(MacroAssembler::ComputeShuffleMask(LaneX, LaneZ, LaneX, LaneZ), ScratchSimdReg, lhs, lhs);
         // lhs contains (Ry, Rw, Rx, Rz)
         masm.vshufps(MacroAssembler::ComputeShuffleMask(LaneZ, LaneX, LaneW, LaneY), lhs, lhs, lhs);
         return;
       }
-      case MSimdBinaryArith::Div:
+      case MSimdBinaryArith::Op_div:
         // x86 doesn't have SIMD i32 div.
         break;
-      case MSimdBinaryArith::Max:
+      case MSimdBinaryArith::Op_max:
         // we can do max with a single instruction only if we have SSE4.1
         // using the PMAXSD instruction.
         break;
-      case MSimdBinaryArith::Min:
+      case MSimdBinaryArith::Op_min:
         // we can do max with a single instruction only if we have SSE4.1
         // using the PMINSD instruction.
         break;
-      case MSimdBinaryArith::MinNum:
-      case MSimdBinaryArith::MaxNum:
+      case MSimdBinaryArith::Op_minNum:
+      case MSimdBinaryArith::Op_maxNum:
         break;
     }
     MOZ_CRASH("unexpected SIMD op");
 }
 
 void
 CodeGeneratorX86Shared::visitSimdBinaryArithFx4(LSimdBinaryArithFx4 *ins)
 {
     FloatRegister lhs = ToFloatRegister(ins->lhs());
     Operand rhs = ToOperand(ins->rhs());
     FloatRegister output = ToFloatRegister(ins->output());
 
     MSimdBinaryArith::Operation op = ins->operation();
     switch (op) {
-      case MSimdBinaryArith::Add:
+      case MSimdBinaryArith::Op_add:
         masm.vaddps(rhs, lhs, output);
         return;
-      case MSimdBinaryArith::Sub:
+      case MSimdBinaryArith::Op_sub:
         masm.vsubps(rhs, lhs, output);
         return;
-      case MSimdBinaryArith::Mul:
+      case MSimdBinaryArith::Op_mul:
         masm.vmulps(rhs, lhs, output);
         return;
-      case MSimdBinaryArith::Div:
+      case MSimdBinaryArith::Op_div:
         masm.vdivps(rhs, lhs, output);
         return;
-      case MSimdBinaryArith::Max: {
+      case MSimdBinaryArith::Op_max: {
         FloatRegister lhsCopy = masm.reusedInputFloat32x4(lhs, ScratchSimdReg);
         masm.vcmpunordps(rhs, lhsCopy, ScratchSimdReg);
 
         FloatRegister tmp = ToFloatRegister(ins->temp());
         FloatRegister rhsCopy = masm.reusedInputAlignedFloat32x4(rhs, tmp);
         masm.vmaxps(Operand(lhs), rhsCopy, tmp);
         masm.vmaxps(rhs, lhs, output);
 
         masm.vandps(tmp, output, output);
         masm.vorps(ScratchSimdReg, output, output); // or in the all-ones NaNs
         return;
       }
-      case MSimdBinaryArith::Min: {
+      case MSimdBinaryArith::Op_min: {
         FloatRegister rhsCopy = masm.reusedInputAlignedFloat32x4(rhs, ScratchSimdReg);
         masm.vminps(Operand(lhs), rhsCopy, ScratchSimdReg);
         masm.vminps(rhs, lhs, output);
         masm.vorps(ScratchSimdReg, output, output); // NaN or'd with arbitrary bits is NaN
         return;
       }
-      case MSimdBinaryArith::MinNum: {
+      case MSimdBinaryArith::Op_minNum: {
         FloatRegister tmp = ToFloatRegister(ins->temp());
         masm.loadConstantInt32x4(SimdConstant::SplatX4(int32_t(0x80000000)), tmp);
 
         FloatRegister mask = ScratchSimdReg;
         FloatRegister tmpCopy = masm.reusedInputFloat32x4(tmp, ScratchSimdReg);
         masm.vpcmpeqd(Operand(lhs), tmpCopy, mask);
         masm.vandps(tmp, mask, mask);
 
@@ -2768,17 +2768,17 @@ CodeGeneratorX86Shared::visitSimdBinaryA
             if (lhs != output)
                 masm.moveFloat32x4(lhs, output);
             masm.vandps(Operand(mask), output, output);
             masm.vandnps(Operand(tmp), mask, mask);
             masm.vorps(Operand(mask), output, output);
         }
         return;
       }
-      case MSimdBinaryArith::MaxNum: {
+      case MSimdBinaryArith::Op_maxNum: {
         FloatRegister mask = ScratchSimdReg;
         masm.loadConstantInt32x4(SimdConstant::SplatX4(0), mask);
         masm.vpcmpeqd(Operand(lhs), mask, mask);
 
         FloatRegister tmp = ToFloatRegister(ins->temp());
         masm.loadConstantInt32x4(SimdConstant::SplatX4(int32_t(0x80000000)), tmp);
         masm.vandps(tmp, mask, mask);
 
--- a/js/src/jit/shared/Lowering-x86-shared.cpp
+++ b/js/src/jit/shared/Lowering-x86-shared.cpp
@@ -634,29 +634,29 @@ LIRGeneratorX86Shared::visitSimdBinaryAr
     MDefinition *lhs = ins->lhs();
     MDefinition *rhs = ins->rhs();
 
     if (ins->isCommutative())
         ReorderCommutative(&lhs, &rhs, ins);
 
     if (ins->type() == MIRType_Int32x4) {
         LSimdBinaryArithIx4 *lir = new(alloc()) LSimdBinaryArithIx4();
-        bool needsTemp = ins->operation() == MSimdBinaryArith::Mul && !MacroAssembler::HasSSE41();
+        bool needsTemp = ins->operation() == MSimdBinaryArith::Op_mul && !MacroAssembler::HasSSE41();
         lir->setTemp(0, needsTemp ? temp(LDefinition::INT32X4) : LDefinition::BogusTemp());
         lowerForFPU(lir, ins, lhs, rhs);
         return;
     }
 
     MOZ_ASSERT(ins->type() == MIRType_Float32x4, "unknown simd type on binary arith operation");
 
     LSimdBinaryArithFx4 *lir = new(alloc()) LSimdBinaryArithFx4();
 
-    bool needsTemp = ins->operation() == MSimdBinaryArith::Max ||
-                     ins->operation() == MSimdBinaryArith::MinNum ||
-                     ins->operation() == MSimdBinaryArith::MaxNum;
+    bool needsTemp = ins->operation() == MSimdBinaryArith::Op_max ||
+                     ins->operation() == MSimdBinaryArith::Op_minNum ||
+                     ins->operation() == MSimdBinaryArith::Op_maxNum;
     lir->setTemp(0, needsTemp ? temp(LDefinition::FLOAT32X4) : LDefinition::BogusTemp());
 
     lowerForFPU(lir, ins, lhs, rhs);
 }
 
 void
 LIRGeneratorX86Shared::visitSimdSelect(MSimdSelect *ins)
 {
--- a/js/src/jsapi-tests/testSetPropertyIgnoringNamedGetter.cpp
+++ b/js/src/jsapi-tests/testSetPropertyIgnoringNamedGetter.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  */
 
 #include "jsfriendapi.h"
-#include "jsproxy.h"
+
+#include "js/Proxy.h"
 
 #include "jsapi-tests/tests.h"
 
 using namespace js;
 using namespace JS;
 
 class CustomProxyHandler : public DirectProxyHandler {
   public:
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -30,17 +30,16 @@
 #include "jsgc.h"
 #include "jsiter.h"
 #include "jslock.h"
 #include "jsmath.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "json.h"
 #include "jsprf.h"
-#include "jsproxy.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jstypes.h"
 #include "jsutil.h"
 #include "jswatchpoint.h"
 #include "jsweakmap.h"
 #include "jswrapper.h"
 
@@ -57,16 +56,17 @@
 #endif
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/FullParseHandler.h"  // for JS_BufferIsCompileableUnit
 #include "frontend/Parser.h" // for JS_BufferIsCompileableUnit
 #include "gc/Marking.h"
 #include "jit/JitCommon.h"
 #include "js/CharacterEncoding.h"
 #include "js/Conversions.h"
+#include "js/Proxy.h"
 #include "js/SliceBudget.h"
 #include "js/StructuredClone.h"
 #if ENABLE_INTL_API
 #include "unicode/uclean.h"
 #include "unicode/utypes.h"
 #endif // ENABLE_INTL_API
 #include "vm/DateObject.h"
 #include "vm/Debugger.h"
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -8,22 +8,22 @@
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MemoryReporting.h"
 
 #include "jscntxt.h"
 #include "jsfriendapi.h"
 #include "jsgc.h"
 #include "jsiter.h"
-#include "jsproxy.h"
 #include "jswatchpoint.h"
 #include "jswrapper.h"
 
 #include "gc/Marking.h"
 #include "jit/JitCompartment.h"
+#include "js/Proxy.h"
 #include "js/RootingAPI.h"
 #include "proxy/DeadObjectProxy.h"
 #include "vm/Debugger.h"
 #include "vm/StopIterationObject.h"
 #include "vm/WrapperObject.h"
 
 #include "jsatominlines.h"
 #include "jsfuninlines.h"
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -10,23 +10,23 @@
 
 #include <stdint.h>
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsgc.h"
 #include "jsobj.h"
 #include "jsprf.h"
-#include "jsproxy.h"
 #include "jswatchpoint.h"
 #include "jsweakmap.h"
 #include "jswrapper.h"
 #include "prmjtime.h"
 
 #include "builtin/TestingFunctions.h"
+#include "js/Proxy.h"
 #include "proxy/DeadObjectProxy.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/WrapperObject.h"
 
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/NativeObject-inl.h"
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -16,30 +16,30 @@
 
 #include <string.h>
 
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsobj.h"
-#include "jsproxy.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jstypes.h"
 #include "jswrapper.h"
 
 #include "builtin/Eval.h"
 #include "builtin/Object.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/TokenStream.h"
 #include "gc/Marking.h"
 #include "jit/Ion.h"
 #include "jit/JitFrameIterator.h"
 #include "js/CallNonGenericMethod.h"
+#include "js/Proxy.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/Shape.h"
 #include "vm/StringBuffer.h"
 #include "vm/WrapperObject.h"
 #include "vm/Xdr.h"
 
 #include "jsscriptinlines.h"
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2917,89 +2917,73 @@ void
 ArenaLists::queueForegroundThingsForSweep(FreeOp *fop)
 {
     gcShapeArenasToUpdate = arenaListsToSweep[FINALIZE_SHAPE];
     gcAccessorShapeArenasToUpdate = arenaListsToSweep[FINALIZE_ACCESSOR_SHAPE];
     gcObjectGroupArenasToUpdate = arenaListsToSweep[FINALIZE_OBJECT_GROUP];
     gcScriptArenasToUpdate = arenaListsToSweep[FINALIZE_SCRIPT];
 }
 
-static void *
-RunLastDitchGC(JSContext *cx, JS::Zone *zone, AllocKind thingKind)
-{
-    PrepareZoneForGC(zone);
-
-    JSRuntime *rt = cx->runtime();
-
-    /* The last ditch GC preserves all atoms. */
-    AutoKeepAtoms keepAtoms(cx->perThreadData);
-    rt->gc.gc(GC_NORMAL, JS::gcreason::LAST_DITCH);
-
-    /*
-     * The JSGC_END callback can legitimately allocate new GC
-     * things and populate the free list. If that happens, just
-     * return that list head.
-     */
-    size_t thingSize = Arena::thingSize(thingKind);
-    return zone->arenas.allocateFromFreeList(thingKind, thingSize);
+/* static */ void *
+GCRuntime::tryRefillFreeListFromMainThread(JSContext *cx, AllocKind thingKind)
+{
+    ArenaLists *arenas = cx->arenas();
+    Zone *zone = cx->zone();
+
+    AutoMaybeStartBackgroundAllocation maybeStartBGAlloc;
+
+    void *thing = arenas->allocateFromArena(zone, thingKind, maybeStartBGAlloc);
+    if (MOZ_LIKELY(thing))
+        return thing;
+
+    // Even if allocateFromArena failed due to OOM, a background
+    // finalization or allocation task may be running freeing more memory
+    // or adding more available memory to our free pool; wait for them to
+    // finish, then try to allocate again in case they made more memory
+    // available.
+    cx->runtime()->gc.waitBackgroundSweepOrAllocEnd();
+
+    thing = arenas->allocateFromArena(zone, thingKind, maybeStartBGAlloc);
+    if (thing)
+        return thing;
+
+    return nullptr;
 }
 
 template <AllowGC allowGC>
 /* static */ void *
 GCRuntime::refillFreeListFromMainThread(JSContext *cx, AllocKind thingKind)
 {
     JSRuntime *rt = cx->runtime();
     MOZ_ASSERT(!rt->isHeapBusy(), "allocating while under GC");
     MOZ_ASSERT_IF(allowGC, !rt->currentThreadHasExclusiveAccess());
 
-    ArenaLists *arenas = cx->arenas();
-    Zone *zone = cx->zone();
-
-    // If we have grown past our GC heap threshold while in the middle of an
-    // incremental GC, we're growing faster than we're GCing, so stop the world
-    // and do a full, non-incremental GC right now, if possible.
-    const bool mustCollectNow = allowGC && rt->gc.isIncrementalGCInProgress() &&
-                                zone->usage.gcBytes() > zone->threshold.gcTriggerBytes();
-
-    bool outOfMemory = false;  // Set true if we fail to allocate.
-    bool ranGC = false;  // Once we've GC'd and still cannot allocate, report.
-    do {
-        if (MOZ_UNLIKELY(mustCollectNow || outOfMemory)) {
-            // If we are doing a fallible allocation, percolate up the OOM
-            // instead of reporting it.
-            if (!allowGC) {
-                MOZ_ASSERT(!mustCollectNow);
-                return nullptr;
-            }
-
-            if (void *thing = RunLastDitchGC(cx, zone, thingKind))
-                return thing;
-            ranGC = true;
-        }
-
-        AutoMaybeStartBackgroundAllocation maybeStartBGAlloc;
-        void *thing = arenas->allocateFromArena(zone, thingKind, maybeStartBGAlloc);
-        if (MOZ_LIKELY(thing))
-            return thing;
-
-        // Even if allocateFromArena failed due to OOM, a background
-        // finalization or allocation task may be running freeing more memory
-        // or adding more available memory to our free pool; wait for them to
-        // finish, then try to allocate again in case they made more memory
-        // available.
-        rt->gc.waitBackgroundSweepOrAllocEnd();
-
-        thing = arenas->allocateFromArena(zone, thingKind, maybeStartBGAlloc);
-        if (MOZ_LIKELY(thing))
-            return thing;
-
-        // Retry after a last-ditch GC, unless we've already tried that.
-        outOfMemory = true;
-    } while (!ranGC);
-
+    // Try to allocate; synchronize with background GC threads if necessary.
+    void *thing = tryRefillFreeListFromMainThread(cx, thingKind);
+    if (MOZ_LIKELY(thing))
+        return thing;
+
+    // Perform a last-ditch GC to hopefully free up some memory.
+    {
+        // If we are doing a fallible allocation, percolate up the OOM
+        // instead of reporting it.
+        if (!allowGC)
+            return nullptr;
+
+        JS::PrepareZoneForGC(cx->zone());
+        AutoKeepAtoms keepAtoms(cx->perThreadData);
+        rt->gc.gc(GC_NORMAL, JS::gcreason::LAST_DITCH);
+    }
+
+    // Retry the allocation after the last-ditch GC.
+    thing = tryRefillFreeListFromMainThread(cx, thingKind);
+    if (thing)
+        return thing;
+
+    // We are really just totally out of memory.
     MOZ_ASSERT(allowGC, "A fallible allocation must not report OOM on failure.");
     js_ReportOutOfMemory(cx);
     return nullptr;
 }
 
 /* static */ void *
 GCRuntime::refillFreeListOffMainThread(ExclusiveContext *cx, AllocKind thingKind)
 {
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -432,30 +432,40 @@ CheckAllocatorState(ExclusiveContext *cx
 #endif
 
     // Crash if we perform a GC action when it is not safe.
     if (allowGC && !rt->mainThread.suppressGC)
         JS::AutoAssertOnGC::VerifyIsSafeToGC(rt);
 
     // For testing out of memory conditions
     if (!PossiblyFail()) {
-        js_ReportOutOfMemory(cx->asJSContext());
+        js_ReportOutOfMemory(ncx);
         return false;
     }
 
     if (allowGC) {
 #ifdef JS_GC_ZEAL
         if (rt->gc.needZealousGC())
             rt->gc.runDebugGC();
 #endif
-
         if (rt->hasPendingInterrupt()) {
             // Invoking the interrupt callback can fail and we can't usefully
             // handle that here. Just check in case we need to collect instead.
-            rt->gc.gcIfRequested();
+            rt->gc.gcIfRequested(ncx);
+        }
+
+        // If we have grown past our GC heap threshold while in the middle of
+        // an incremental GC, we're growing faster than we're GCing, so stop
+        // the world and do a full, non-incremental GC right now, if possible.
+        if (rt->gc.isIncrementalGCInProgress() &&
+            ncx->zone()->usage.gcBytes() > ncx->zone()->threshold.gcTriggerBytes())
+        {
+            PrepareZoneForGC(ncx->zone());
+            AutoKeepAtoms keepAtoms(cx->perThreadData);
+            rt->gc.gc(GC_NORMAL, JS::gcreason::INCREMENTAL_TOO_SLOW);
         }
     }
 
     return true;
 }
 
 template <typename T>
 static inline void
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -14,23 +14,23 @@
 #include "mozilla/PodOperations.h"
 
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsgc.h"
 #include "jsobj.h"
 #include "jsopcode.h"
-#include "jsproxy.h"
 #include "jsscript.h"
 #include "jstypes.h"
 #include "jsutil.h"
 
 #include "ds/Sort.h"
 #include "gc/Marking.h"
+#include "js/Proxy.h"
 #include "vm/GeneratorObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
 #include "vm/Shape.h"
 #include "vm/StopIterationObject.h"
 #include "vm/TypedArrayCommon.h"
 
 #include "jsscriptinlines.h"
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -23,32 +23,32 @@
 #include "jscntxt.h"
 #include "jsfriendapi.h"
 #include "jsfun.h"
 #include "jsgc.h"
 #include "jsiter.h"
 #include "jsnum.h"
 #include "jsopcode.h"
 #include "jsprf.h"
-#include "jsproxy.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jstypes.h"
 #include "jsutil.h"
 #include "jswatchpoint.h"
 #include "jswrapper.h"
 
 #include "asmjs/AsmJSModule.h"
 #include "builtin/Eval.h"
 #include "builtin/Object.h"
 #include "builtin/SymbolObject.h"
 #include "frontend/BytecodeCompiler.h"
 #include "gc/Marking.h"
 #include "jit/BaselineJIT.h"
 #include "js/MemoryMetrics.h"
+#include "js/Proxy.h"
 #include "vm/ArgumentsObject.h"
 #include "vm/Interpreter.h"
 #include "vm/ProxyObject.h"
 #include "vm/RegExpStaticsObject.h"
 #include "vm/Shape.h"
 #include "vm/TypedArrayCommon.h"
 
 #include "jsatominlines.h"
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -725,17 +725,17 @@ namespace js {
  * is actually quite good on this topic, though you may have to read it a few
  * times. See ES6 draft rev 29 (6 Dec 2014) 6.1.7.2 and 6.1.7.3.
  *
  * When 'obj' is an ordinary object, these functions have boring standard
  * behavior as specified by ES6 draft rev 29 section 9.1; see the section about
  * internal methods in vm/NativeObject.h.
  *
  * Proxies override the behavior of internal methods. So when 'obj' is a proxy,
- * any one of the functions below could do just about anything. See jsproxy.h.
+ * any one of the functions below could do just about anything. See js/Proxy.h.
  */
 
 /*
  * ES6 [[GetPrototypeOf]]. Get obj's prototype, storing it in protop.
  *
  * If obj is definitely not a proxy, the infallible obj->getProto() can be used
  * instead. See the comment on JSObject::getTaggedProto().
  */
--- a/js/src/jsreflect.cpp
+++ b/js/src/jsreflect.cpp
@@ -2297,21 +2297,21 @@ ASTSerializer::tryStatement(ParseNode *p
 
     RootedValue body(cx);
     if (!statement(pn->pn_kid1, &body))
         return false;
 
     NodeVector guarded(cx);
     RootedValue unguarded(cx, NullValue());
 
-    if (pn->pn_kid2) {
-        if (!guarded.reserve(pn->pn_kid2->pn_count))
+    if (ParseNode *catchList = pn->pn_kid2) {
+        if (!guarded.reserve(catchList->pn_count))
             return false;
 
-        for (ParseNode *next = pn->pn_kid2->pn_head; next; next = next->pn_next) {
+        for (ParseNode *next = catchList->pn_head; next; next = next->pn_next) {
             RootedValue clause(cx);
             bool isGuarded;
             if (!catchClause(next->pn_expr, &isGuarded, &clause))
                 return false;
             if (isGuarded)
                 guarded.infallibleAppend(clause);
             else
                 unguarded = clause;
@@ -2362,21 +2362,22 @@ ASTSerializer::statement(ParseNode *pn, 
 {
     JS_CHECK_RECURSION(cx, return false);
     switch (pn->getKind()) {
       case PNK_FUNCTION:
       case PNK_VAR:
       case PNK_GLOBALCONST:
         return declaration(pn, dst);
 
+      case PNK_LETBLOCK:
+        return let(pn, false, dst);
+
       case PNK_LET:
       case PNK_CONST:
-        return pn->isArity(PN_BINARY)
-               ? let(pn, false, dst)
-               : declaration(pn, dst);
+        return declaration(pn, dst);
 
       case PNK_IMPORT:
         return importDeclaration(pn, dst);
 
       case PNK_EXPORT:
       case PNK_EXPORT_FROM:
         return exportDeclaration(pn, dst);
 
@@ -2750,28 +2751,17 @@ ASTSerializer::expression(ParseNode *pn,
         return expression(pn->pn_kid1, &test) &&
                expression(pn->pn_kid2, &cons) &&
                expression(pn->pn_kid3, &alt) &&
                builder.conditionalExpression(test, cons, alt, &pn->pn_pos, dst);
       }
 
       case PNK_OR:
       case PNK_AND:
-      {
-        if (pn->isArity(PN_BINARY)) {
-            MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
-            MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
-
-            RootedValue left(cx), right(cx);
-            return expression(pn->pn_left, &left) &&
-                   expression(pn->pn_right, &right) &&
-                   builder.logicalExpression(pn->isKind(PNK_OR), left, right, &pn->pn_pos, dst);
-        }
         return leftAssociate(pn, dst);
-      }
 
       case PNK_PREINCREMENT:
       case PNK_PREDECREMENT:
       {
         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_kid->pn_pos));
 
         bool inc = pn->isKind(PNK_PREINCREMENT);
         RootedValue expr(cx);
@@ -2831,28 +2821,16 @@ ASTSerializer::expression(ParseNode *pn,
       case PNK_STAR:
       case PNK_DIV:
       case PNK_MOD:
       case PNK_BITOR:
       case PNK_BITXOR:
       case PNK_BITAND:
       case PNK_IN:
       case PNK_INSTANCEOF:
-        if (pn->isArity(PN_BINARY)) {
-            MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos));
-            MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos));
-
-            BinaryOperator op = binop(pn->getKind(), pn->getOp());
-            LOCAL_ASSERT(op > BINOP_ERR && op < BINOP_LIMIT);
-
-            RootedValue left(cx), right(cx);
-            return expression(pn->pn_left, &left) &&
-                   expression(pn->pn_right, &right) &&
-                   builder.binaryExpression(op, left, right, &pn->pn_pos, dst);
-        }
         return leftAssociate(pn, dst);
 
       case PNK_DELETE:
       case PNK_TYPEOF:
       case PNK_VOID:
       case PNK_NOT:
       case PNK_BITNOT:
       case PNK_POS:
@@ -3062,17 +3040,17 @@ ASTSerializer::expression(ParseNode *pn,
 
       case PNK_ARRAYCOMP:
         MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_head->pn_pos));
 
         /* NB: it's no longer the case that pn_count could be 2. */
         LOCAL_ASSERT(pn->pn_count == 1);
         return comprehension(pn->pn_head, dst);
 
-      case PNK_LET:
+      case PNK_LETEXPR:
         return let(pn, true, dst);
 
       default:
         LOCAL_NOT_REACHED("unexpected expression type");
     }
 }
 
 bool
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jswrapper_h
 #define jswrapper_h
 
 #include "mozilla/Attributes.h"
 
-#include "jsproxy.h"
+#include "js/Proxy.h"
 
 namespace js {
 
 class DummyFrameGuard;
 
 /*
  * Helper for Wrapper::New default options.
  *
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -46,17 +46,16 @@ EXPORTS += [
     'jsalloc.h',
     'jsapi.h',
     'jsbytecode.h',
     'jsclist.h',
     'jscpucfg.h',
     'jsfriendapi.h',
     'jsprf.h',
     'jsprototypes.h',
-    'jsproxy.h',
     'jspubtd.h',
     'jstypes.h',
     'jsversion.h',
     'jswrapper.h',
     'perf/jsperf.h',
 ]
 
 # If you add a header here, add it to js/src/jsapi-tests/testIntTypesABI.cpp so
@@ -74,16 +73,17 @@ EXPORTS.js += [
     '../public/HashTable.h',
     '../public/HeapAPI.h',
     '../public/Id.h',
     '../public/LegacyIntTypes.h',
     '../public/MemoryMetrics.h',
     '../public/Principals.h',
     '../public/ProfilingFrameIterator.h',
     '../public/ProfilingStack.h',
+    '../public/Proxy.h',
     '../public/RequiredDefines.h',
     '../public/RootingAPI.h',
     '../public/SliceBudget.h',
     '../public/StructuredClone.h',
     '../public/TracingAPI.h',
     '../public/TrackedOptimizationInfo.h',
     '../public/TypeDecls.h',
     '../public/UbiNode.h',
--- a/js/src/proxy/BaseProxyHandler.cpp
+++ b/js/src/proxy/BaseProxyHandler.cpp
@@ -1,16 +1,15 @@
 /* -*- 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 "jsproxy.h"
-
+#include "js/Proxy.h"
 #include "vm/ProxyObject.h"
 
 #include "jscntxtinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 
 bool
--- a/js/src/proxy/DeadObjectProxy.h
+++ b/js/src/proxy/DeadObjectProxy.h
@@ -2,17 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef proxy_DeadObjectProxy_h
 #define proxy_DeadObjectProxy_h
 
-#include "jsproxy.h"
+#include "js/Proxy.h"
 
 namespace js {
 
 class DeadObjectProxy : public BaseProxyHandler
 {
   public:
     explicit MOZ_CONSTEXPR DeadObjectProxy()
       : BaseProxyHandler(&family)
--- a/js/src/proxy/DirectProxyHandler.cpp
+++ b/js/src/proxy/DirectProxyHandler.cpp
@@ -1,17 +1,17 @@
 /* -*- 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 "jsproxy.h"
 #include "jswrapper.h" // UncheckedUnwrap
 
+#include "js/Proxy.h"
 #include "vm/ProxyObject.h"
 
 #include "jsobjinlines.h"
 
 using namespace js;
 
 bool
 DirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -1,21 +1,22 @@
 /* -*- 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 "js/Proxy.h"
+
 #include <string.h>
 
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsfun.h"
 #include "jsgc.h"
-#include "jsproxy.h"
 #include "jswrapper.h"
 
 #include "gc/Marking.h"
 #include "proxy/DeadObjectProxy.h"
 #include "proxy/ScriptedDirectProxyHandler.h"
 #include "proxy/ScriptedIndirectProxyHandler.h"
 #include "vm/WrapperObject.h"
 
@@ -109,53 +110,30 @@ Proxy::getPropertyDescriptor(JSContext *
     if (!handler->getOwnPropertyDescriptor(cx, proxy, id, desc))
         return false;
     if (desc.object())
         return true;
     INVOKE_ON_PROTOTYPE(cx, handler, proxy, GetPropertyDescriptor(cx, proto, id, desc));
 }
 
 bool
-Proxy::getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id, MutableHandleValue vp)
-{
-    JS_CHECK_RECURSION(cx, return false);
-
-    Rooted<PropertyDescriptor> desc(cx);
-    if (!Proxy::getPropertyDescriptor(cx, proxy, id, &desc))
-        return false;
-    return NewPropertyDescriptorObject(cx, desc, vp);
-}
-
-bool
 Proxy::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                 MutableHandle<PropertyDescriptor> desc)
 {
     JS_CHECK_RECURSION(cx, return false);
 
     const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
     desc.object().set(nullptr); // default result if we refuse to perform this action
     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::GET_PROPERTY_DESCRIPTOR, true);
     if (!policy.allowed())
         return policy.returnValue();
     return handler->getOwnPropertyDescriptor(cx, proxy, id, desc);
 }
 
 bool
-Proxy::getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
-                                MutableHandleValue vp)
-{
-    JS_CHECK_RECURSION(cx, return false);
-
-    Rooted<PropertyDescriptor> desc(cx);
-    if (!Proxy::getOwnPropertyDescriptor(cx, proxy, id, &desc))
-        return false;
-    return NewPropertyDescriptorObject(cx, desc, vp);
-}
-
-bool
 Proxy::defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                       MutableHandle<PropertyDescriptor> desc)
 {
     JS_CHECK_RECURSION(cx, return false);
     const BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
     AutoEnterPolicy policy(cx, handler, proxy, id, BaseProxyHandler::SET, true);
     if (!policy.allowed())
         return policy.returnValue();
--- a/js/src/proxy/Proxy.h
+++ b/js/src/proxy/Proxy.h
@@ -23,18 +23,16 @@ class RegExpGuard;
  * 945826 comment 0.
  */
 class Proxy
 {
   public:
     /* Standard internal methods. */
     static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                          MutableHandle<JSPropertyDescriptor> desc);
-    static bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
-                                         MutableHandleValue vp);
     static bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                                MutableHandle<JSPropertyDescriptor> desc);
     static bool ownPropertyKeys(JSContext *cx, HandleObject proxy, AutoIdVector &props);
     static bool delete_(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
     static bool enumerate(JSContext *cx, HandleObject proxy, MutableHandleObject objp);
     static bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible);
     static bool preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded);
     static bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop);
@@ -46,18 +44,16 @@ class Proxy
     static bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id,
                     bool strict, MutableHandleValue vp);
     static bool call(JSContext *cx, HandleObject proxy, const CallArgs &args);
     static bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args);
 
     /* SpiderMonkey extensions. */
     static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                       MutableHandle<JSPropertyDescriptor> desc);
-    static bool getPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
-                                      MutableHandleValue vp);
     static bool hasOwn(JSContext *cx, HandleObject proxy, HandleId id, bool *bp);
     static bool getOwnEnumerablePropertyKeys(JSContext *cx, HandleObject proxy,
                                              AutoIdVector &props);
     static bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args);
     static bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp);
     static bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx);
     static const char *className(JSContext *cx, HandleObject proxy);
     static JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent);
--- a/js/src/proxy/ScriptedDirectProxyHandler.h
+++ b/js/src/proxy/ScriptedDirectProxyHandler.h
@@ -2,17 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef proxy_ScriptedDirectProxyHandler_h
 #define proxy_ScriptedDirectProxyHandler_h
 
-#include "jsproxy.h"
+#include "js/Proxy.h"
 
 namespace js {
 
 /* Derived class for all scripted direct proxy handlers. */
 class ScriptedDirectProxyHandler : public DirectProxyHandler {
   public:
     MOZ_CONSTEXPR ScriptedDirectProxyHandler()
       : DirectProxyHandler(&family)
--- a/js/src/proxy/ScriptedIndirectProxyHandler.h
+++ b/js/src/proxy/ScriptedIndirectProxyHandler.h
@@ -2,17 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef proxy_ScriptedIndirectProxyHandler_h
 #define proxy_ScriptedIndirectProxyHandler_h
 
-#include "jsproxy.h"
+#include "js/Proxy.h"
 
 namespace js {
 
 /* Derived class for all scripted indirect proxy handlers. */
 class ScriptedIndirectProxyHandler : public BaseProxyHandler
 {
   public:
     MOZ_CONSTEXPR ScriptedIndirectProxyHandler()
--- a/js/src/vm/ObjectGroup.h
+++ b/js/src/vm/ObjectGroup.h
@@ -231,21 +231,25 @@ class ObjectGroup : public gc::TenuredCe
         if (addendumKind() == Addendum_UnboxedLayout)
             return reinterpret_cast<UnboxedLayout *>(addendum_);
         return nullptr;
     }
 
     TypeNewScript *anyNewScript();
     void detachNewScript(bool writeBarrier);
 
+    ObjectGroupFlags flagsDontCheckGeneration() {
+        return flags_;
+    }
+
   public:
 
     ObjectGroupFlags flags() {
         maybeSweep(nullptr);