Merge m-c to cedar
authorPhil Ringnalda <philringnalda@gmail.com>
Mon, 19 Jan 2015 09:05:33 -0800
changeset 326727 7b186fd1105cab6d49e551329fbbea6dc765ee87
parent 326726 fd859bff31fd66d399b4360e56a9e79575cac351 (current diff)
parent 237740 dcb4c5573aef75c9d07e5aff3610f08b9f74ac15 (diff)
child 326728 852f84996843ac60478b5ac8bf760ff90ab0f9c2
push id10169
push userdminor@mozilla.com
push dateThu, 28 Jan 2016 13:10:48 +0000
milestone38.0a1
Merge m-c to cedar
js/src/tests/ecma_7/SIMD/float32x4-minmax.js
js/src/tests/ecma_7/SIMD/float32x4clamp.js
netwerk/test/TestPageLoad.cpp
netwerk/test/TestPerf.cpp
netwerk/test/TestSyncHTTP.cpp
netwerk/test/TestThreadedIO.cpp
xpcom/tests/CvtURL.cpp
--- 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="e0c735ec89df011ea7dd435087a9045ecff9ff9e">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="0f65b258bceddd9d479b3c027d9bd234c1e99aaf"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e6a5e65ec98f3dee36c55809639c4800e52fbc19"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c28e606f7656de3a3a8808b68eb048528397d5a7"/>
--- 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="df362ace56338da8173d30d3e09e08c42c1accfa">
     <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="0f65b258bceddd9d479b3c027d9bd234c1e99aaf"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e6a5e65ec98f3dee36c55809639c4800e52fbc19"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="6fa7a4936414ceb4055fd27f7a30e76790f834fb"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c28e606f7656de3a3a8808b68eb048528397d5a7"/>
   <!-- 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="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="0f65b258bceddd9d479b3c027d9bd234c1e99aaf"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e6a5e65ec98f3dee36c55809639c4800e52fbc19"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c28e606f7656de3a3a8808b68eb048528397d5a7"/>
   <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="e0c735ec89df011ea7dd435087a9045ecff9ff9e">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="0f65b258bceddd9d479b3c027d9bd234c1e99aaf"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e6a5e65ec98f3dee36c55809639c4800e52fbc19"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c28e606f7656de3a3a8808b68eb048528397d5a7"/>
--- 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="df362ace56338da8173d30d3e09e08c42c1accfa">
     <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="0f65b258bceddd9d479b3c027d9bd234c1e99aaf"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e6a5e65ec98f3dee36c55809639c4800e52fbc19"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="6fa7a4936414ceb4055fd27f7a30e76790f834fb"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c28e606f7656de3a3a8808b68eb048528397d5a7"/>
   <!-- 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="e0c735ec89df011ea7dd435087a9045ecff9ff9e">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="0f65b258bceddd9d479b3c027d9bd234c1e99aaf"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e6a5e65ec98f3dee36c55809639c4800e52fbc19"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c28e606f7656de3a3a8808b68eb048528397d5a7"/>
@@ -140,26 +140,26 @@
   <project name="device-flame" path="device/t2m/flame" remote="b2g" revision="a814b2e2dfdda7140cb3a357617dc4fbb1435e76"/>
   <project name="codeaurora_kernel_msm" path="kernel" remote="b2g" revision="0865bc4134b67220df4058625fba29305d6b10c3"/>
   <project name="kernel_lk" path="bootable/bootloader/lk" remote="b2g" revision="fda40423ffa573dc6cafd3780515010cb2a086be"/>
   <remove-project name="platform/bootable/recovery"/>
   <project name="platform_bootable_recovery" path="bootable/recovery" remote="b2g" revision="26e78a979f3090dc196219e268467620b6c40ec5"/>
   <project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="30b96dfca99cb384bf520a16b81f3aba56f09907"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="5b71e40213f650459e95d35b6f14af7e88d8ab62"/>
   <project name="platform_external_libnfc-nci" path="external/libnfc-nci" remote="t2m" revision="4186bdecb4dae911b39a8202252cc2310d91b0be"/>
-  <project name="platform/frameworks/av" path="frameworks/av" revision="c00de33ebfad57ae79ad5f93c2819ee2c40c8bcd"/>
-  <project name="platform/frameworks/base" path="frameworks/base" revision="6b58ab45e3e56c1fc20708cc39fa2264c52558df"/>
+  <project name="platform/frameworks/av" path="frameworks/av" revision="65f5144987afff35a932262c0c5fad6ecce0c04a"/>
+  <project name="platform/frameworks/base" path="frameworks/base" revision="da8e6bc53c8bc669da0bb627904d08aa293f2497"/>
   <project name="platform/frameworks/native" path="frameworks/native" revision="a46a9f1ac0ed5662d614c277cbb14eb3f332f365"/>
   <project name="platform/hardware/libhardware" path="hardware/libhardware" revision="7196881a0e9dd7bfbbcf0af64c8064e70f0fa094"/>
   <project name="platform/hardware/qcom/audio" path="hardware/qcom/audio" revision="8d7676dfb68ee0cd069affedd5d1e97316a184ba"/>
   <project name="platform/hardware/qcom/camera" path="hardware/qcom/camera" revision="2a1ded216a91bf62a72b1640cf01ab4998f37028"/>
-  <project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="f8bec8a61dc0f2581fa72a31d4144084b47ef7cf"/>
+  <project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="a74adcf8d88320d936daa8d20ce88ca0107fb916"/>
   <project name="platform/hardware/qcom/gps" path="hardware/qcom/gps" revision="9883ea57b0668d8f60dba025d4522dfa69a1fbfa"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="a558dc844bf5144fc38603fd8f4df8d9557052a5"/>
   <project name="platform/hardware/qcom/wlan" path="hardware/qcom/wlan" revision="57ee1320ed7b4a1a1274d8f3f6c177cd6b9becb2"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="12b1977cc704b35f2e9db2bb423fa405348bc2f3"/>
   <project name="platform/system/bluetooth" path="system/bluetooth" revision="985bf15264d865fe7b9c5b45f61c451cbaafa43d"/>
   <project name="platform/system/core" path="system/core" revision="350eac5403124dacb2a5fd9e28ac290a59fc3b8e"/>
   <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="0c557e136bb615923aad0f5c69da4e869523efc5"/>
   <project name="platform/system/qcom" path="system/qcom" revision="63e3f6f176caad587d42bba4c16b66d953fb23c2"/>
   <project name="platform/vendor/qcom-opensource/wlan/prima" path="vendor/qcom/opensource/wlan/prima" revision="d8952a42771045fca73ec600e2b42a4c7129d723"/>
-  <project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="018b44e52b2bac5d3631d559550e88a4b68c6e67"/>
+  <project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="4c187c1f3a0dffd8e51a961735474ea703535b99"/>
 </manifest>
--- 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="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="0f65b258bceddd9d479b3c027d9bd234c1e99aaf"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e6a5e65ec98f3dee36c55809639c4800e52fbc19"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c28e606f7656de3a3a8808b68eb048528397d5a7"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "9afe83aea4c5b5ca1161f3901bfb6118aa5c5600", 
+    "revision": "aad58c5dd3667a2a4a0131388d39a6e5d872e7d0", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <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="0f65b258bceddd9d479b3c027d9bd234c1e99aaf"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e6a5e65ec98f3dee36c55809639c4800e52fbc19"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c28e606f7656de3a3a8808b68eb048528397d5a7"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="1a2a32eda22ef2cd18f57f423a5e7b22a105a6f8"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="746bc48f34f5060f90801925dcdd964030c1ab6d"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <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="0f65b258bceddd9d479b3c027d9bd234c1e99aaf"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e6a5e65ec98f3dee36c55809639c4800e52fbc19"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="1a2a32eda22ef2cd18f57f423a5e7b22a105a6f8"/>
--- 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="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="0f65b258bceddd9d479b3c027d9bd234c1e99aaf"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e6a5e65ec98f3dee36c55809639c4800e52fbc19"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c28e606f7656de3a3a8808b68eb048528397d5a7"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <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="0f65b258bceddd9d479b3c027d9bd234c1e99aaf"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e6a5e65ec98f3dee36c55809639c4800e52fbc19"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="049c281ad212bf528b2af8fc246b0dd0c9f97415"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c28e606f7656de3a3a8808b68eb048528397d5a7"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -214,17 +214,17 @@ skip-if = e10s # Bug 1093677 - automated
 [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]
 run-if = toolkit == "cocoa"
 [browser_bug567306.js]
-skip-if = e10s
+skip-if = e10s # Bug XXX - Needs some massaging to run in e10s
 [browser_bug575561.js]
 [browser_bug575830.js]
 skip-if = e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s
 [browser_bug577121.js]
 [browser_bug578534.js]
 [browser_bug579872.js]
 [browser_bug580638.js]
 [browser_bug580956.js]
--- a/browser/base/content/test/general/browser_bug567306.js
+++ b/browser/base/content/test/general/browser_bug567306.js
@@ -8,18 +8,18 @@ let Clipboard = Cc["@mozilla.org/widget/
 let HasFindClipboard = Clipboard.supportsFindClipboard();
 
 function test() {
   waitForExplicitFinish();
 
   whenNewWindowLoaded(undefined, function (win) {
     whenDelayedStartupFinished(win, function () {
       let selectedBrowser = win.gBrowser.selectedBrowser;
-      selectedBrowser.addEventListener("pageshow", function() {
-        selectedBrowser.removeEventListener("pageshow", arguments.callee, true);
+      selectedBrowser.addEventListener("pageshow", function pageshowListener() {
+        selectedBrowser.removeEventListener("pageshow", pageshowListener, true);
         ok(true, "pageshow listener called: " + win.content.location);
         waitForFocus(function () {
           onFocus(win);
         }, selectedBrowser.contentWindow);
       }, true);
       selectedBrowser.loadURI("data:text/html,<h1 id='h1'>Select Me</h1>");
     });
   });
@@ -34,17 +34,21 @@ function selectText(win) {
   selection.removeAllRanges();
   selection.addRange(range);
 }
 
 function onFocus(win) {
   ok(!win.gFindBarInitialized, "find bar is not yet initialized");
   let findBar = win.gFindBar;
   selectText(win.content);
-  findBar.onFindCommand();
+
+  findBar.onFindCommand().then(onInitialized.bind(null, findBar, win));
+}
+
+function onInitialized(findBar, win) {
   // When the OS supports the Find Clipboard (OSX), the find field value is
   // persisted across Fx sessions, thus not useful to test.
   if (!HasFindClipboard)
     is(findBar._findField.value, "Select Me", "Findbar is initialized with selection");
   findBar.close();
   win.close();
   finish();
 }
--- a/browser/base/content/test/general/browser_zbug569342.js
+++ b/browser/base/content/test/general/browser_zbug569342.js
@@ -1,19 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-///////////////////
-//
-// Whitelisting this test.
-// As part of bug 1077403, the leaking uncaught rejection should be fixed.
-//
-thisTestLeaksUncaughtRejectionsAndShouldBeFixed("");
-
 var gTab = null;
 
 function load(url, cb) {
   gTab = gBrowser.addTab(url);
   gBrowser.addEventListener("load", function (event) {
     if (event.target.location != url)
       return;
 
--- a/browser/base/content/test/general/mochitest.ini
+++ b/browser/base/content/test/general/mochitest.ini
@@ -24,16 +24,16 @@ support-files =
   offlineEvent.cacheManifest^headers^
   offlineEvent.html
   subtst_contextmenu.html
   video.ogg
 
 [test_bug364677.html]
 [test_bug395533.html]
 [test_contextmenu.html]
-skip-if = toolkit == "gtk2" || toolkit == "gtk3" # disabled on Linux due to bug 513558
+skip-if = toolkit == "gtk2" || toolkit == "gtk3" || (os == 'mac' && os_version != '10.6') # disabled on Linux due to bug 513558, on Mac after 10.6 due to bug 792304
 [test_contextmenu_input.html]
 skip-if = toolkit == "gtk2" || toolkit == "gtk3" # disabled on Linux due to bug 513558
 [test_feed_discovery.html]
 [test_offlineNotification.html]
 skip-if = buildapp == 'mulet' # Bug 1066070 - I don't think either popup notifications nor addon install stuff works?
 [test_offline_gzip.html]
 skip-if = buildapp == 'mulet' # Bug 1066070 - I don't think either popup notifications nor addon install stuff works?
--- a/browser/base/content/test/general/test_contextmenu.html
+++ b/browser/base/content/test/general/test_contextmenu.html
@@ -847,26 +847,20 @@ function waitForEvents(event)
     loaded = true;
   if (painted && loaded) {
     subwindow.removeEventListener("MozAfterPaint", waitForEvents, false);
     subwindow.onload = null;
     startTest();
   }
 }
 
-const isOSXMtnLion = navigator.userAgent.indexOf("Mac OS X 10.8") != -1;
+SpecialPowers.setBoolPref("plugins.click_to_play", true);
+setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
 
-if (isOSXMtnLion) {
-  todo(false, "Mountain Lion doesn't like this test (bug 792304)");
-} else {
-  SpecialPowers.setBoolPref("plugins.click_to_play", true);
-  setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY);
+var subwindow = window.open("./subtst_contextmenu.html", "contextmenu-subtext", "width=600,height=800");
+subwindow.addEventListener("MozAfterPaint", waitForEvents, false);
+subwindow.onload = waitForEvents;
 
-  var subwindow = window.open("./subtst_contextmenu.html", "contextmenu-subtext", "width=600,height=800");
-  subwindow.addEventListener("MozAfterPaint", waitForEvents, false);
-  subwindow.onload = waitForEvents;
-
-  SimpleTest.waitForExplicitFinish();
-}
+SimpleTest.waitForExplicitFinish();
 </script>
 </pre>
 </body>
 </html>
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -14,52 +14,62 @@ function PlacesViewBase(aPlace, aOptions
   this.options = aOptions;
   this._controller = new PlacesController(this);
   this._viewElt.controllers.appendController(this._controller);
 }
 
 PlacesViewBase.prototype = {
   // The xul element that holds the entire view.
   _viewElt: null,
-  get viewElt() this._viewElt,
+  get viewElt() {
+    return this._viewElt;
+  },
 
-  get associatedElement() this._viewElt,
+  get associatedElement() {
+    return this._viewElt;
+  },
 
-  get controllers() this._viewElt.controllers,
+  get controllers() {
+    return this._viewElt.controllers;
+  },
 
   // The xul element that represents the root container.
   _rootElt: null,
 
   // Set to true for views that are represented by native widgets (i.e.
   // the native mac menu).
   _nativeView: false,
 
   QueryInterface: XPCOMUtils.generateQI(
     [Components.interfaces.nsINavHistoryResultObserver,
      Components.interfaces.nsISupportsWeakReference]),
 
   _place: "",
-  get place() this._place,
+  get place() {
+    return this._place;
+  },
   set place(val) {
     this._place = val;
 
     let history = PlacesUtils.history;
     let queries = { }, options = { };
     history.queryStringToQueries(val, queries, { }, options);
     if (!queries.value.length)
       queries.value = [history.getNewQuery()];
 
     let result = history.executeQueries(queries.value, queries.value.length,
                                         options.value);
     result.addObserver(this, false);
     return val;
   },
 
   _result: null,
-  get result() this._result,
+  get result() {
+    return this._result;
+  },
   set result(val) {
     if (this._result == val)
       return val;
 
     if (this._result) {
       this._result.removeObserver(this);
       this._resultNode.containerOpen = false;
     }
@@ -81,17 +91,19 @@ PlacesViewBase.prototype = {
       this._resultNode = null;
       delete this._domNodes;
     }
 
     return val;
   },
 
   _options: null,
-  get options() this._options,
+  get options() {
+    return this._options;
+  },
   set options(val) {
     if (!val)
       val = {};
 
     if (!("extraClasses" in val))
       val.extraClasses = {};
     this._options = val;
 
@@ -110,19 +122,23 @@ PlacesViewBase.prototype = {
     let node = this._domNodes.get(aPlacesNode, null);
     if (!node) {
       throw new Error("No DOM node set for aPlacesNode.\nnode.type: " +
                       aPlacesNode.type + ". node.parent: " + aPlacesNode);
     }
     return node;
   },
 
-  get controller() this._controller,
+  get controller() {
+    return this._controller;
+  },
 
-  get selType() "single",
+  get selType() {
+    return "single";
+  },
   selectItems: function() { },
   selectAll: function() { },
 
   get selectedNode() {
     if (this._contextMenuShown) {
       let anchor = this._contextMenuShown.triggerNode;
       if (!anchor)
         return null;
@@ -131,17 +147,19 @@ PlacesViewBase.prototype = {
         return this._rootElt == anchor ? null : anchor._placesNode;
 
       anchor = anchor.parentNode;
       return this._rootElt == anchor ? null : (anchor._placesNode || null);
     }
     return null;
   },
 
-  get hasSelection() this.selectedNode != null,
+  get hasSelection() {
+    return this.selectedNode != null;
+  },
 
   get selectedNodes() {
     let selectedNode = this.selectedNode;
     return selectedNode ? [selectedNode] : [];
   },
 
   get removableSelectionRanges() {
     // On static content the current selectedNode would be the selection's
@@ -149,17 +167,19 @@ PlacesViewBase.prototype = {
     // selection is not explicit.
     if (document.popupNode &&
         (document.popupNode == "menupopup" || !document.popupNode._placesNode))
       return [];
 
     return [this.selectedNodes];
   },
 
-  get draggableSelection() [this._draggedElt],
+  get draggableSelection() {
+    return [this._draggedElt];
+  },
 
   get insertionPoint() {
     // There is no insertion point for history queries, so bail out now and
     // save a lot of work when updating commands.
     let resultNode = this._resultNode;
     if (PlacesUtils.nodeIsQuery(resultNode) &&
         PlacesUtils.asQuery(resultNode).queryOptions.queryType ==
           Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY)
@@ -747,17 +767,19 @@ PlacesViewBase.prototype = {
     if ("_isRTL" in this)
       return this._isRTL;
 
     return this._isRTL = document.defaultView
                                  .getComputedStyle(this.viewElt, "")
                                  .direction == "rtl";
   },
 
-  get ownerWindow() window,
+  get ownerWindow() {
+    return window;
+  },
 
   /**
    * Adds an "Open All in Tabs" menuitem to the bottom of the popup.
    * @param aPopup
    *        a Places popup.
    */
   _mayAddCommandsItems: function PVB__mayAddCommandsItems(aPopup) {
     // The command items are never added to the root popup.
--- a/browser/components/preferences/jar.mn
+++ b/browser/components/preferences/jar.mn
@@ -23,17 +23,17 @@ browser.jar:
 *   content/browser/preferences/fonts.xul
     content/browser/preferences/fonts.js
     content/browser/preferences/handlers.xml
     content/browser/preferences/handlers.css
 *   content/browser/preferences/languages.xul
     content/browser/preferences/languages.js
 *   content/browser/preferences/main.xul
 *   content/browser/preferences/main.js
-*   content/browser/preferences/permissions.xul
+    content/browser/preferences/permissions.xul
 *   content/browser/preferences/permissions.js
 *   content/browser/preferences/preferences.xul
     content/browser/preferences/preferences.js
     content/browser/preferences/privacy.xul
 *   content/browser/preferences/privacy.js
     content/browser/preferences/sanitize.xul
     content/browser/preferences/sanitize.js
     content/browser/preferences/security.xul
@@ -43,10 +43,10 @@ browser.jar:
 #ifdef MOZ_SERVICES_SYNC
     content/browser/preferences/sync.xul
     content/browser/preferences/sync.js
 #endif
     content/browser/preferences/search.xul
     content/browser/preferences/search.js
 *   content/browser/preferences/tabs.xul
 *   content/browser/preferences/tabs.js
-*   content/browser/preferences/translation.xul
+    content/browser/preferences/translation.xul
     content/browser/preferences/translation.js
--- a/browser/components/preferences/permissions.xul
+++ b/browser/components/preferences/permissions.xul
@@ -1,14 +1,13 @@
 <?xml version="1.0"?>
 
-# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+<!-- 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/. -->
 
 <?xml-stylesheet href="chrome://global/skin/" type="text/css"?> 
 <?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?> 
 
 <!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/permissions.dtd" >
 
 <window id="PermissionsDialog" class="windowDialog"
         windowtype="Browser:Permissions"
@@ -24,17 +23,17 @@
   <script src="chrome://browser/content/preferences/permissions.js"/>
 
   <stringbundle id="bundlePreferences"
                 src="chrome://browser/locale/preferences/preferences.properties"/>
 
   <keyset>
     <key key="&windowClose.key;" modifiers="accel" oncommand="window.close();"/>
   </keyset>
-  
+
   <vbox class="contentPane" flex="1">
     <description id="permissionsText" control="url"/>
     <separator class="thin"/>
     <label id="urlLabel" control="url" value="&address.label;" accesskey="&address.accesskey;"/>
     <hbox align="start">
       <textbox id="url" flex="1" 
                oninput="gPermissionManager.onHostInput(event.target);"
                onkeypress="gPermissionManager.onHostKeyPress(event);"/>
--- a/browser/components/preferences/translation.xul
+++ b/browser/components/preferences/translation.xul
@@ -1,14 +1,13 @@
 <?xml version="1.0"?>
 
-# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+<!-- 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/. -->
 
 <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
 
 <!DOCTYPE dialog SYSTEM "chrome://browser/locale/preferences/translation.dtd">
 
 <window id="TranslationDialog" class="windowDialog"
         windowtype="Browser:TranslationExceptions"
--- a/browser/devtools/netmonitor/netmonitor-view.js
+++ b/browser/devtools/netmonitor/netmonitor-view.js
@@ -2099,18 +2099,17 @@ NetworkDetailsView.prototype = {
     // Show the "Preview" tabpanel only for plain HTML responses.
     this.sidebar.toggleTab(isHtml, "preview-tab", "preview-tabpanel");
 
     // Show the "Security" tab only for requests that
     //   1) are https (state != insecure)
     //   2) come from a target that provides security information.
     let hasSecurityInfo = aData.securityState &&
                           aData.securityState !== "insecure";
-
-    $("#security-tab").hidden = !hasSecurityInfo;
+    this.sidebar.toggleTab(hasSecurityInfo, "security-tab", "security-tabpanel");
 
     // Switch to the "Headers" tabpanel if the "Preview" previously selected
     // and this is not an HTML response or "Security" was selected but this
     // request has no security information.
 
     if (!isHtml && this.widget.selectedPanel === $("#preview-tabpanel") ||
         !hasSecurityInfo && this.widget.selectedPanel === $("#security-tabpanel")) {
       this.widget.selectedIndex = 0;
--- a/browser/devtools/performance/test/browser.ini
+++ b/browser/devtools/performance/test/browser.ini
@@ -34,12 +34,13 @@ support-files =
 [browser_perf-overview-selection-03.js]
 [browser_perf-shared-connection-02.js]
 [browser_perf-shared-connection-03.js]
 # [browser_perf-shared-connection-04.js] bug 1077464
 [browser_perf-ui-recording.js]
 [browser_perf_recordings-io-01.js]
 [browser_perf_recordings-io-02.js]
 [browser_perf_recordings-io-03.js]
+[browser_perf_recordings-io-04.js]
 [browser_perf-recording-selected-01.js]
 [browser_perf-recording-selected-02.js]
 [browser_perf-recording-selected-03.js]
-[browser_perf_recordings-io-04.js]
+[browser_perf-recording-selected-04.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/browser_perf-recording-selected-04.js
@@ -0,0 +1,29 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that all components get rerendered for a profile when switching.
+ */
+
+let test = Task.async(function*() {
+  let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
+  let { $, EVENTS, PerformanceController, RecordingsView } = panel.panelWin;
+
+  yield startRecording(panel);
+  yield stopRecording(panel);
+
+  yield startRecording(panel);
+  yield stopRecording(panel);
+
+  let rerender = waitForWidgetsRendered(panel);
+  RecordingsView.selectedIndex = 0;
+  yield rerender;
+
+  rerender = waitForWidgetsRendered(panel);
+  RecordingsView.selectedIndex = 1;
+  yield rerender;
+
+  yield teardown(panel);
+  finish();
+});
--- a/browser/devtools/performance/views/details-call-tree.js
+++ b/browser/devtools/performance/views/details-call-tree.js
@@ -7,33 +7,32 @@
  * CallTree view containing profiler call tree, controlled by DetailsView.
  */
 let CallTreeView = {
   /**
    * Sets up the view with event binding.
    */
   initialize: function () {
     this._callTree = $(".call-tree-cells-container");
-    this._onRecordingStopped = this._onRecordingStopped.bind(this);
-    this._onRecordingSelected = this._onRecordingSelected.bind(this);
+    this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
     this._onRangeChange = this._onRangeChange.bind(this);
     this._onLink = this._onLink.bind(this);
 
-    PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
-    PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
+    PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
+    PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
     OverviewView.on(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
     OverviewView.on(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
   },
 
   /**
    * Unbinds events.
    */
   destroy: function () {
-    PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
-    PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
+    PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
+    PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
     OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
     OverviewView.off(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
   },
 
   /**
    * Method for handling all the set up for rendering a new call tree.
    */
   render: function (profilerData, beginAt, endAt, options={}) {
@@ -42,30 +41,20 @@ let CallTreeView = {
       return;
     }
     let threadNode = this._prepareCallTree(profilerData, beginAt, endAt, options);
     this._populateCallTree(threadNode, options);
     this.emit(EVENTS.CALL_TREE_RENDERED);
   },
 
   /**
-   * Called when recording is stopped.
+   * Called when recording is stopped or has been selected.
    */
-  _onRecordingStopped: function () {
-    let profilerData = PerformanceController.getProfilerData();
-    this.render(profilerData);
-  },
-
-  /**
-   * Called when a recording has been selected.
-   */
-  _onRecordingSelected: function (_, recording) {
+  _onRecordingStoppedOrSelected: function (_, recording) {
     // If not recording, then this recording is done and we can render all of it
-    // Otherwise, TODO in bug 1120699 will hide the details view altogether if
-    // this is still recording.
     if (!recording.isRecording()) {
       let profilerData = recording.getProfilerData();
       this.render(profilerData);
     }
   },
 
   /**
    * Fired when a range is selected or cleared in the OverviewView.
--- a/browser/devtools/performance/views/details-flamegraph.js
+++ b/browser/devtools/performance/views/details-flamegraph.js
@@ -7,33 +7,35 @@
  * FlameGraph view containing a pyramid-like visualization of a profile,
  * controlled by DetailsView.
  */
 let FlameGraphView = {
   /**
    * Sets up the view with event binding.
    */
   initialize: Task.async(function* () {
-    this._onRecordingStopped = this._onRecordingStopped.bind(this);
+    this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
     this._onRangeChange = this._onRangeChange.bind(this);
 
     this.graph = new FlameGraph($("#flamegraph-view"));
     this.graph.timelineTickUnits = L10N.getStr("graphs.ms");
     yield this.graph.ready();
 
-    PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
+    PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
+    PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
     OverviewView.on(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
     OverviewView.on(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
   }),
 
   /**
    * Unbinds events.
    */
   destroy: function () {
-    PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
+    PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
+    PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
     OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
     OverviewView.off(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
   },
 
   /**
    * Method for handling all the set up for rendering a new flamegraph.
    */
   render: function (profilerData) {
@@ -47,21 +49,24 @@ let FlameGraphView = {
       filterFrames: !Prefs.showPlatformData && FrameNode.isContent,
       showIdleBlocks: Prefs.showIdleBlocks && L10N.getStr("table.idle")
     });
     this.graph.setData(dataSrc);
     this.emit(EVENTS.FLAMEGRAPH_RENDERED);
   },
 
   /**
-   * Called when recording is stopped.
+   * Called when recording is stopped or selected.
    */
-  _onRecordingStopped: function () {
-    let profilerData = PerformanceController.getProfilerData();
-    this.render(profilerData);
+  _onRecordingStoppedOrSelected: function (_, recording) {
+    // If not recording, then this recording is done and we can render all of it
+    if (!recording.isRecording()) {
+      let profilerData = recording.getProfilerData();
+      this.render(profilerData);
+    }
   },
 
   /**
    * Fired when a range is selected or cleared in the OverviewView.
    */
   _onRangeChange: function (_, params) {
     // TODO bug 1105014
   }
--- a/browser/devtools/performance/views/details-waterfall.js
+++ b/browser/devtools/performance/views/details-waterfall.js
@@ -7,46 +7,45 @@
  * Waterfall view containing the timeline markers, controlled by DetailsView.
  */
 let WaterfallView = {
   /**
    * Sets up the view with event binding.
    */
   initialize: Task.async(function *() {
     this._onRecordingStarted = this._onRecordingStarted.bind(this);
-    this._onRecordingStopped = this._onRecordingStopped.bind(this);
-    this._onRecordingSelected = this._onRecordingSelected.bind(this);
+    this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
     this._onMarkerSelected = this._onMarkerSelected.bind(this);
     this._onResize = this._onResize.bind(this);
 
     this.waterfall = new Waterfall($("#waterfall-breakdown"), $("#details-pane"), TIMELINE_BLUEPRINT);
     this.details = new MarkerDetails($("#waterfall-details"), $("#waterfall-view > splitter"));
 
     this.waterfall.on("selected", this._onMarkerSelected);
     this.waterfall.on("unselected", this._onMarkerSelected);
     this.details.on("resize", this._onResize);
 
     PerformanceController.on(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
-    PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
-    PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
+    PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
+    PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
 
     this.waterfall.recalculateBounds();
   }),
 
   /**
    * Unbinds events.
    */
   destroy: function () {
     this.waterfall.off("selected", this._onMarkerSelected);
     this.waterfall.off("unselected", this._onMarkerSelected);
     this.details.off("resize", this._onResize);
 
     PerformanceController.off(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
-    PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
-    PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
+    PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
+    PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
   },
 
   /**
    * Method for handling all the set up for rendering a new waterfall.
    */
   render: function() {
     let recording = PerformanceController.getCurrentRecording();
     let { startTime, endTime } = recording.getInterval();
@@ -60,26 +59,19 @@ let WaterfallView = {
   /**
    * Called when recording starts.
    */
   _onRecordingStarted: function () {
     this.waterfall.clearView();
   },
 
   /**
-   * Called when recording stops.
+   * Called when recording stops or is selected.
    */
-  _onRecordingStopped: function () {
-    this.render();
-  },
-
-  /**
-   * Called when a recording is selected.
-   */
-  _onRecordingSelected: function (_, recording) {
+  _onRecordingStoppedOrSelected: function (_, recording) {
     if (!recording.isRecording()) {
       this.render();
     }
   },
 
   /**
    * Called when a marker is selected in the waterfall view,
    * updating the markers detail view.
--- a/browser/modules/WindowsPreviewPerTab.jsm
+++ b/browser/modules/WindowsPreviewPerTab.jsm
@@ -198,17 +198,19 @@ PreviewController.prototype = {
       rects.push(r);
     }
     return rects;
   },
 
   // Resizes the canvasPreview to 0x0, essentially freeing its memory.
   // updateCanvasPreview() will detect the size mismatch as a resize event
   // the next time it is called.
-  resetCanvasPreview: function () this.resizeCanvasPreview(0, 0),
+  resetCanvasPreview: function () {
+    this.resizeCanvasPreview(0, 0);
+  },
 
   resizeCanvasPreview: function (width, height) {
     this.canvasPreview.width = width;
     this.canvasPreview.height = height;
   },
 
   get wasResizedSinceLastPreview () {
     let bx = this.linkedBrowser.boxObject;
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -194,17 +194,17 @@ browser.jar:
 * skin/classic/browser/customizableui/panelUIOverlay.css    (customizableui/panelUIOverlay.css)
   skin/classic/browser/customizableui/whimsy.png          (../shared/customizableui/whimsy.png)
   skin/classic/browser/customizableui/whimsy@2x.png       (../shared/customizableui/whimsy@2x.png)
   skin/classic/browser/customizableui/whimsy-bw.png       (../shared/customizableui/whimsy-bw.png)
   skin/classic/browser/customizableui/whimsy-bw@2x.png    (../shared/customizableui/whimsy-bw@2x.png)
   skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
   skin/classic/browser/downloads/buttons.png                (downloads/buttons.png)
   skin/classic/browser/downloads/buttons@2x.png             (downloads/buttons@2x.png)
-* skin/classic/browser/downloads/contentAreaDownloadsView.css (downloads/contentAreaDownloadsView.css)
+  skin/classic/browser/downloads/contentAreaDownloadsView.css (downloads/contentAreaDownloadsView.css)
   skin/classic/browser/downloads/download-glow-menuPanel.png (downloads/download-glow-menuPanel.png)
   skin/classic/browser/downloads/download-glow-menuPanel@2x.png (downloads/download-glow-menuPanel@2x.png)
   skin/classic/browser/downloads/download-notification-finish.png  (downloads/download-notification-finish.png)
   skin/classic/browser/downloads/download-notification-finish@2x.png  (downloads/download-notification-finish@2x.png)
   skin/classic/browser/downloads/download-notification-start.png  (downloads/download-notification-start.png)
   skin/classic/browser/downloads/download-notification-start@2x.png  (downloads/download-notification-start@2x.png)
   skin/classic/browser/downloads/download-summary.png       (downloads/download-summary.png)
   skin/classic/browser/downloads/download-summary@2x.png    (downloads/download-summary@2x.png)
--- a/build/autoconf/compiler-opts.m4
+++ b/build/autoconf/compiler-opts.m4
@@ -329,17 +329,17 @@ fi
 
 # bionic in Android < 4.1 doesn't support PIE
 # On OSX, the linker defaults to building PIE programs when targetting OSX 10.7+,
 # but not when targetting OSX < 10.7. OSX < 10.7 doesn't support running PIE
 # programs, so as long as support for OSX 10.6 is kept, we can't build PIE.
 # Even after dropping 10.6 support, MOZ_PIE would not be useful since it's the
 # default (and clang says the -pie option is not used).
 # On other Unix systems, some file managers (Nautilus) can't start PIE programs
-if test -n "$gonkdir" -a "$ANDROID_VERSION" -ge 16; then
+if test -n "$gonkdir" && test "$ANDROID_VERSION" -ge 16; then
     MOZ_PIE=1
 else
     MOZ_PIE=
 fi
 
 MOZ_ARG_ENABLE_BOOL(pie,
 [  --enable-pie           Enable Position Independent Executables],
     MOZ_PIE=1,
--- a/caps/tests/mochitest/test_bug995943.xul
+++ b/caps/tests/mochitest/test_bug995943.xul
@@ -21,18 +21,20 @@ https://bugzilla.mozilla.org/show_bug.cg
   const Cc = Components.classes;
   const Ci = Components.interfaces;
   Cu.import("resource://gre/modules/Services.jsm");
   function debug(msg) { info(msg); }
 
   /** Test for CAPS file:// URI prefs. **/
   SimpleTest.waitForExplicitFinish();
   SimpleTest.requestCompleteLog();
-  if (Services.appinfo.OS == "Darwin") // See bug 1067022
-    SimpleTest.expectAssertions(0, 1);
+  if (navigator.userAgent.indexOf("Mac OS X 10.10") != -1)
+    SimpleTest.expectAssertions(5); // See bug 1067022
+  else if (Services.appinfo.OS == "Darwin")
+    SimpleTest.expectAssertions(0, 1); // See bug 1067022
 
   var rootdir = Services.appinfo.OS == "WINNT" ? "file:///C:" : "file:///";
 
   function checkLoadFileURI(domain, shouldLoad) {
     debug("Invoking checkLoadFileURI with domain: " + domain + ", shouldLoad: " + shouldLoad);
     return new Promise(function(resolve, reject) {
       $('ifr').addEventListener('load', function l1() {
         debug("Invoked l1 for " + domain);
--- a/configure.in
+++ b/configure.in
@@ -5080,17 +5080,17 @@ if test -n "$MOZ_WEBRTC"; then
     *)
         dnl default to disabled for all others
         MOZ_WEBRTC=
         ;;
     esac
 fi
 
 dnl Temporary until webrtc works on gonk-L
-if test -n "$gonkdir" -a "$ANDROID_VERSION" -ge 21; then
+if test -n "$gonkdir" && test "$ANDROID_VERSION" -ge 21; then
     MOZ_WEBRTC=
 fi
 
 AC_TRY_COMPILE([#include <linux/ethtool.h>],
                [ struct ethtool_cmd cmd; cmd.speed_hi = 0; ],
                MOZ_WEBRTC_HAVE_ETHTOOL_SPEED_HI=1)
 
 AC_SUBST(MOZ_WEBRTC_HAVE_ETHTOOL_SPEED_HI)
--- a/dom/base/ScriptSettings.h
+++ b/dom/base/ScriptSettings.h
@@ -314,18 +314,18 @@ private:
 
   AutoJSAPI(const AutoJSAPI&) = delete;
   AutoJSAPI& operator= (const AutoJSAPI&) = delete;
 };
 
 /*
  * A class that represents a new script entry point.
  */
-class AutoEntryScript : public AutoJSAPI,
-                        protected ScriptSettingsStackEntry {
+class MOZ_STACK_CLASS AutoEntryScript : public AutoJSAPI,
+                                        protected ScriptSettingsStackEntry {
 public:
   explicit AutoEntryScript(nsIGlobalObject* aGlobalObject,
                   bool aIsMainThread = NS_IsMainThread(),
                   // Note: aCx is mandatory off-main-thread.
                   JSContext* aCx = nullptr);
 
   ~AutoEntryScript();
 
@@ -336,20 +336,20 @@ public:
 private:
   // It's safe to make this a weak pointer, since it's the subject principal
   // when we go on the stack, so can't go away until after we're gone.  In
   // particular, this is only used from the CallSetup constructor, and only in
   // the aIsJSImplementedWebIDL case.  And in that case, the subject principal
   // is the principal of the callee function that is part of the CallArgs just a
   // bit up the stack, and which will outlive us.  So we know the principal
   // can't go away until then either.
-  nsIPrincipal* mWebIDLCallerPrincipal;
+  nsIPrincipal* MOZ_NON_OWNING_REF mWebIDLCallerPrincipal;
   friend nsIPrincipal* GetWebIDLCallerPrincipal();
 
-  nsIDocShell* mDocShellForJSRunToCompletion;
+  nsCOMPtr<nsIDocShell> mDocShellForJSRunToCompletion;
 
   bool mIsMainThread;
 };
 
 /*
  * A class that can be used to force a particular incumbent script on the stack.
  */
 class AutoIncumbentScript : protected ScriptSettingsStackEntry {
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -150,17 +150,19 @@ enum EventNameType {
   EventNameType_HTMLBodyOrFramesetOnly = 0x0020,
 
   EventNameType_HTMLXUL = 0x0003,
   EventNameType_All = 0xFFFF
 };
 
 struct EventNameMapping
 {
-  nsIAtom* mAtom;
+  // This holds pointers to nsGkAtoms members, and is therefore safe as a
+  // non-owning reference.
+  nsIAtom* MOZ_OWNING_REF mAtom;
   uint32_t mId;
   int32_t  mType;
   mozilla::EventClassID mEventClassID;
 };
 
 struct nsShortcutCandidate {
   nsShortcutCandidate(uint32_t aCharCode, bool aIgnoreShift) :
     mCharCode(aCharCode), mIgnoreShift(aIgnoreShift)
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -830,17 +830,17 @@ protected:
   // is false.  Too bad we have so many different concepts of
   // "active".  Only used on outer windows.
   bool                   mIsBackground;
 
   bool                   mAudioMuted;
   float                  mAudioVolume;
 
   // And these are the references between inner and outer windows.
-  nsPIDOMWindow         *mInnerWindow;
+  nsPIDOMWindow* MOZ_NON_OWNING_REF mInnerWindow;
   nsCOMPtr<nsPIDOMWindow> mOuterWindow;
 
   // the element within the document that is currently focused when this
   // window is active
   nsCOMPtr<nsIContent> mFocusedNode;
 
   // The AudioContexts created for the current document, if any.
   nsTArray<mozilla::dom::AudioContext*> mAudioContexts; // Weak
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -675,17 +675,17 @@ skip-if = buildapp == 'b2g' || toolkit =
 [test_domparser_null_char.html]
 [test_domparsing.html]
 [test_elementTraversal.html]
 [test_element_closest.html]
 [test_encodeToStringWithMaxLength.html]
 [test_fileapi.html]
 skip-if = e10s
 [test_fileapi_slice.html]
-disabled = Busted on B2G, Android, E10S and now Mulet. Bug 775227.
+skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' || e10s #bug 775227
 [test_getElementById.html]
 [test_html_colors_quirks.html]
 [test_html_colors_standards.html]
 [test_html_in_xhr.html]
 [test_htmlcopyencoder.html]
 [test_htmlcopyencoder.xhtml]
 [test_ipc_messagemanager_blob.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 936226
--- a/dom/base/test/test_fileapi_slice.html
+++ b/dom/base/test/test_fileapi_slice.html
@@ -17,21 +17,16 @@ https://bugzilla.mozilla.org/show_bug.cg
   <canvas id=testcanvas hidden moz-opaque></canvas>
   <input id="fileList" type="file"></input>
 </p>
 <div id="content" style="display: none">
 </div>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
-const isOSXMtnLion = navigator.userAgent.indexOf("Mac OS X 10.8") != -1;
-
-if (isOSXMtnLion) {
-  todo(false, "Mountain Lion doesn't like this test (bug 788999)");
-} else {
 var fileNum = 1;
 SimpleTest.waitForExplicitFinish();
 
 // Create files containing data we'll test with. We'll want long
 // strings to ensure they span multiple buffers while loading
 
 // Create a decent-sized image
 cx = $("canvas").getContext('2d');
@@ -131,12 +126,11 @@ expectedTestCount++;
 
 // image past end
 var imgfile = createFileWithData(testBinaryData + fileData);
 is(imgfile.size, size + testBinaryData.length, "correct file size (past end)");
 var img = new Image;
 img.src = URL.createObjectURL(imgfile.slice(testBinaryData.length, testBinaryData.length + size + 1000));
 img.onload = imageLoadHandler;
 expectedTestCount++;
-}
 </script>
 </pre>
 </body> </html>
--- a/dom/bindings/DOMString.h
+++ b/dom/bindings/DOMString.h
@@ -170,17 +170,19 @@ public:
   }
 
 private:
   // We need to be able to act like a string as needed
   Maybe<nsAutoString> mString;
 
   // For callees that know we exist, we can be a stringbuffer/length/null-flag
   // triple.
-  nsStringBuffer* mStringBuffer;
+  nsStringBuffer* MOZ_UNSAFE_REF("The ways in which this can be safe are "
+                                 "documented above and enforced through "
+                                 "assertions") mStringBuffer;
   uint32_t mLength;
   bool mIsNull;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_DOMString_h
--- a/dom/events/EventListenerManager.h
+++ b/dom/events/EventListenerManager.h
@@ -553,17 +553,17 @@ protected:
   uint32_t mMayHaveScrollWheelEventListener : 1;
   uint32_t mMayHaveMouseEnterLeaveEventListener : 1;
   uint32_t mMayHavePointerEnterLeaveEventListener : 1;
   uint32_t mClearingListeners : 1;
   uint32_t mIsMainThreadELM : 1;
   uint32_t mNoListenerForEvent : 23;
 
   nsAutoTObserverArray<Listener, 2> mListeners;
-  dom::EventTarget* mTarget;  // WEAK
+  dom::EventTarget* MOZ_NON_OWNING_REF mTarget;
   nsCOMPtr<nsIAtom> mNoListenerForEventAtom;
 
   friend class ELMCreationDetector;
   static uint32_t sMainThreadCreatedCount;
 };
 
 } // namespace mozilla
 
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -13,16 +13,17 @@
 #include <stdint.h>
 
 #include "MediaDecoderStateMachine.h"
 #include "MediaDecoderStateMachineScheduler.h"
 #include "AudioSink.h"
 #include "nsTArray.h"
 #include "MediaDecoder.h"
 #include "MediaDecoderReader.h"
+#include "mozilla/MathAlgorithms.h"
 #include "mozilla/mozalloc.h"
 #include "VideoUtils.h"
 #include "mozilla/dom/TimeRanges.h"
 #include "nsDeque.h"
 #include "AudioSegment.h"
 #include "VideoSegment.h"
 #include "ImageContainer.h"
 #include "nsComponentManagerUtils.h"
@@ -160,17 +161,17 @@ static const uint32_t QUICK_BUFFERING_LO
 // QUICK_BUFFERING_LOW_DATA_USECS.
 static_assert(QUICK_BUFFERING_LOW_DATA_USECS <= AMPLE_AUDIO_USECS,
               "QUICK_BUFFERING_LOW_DATA_USECS is too large");
 
 // The amount of instability we tollerate in calls to
 // MediaDecoderStateMachine::UpdateEstimatedDuration(); changes of duration
 // less than this are ignored, as they're assumed to be the result of
 // instability in the duration estimation.
-static const int64_t ESTIMATED_DURATION_FUZZ_FACTOR_USECS = USECS_PER_S / 2;
+static const uint64_t ESTIMATED_DURATION_FUZZ_FACTOR_USECS = USECS_PER_S / 2;
 
 static TimeDuration UsecsToDuration(int64_t aUsecs) {
   return TimeDuration::FromMicroseconds(aUsecs);
 }
 
 static int64_t DurationToUsecs(TimeDuration aDuration) {
   return static_cast<int64_t>(aDuration.ToSeconds() * USECS_PER_S);
 }
@@ -1444,17 +1445,17 @@ void MediaDecoderStateMachine::SetDurati
   mEndTime = mStartTime + aDuration;
 }
 
 void MediaDecoderStateMachine::UpdateEstimatedDuration(int64_t aDuration)
 {
   AssertCurrentThreadInMonitor();
   int64_t duration = GetDuration();
   if (aDuration != duration &&
-      std::abs(aDuration - duration) > ESTIMATED_DURATION_FUZZ_FACTOR_USECS) {
+      mozilla::Abs(aDuration - duration) > ESTIMATED_DURATION_FUZZ_FACTOR_USECS) {
     SetDuration(aDuration);
     nsCOMPtr<nsIRunnable> event =
       NS_NewRunnableMethod(mDecoder, &MediaDecoder::DurationChanged);
     NS_DispatchToMainThread(event);
   }
 }
 
 void MediaDecoderStateMachine::SetMediaEndTime(int64_t aEndTime)
--- a/dom/system/gonk/GonkGPSGeolocationProvider.cpp
+++ b/dom/system/gonk/GonkGPSGeolocationProvider.cpp
@@ -54,16 +54,20 @@
 using namespace mozilla;
 using namespace mozilla::dom;
 
 static const int kDefaultPeriod = 1000; // ms
 static bool gDebug_isLoggingEnabled = false;
 static bool gDebug_isGPSLocationIgnored = false;
 static const char* kNetworkConnStateChangedTopic = "network-connection-state-changed";
 static const char* kMozSettingsChangedTopic = "mozsettings-changed";
+#ifdef MOZ_B2G_RIL
+static const char* kPrefRilNumRadioInterfaces = "ril.numRadioInterfaces";
+static const char* kSettingRilDefaultServiceId = "ril.data.defaultServiceId";
+#endif
 // Both of these settings can be toggled in the Gaia Developer settings screen.
 static const char* kSettingDebugEnabled = "geolocation.debugging.enabled";
 static const char* kSettingDebugGpsIgnored = "geolocation.debugging.gps-locations-ignored";
 
 // While most methods of GonkGPSGeolocationProvider should only be
 // called from main thread, we deliberately put the Init and ShutdownGPS
 // methods off main thread to avoid blocking.
 NS_IMPL_ISUPPORTS(GonkGPSGeolocationProvider,
@@ -284,16 +288,20 @@ GonkGPSGeolocationProvider::AGPSRILRefLo
 #endif // MOZ_B2G_RIL
 
 GonkGPSGeolocationProvider::GonkGPSGeolocationProvider()
   : mStarted(false)
   , mSupportsScheduling(false)
 #ifdef MOZ_B2G_RIL
   , mSupportsMSB(false)
   , mSupportsMSA(false)
+  , mRilDataServiceId(0)
+  , mNumberOfRilServices(1)
+  , mObservingNetworkConnStateChange(false)
+  , mObservingSettingsChange(false)
 #endif
   , mSupportsSingleShot(false)
   , mSupportsTimeInjection(false)
   , mGpsInterface(nullptr)
 {
 }
 
 GonkGPSGeolocationProvider::~GonkGPSGeolocationProvider()
@@ -709,27 +717,34 @@ GonkGPSGeolocationProvider::SetupAGPS()
   int32_t suplPort = Preferences::GetInt("geo.gps.supl_port", -1);
   if (!suplServer.IsEmpty() && suplPort > 0) {
     mAGpsInterface->set_server(AGPS_TYPE_SUPL, suplServer.get(), suplPort);
   } else {
     NS_WARNING("Cannot get SUPL server settings");
     return;
   }
 
-  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
-  if (obs) {
-    obs->AddObserver(this, kNetworkConnStateChangedTopic, false);
-  }
+  // Request RIL date service ID for correct RadioInterface object first due to
+  // multi-SIM case needs it to handle AGPS related stuffs. For single SIM, 0
+  // will be returned as default RIL data service ID.
+  RequestSettingValue(kSettingRilDefaultServiceId);
+}
 
+void
+GonkGPSGeolocationProvider::UpdateRadioInterface()
+{
   nsCOMPtr<nsIRadioInterfaceLayer> ril = do_GetService("@mozilla.org/ril;1");
-  if (ril) {
-    // TODO: Bug 878748 - B2G GPS: acquire correct RadioInterface instance in
-    // MultiSIM configuration
-    ril->GetRadioInterface(0 /* clientId */, getter_AddRefs(mRadioInterface));
-  }
+  NS_ENSURE_TRUE_VOID(ril);
+  ril->GetRadioInterface(mRilDataServiceId, getter_AddRefs(mRadioInterface));
+}
+
+bool
+GonkGPSGeolocationProvider::IsValidRilServiceId(uint32_t aServiceId)
+{
+  return aServiceId < mNumberOfRilServices;
 }
 #endif // MOZ_B2G_RIL
 
 
 NS_IMPL_ISUPPORTS(GonkGPSGeolocationProvider::NetworkLocationUpdate,
                   nsIGeolocationUpdate)
 
 NS_IMETHODIMP
@@ -843,19 +858,22 @@ GonkGPSGeolocationProvider::Startup()
   }
 
   RequestSettingValue(kSettingDebugEnabled);
   RequestSettingValue(kSettingDebugGpsIgnored);
 
   // Setup an observer to watch changes to the setting.
   nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
   if (observerService) {
+    MOZ_ASSERT(!mObservingSettingsChange);
     nsresult rv = observerService->AddObserver(this, kMozSettingsChangedTopic, false);
     if (NS_FAILED(rv)) {
       NS_WARNING("geo: Gonk GPS AddObserver failed");
+    } else {
+      mObservingSettingsChange = true;
     }
   }
 
   if (!mInitThread) {
     nsresult rv = NS_NewThread(getter_AddRefs(mInitThread));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
@@ -867,16 +885,19 @@ GonkGPSGeolocationProvider::Startup()
     nsresult rv = mNetworkLocationProvider->Startup();
     if (NS_SUCCEEDED(rv)) {
       nsRefPtr<NetworkLocationUpdate> update = new NetworkLocationUpdate();
       mNetworkLocationProvider->Watch(update);
     }
   }
 
   mStarted = true;
+#ifdef MOZ_B2G_RIL
+  mNumberOfRilServices = Preferences::GetUint(kPrefRilNumRadioInterfaces, 1);
+#endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GonkGPSGeolocationProvider::Watch(nsIGeolocationUpdate* aCallback)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
@@ -901,21 +922,25 @@ GonkGPSGeolocationProvider::Shutdown()
 
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (obs) {
     nsresult rv;
 #ifdef MOZ_B2G_RIL
     rv = obs->RemoveObserver(this, kNetworkConnStateChangedTopic);
     if (NS_FAILED(rv)) {
       NS_WARNING("geo: Gonk GPS network state RemoveObserver failed");
+    } else {
+      mObservingNetworkConnStateChange = false;
     }
 #endif
     rv = obs->RemoveObserver(this, kMozSettingsChangedTopic);
     if (NS_FAILED(rv)) {
       NS_WARNING("geo: Gonk GPS mozsettings RemoveObserver failed");
+    } else {
+      mObservingSettingsChange = false;
     }
   }
 
   mInitThread->Dispatch(NS_NewRunnableMethod(this, &GonkGPSGeolocationProvider::ShutdownGPS),
                         NS_DISPATCH_NORMAL);
 
   return NS_OK;
 }
@@ -1038,16 +1063,25 @@ GonkGPSGeolocationProvider::Observe(nsIS
                                             gDebug_isGPSLocationIgnored);
       }
       return NS_OK;
     } else if (setting.mKey.EqualsASCII(kSettingDebugEnabled)) {
       nsContentUtils::LogMessageToConsole("geo: received mozsettings-changed: logging\n");
       gDebug_isLoggingEnabled =
         setting.mValue.isBoolean() ? setting.mValue.toBoolean() : false;
       return NS_OK;
+    } else if (setting.mKey.EqualsASCII(kSettingRilDefaultServiceId)) {
+      if (!setting.mValue.isNumber() ||
+          !IsValidRilServiceId(setting.mValue.toNumber())) {
+        return NS_ERROR_UNEXPECTED;
+      }
+
+      mRilDataServiceId = setting.mValue.toNumber();
+      UpdateRadioInterface();
+      return NS_OK;
     }
   }
 
   return NS_OK;
 }
 
 /** nsISettingsServiceCallback **/
 
@@ -1066,16 +1100,42 @@ GonkGPSGeolocationProvider::Handle(const
       nsAutoJSString apn;
       if (!apn.init(cx, aResult.toString())) {
         return NS_ERROR_FAILURE;
       }
       if (!apn.IsEmpty()) {
         SetAGpsDataConn(apn);
       }
     }
+  } else if (aName.EqualsASCII(kSettingRilDefaultServiceId)) {
+    uint32_t id = 0;
+    JSContext *cx = nsContentUtils::GetCurrentJSContext();
+    NS_ENSURE_TRUE(cx, NS_OK);
+    if (!JS::ToUint32(cx, aResult, &id)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    if (!IsValidRilServiceId(id)) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    mRilDataServiceId = id;
+    UpdateRadioInterface();
+
+    MOZ_ASSERT(!mObservingNetworkConnStateChange);
+
+    // Now we know which service ID to deal with, observe necessary topic then
+    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+    NS_ENSURE_TRUE(obs, NS_OK);
+
+    if (NS_FAILED(obs->AddObserver(this, kNetworkConnStateChangedTopic, false))) {
+      NS_WARNING("Failed to add network state changed observer!");
+    } else {
+      mObservingNetworkConnStateChange = true;
+    }
   }
 #endif // MOZ_B2G_RIL
   return NS_OK;
 }
 
 NS_IMETHODIMP
 GonkGPSGeolocationProvider::HandleError(const nsAString& aErrorMessage)
 {
--- a/dom/system/gonk/GonkGPSGeolocationProvider.h
+++ b/dom/system/gonk/GonkGPSGeolocationProvider.h
@@ -77,16 +77,18 @@ private:
 #endif
 
   void Init();
   void StartGPS();
   void ShutdownGPS();
   void InjectLocation(double latitude, double longitude, float accuracy);
   void RequestSettingValue(const char* aKey);
 #ifdef MOZ_B2G_RIL
+  void UpdateRadioInterface();
+  bool IsValidRilServiceId(uint32_t aServiceId);
   void SetupAGPS();
   int32_t GetDataConnectionState();
   void SetAGpsDataConn(nsAString& aApn);
   void RequestDataConnection();
   void ReleaseDataConnection();
   void RequestSetID(uint32_t flags);
   void SetReferenceLocation();
 #endif
@@ -96,16 +98,23 @@ private:
   static GonkGPSGeolocationProvider* sSingleton;
 
   bool mStarted;
 
   bool mSupportsScheduling;
 #ifdef MOZ_B2G_RIL
   bool mSupportsMSB;
   bool mSupportsMSA;
+  uint32_t mRilDataServiceId;
+  // mNumberOfRilServices indicates how many SIM slots supported on device, and
+  // RadioInterfaceLayer.js takes responsibility to set up the corresponding
+  // preference value.
+  uint32_t mNumberOfRilServices;
+  bool mObservingNetworkConnStateChange;
+  bool mObservingSettingsChange;
 #endif
   bool mSupportsSingleShot;
   bool mSupportsTimeInjection;
 
   const GpsInterface* mGpsInterface;
 #ifdef MOZ_B2G_RIL
   const AGpsInterface* mAGpsInterface;
   const AGpsRilInterface* mAGpsRilInterface;
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -2032,16 +2032,22 @@ public:
     // Assumes that WorkerJSRuntimeStats will hold a reference to |path|, and
     // not a copy, as TryToMapAddon() may later modify if.
     nsCString path;
     WorkerJSRuntimeStats rtStats(path);
 
     {
       MutexAutoLock lock(mMutex);
 
+      if (!mWorkerPrivate ||
+          !mWorkerPrivate->BlockAndCollectRuntimeStats(&rtStats, aAnonymize)) {
+        // Returning NS_OK here will effectively report 0 memory.
+        return NS_OK;
+      }
+
       path.AppendLiteral("explicit/workers/workers(");
       if (aAnonymize && !mWorkerPrivate->Domain().IsEmpty()) {
         path.AppendLiteral("<anonymized-domain>)/worker(<anonymized-url>");
       } else {
         nsCString escapedDomain(mWorkerPrivate->Domain());
         if (escapedDomain.IsEmpty()) {
           escapedDomain += "chrome";
         } else {
@@ -2051,22 +2057,16 @@ public:
         path.AppendLiteral(")/worker(");
         NS_ConvertUTF16toUTF8 escapedURL(mWorkerPrivate->ScriptURL());
         escapedURL.ReplaceChar('/', '\\');
         path.Append(escapedURL);
       }
       path.AppendPrintf(", 0x%p)/", static_cast<void*>(mWorkerPrivate));
 
       TryToMapAddon(path);
-
-      if (!mWorkerPrivate ||
-          !mWorkerPrivate->BlockAndCollectRuntimeStats(&rtStats, aAnonymize)) {
-        // Returning NS_OK here will effectively report 0 memory.
-        return NS_OK;
-      }
     }
 
     return xpc::ReportJSRuntimeExplicitTreeStats(rtStats, path,
                                                  aCallback, aClosure,
                                                  aAnonymize);
   }
 
 private:
--- a/editor/libeditor/nsHTMLURIRefObject.cpp
+++ b/editor/libeditor/nsHTMLURIRefObject.cpp
@@ -34,42 +34,16 @@
     applet: codebase, archive <list>
     object: codebase, data, classid, usemap
     head:   profile
     del:    cite
     ins:    cite
     q:      cite
  */
 
-/* Here is how to open a channel for testing
-   (from embed/qa/testembed/Tests.cpp):
-
-  nsCOMPtr<nsIChannel> theChannel;
-  nsCString uri;
-  nsCOMPtr<nsIURI> theURI;
-  rv = NS_NewURI(getter_AddRefs(theURI), theSpec);
-  if (!theURI)
-    error;
-  rv = NS_OpenURI(getter_AddRefs(theChannel), theURI, nullptr, theLoadGroup);
-  if (!theChannel)
-    error;
-  nsCOMPtr<nsILoadGroup> theLoadGroup(do_CreateInstance(NS_LOADGROUP_CONTRACTID));
-  if (!theLoadGroup)
-    error;
-		nsCOMPtr<nsIStreamListener> listener(static_cast<nsIStreamListener*>(qaBrowserImpl));
-		//nsCOMPtr<nsIWeakReference> thisListener(do_GetWeakReference(listener));
-		//qaWebBrowser->AddWebBrowserListener(thisListener, NS_GET_IID(nsIStreamListener));
-
-		// this calls nsIStreamListener::OnDataAvailable()
-		rv = theChannel->AsyncOpen(listener, nullptr);
-
-		nsCOMPtr<nsIRequest> theRequest = do_QueryInterface(theChannel);
-    // Now we can do things on nsIRequest (like what?)
- */
-
 #include "nsHTMLURIRefObject.h"
 
 #include "mozilla/mozalloc.h"
 #include "nsAString.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsID.h"
 #include "nsIDOMAttr.h"
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -229,16 +229,17 @@ private:
   DECL_GFX_PREF(Once, "gfx.touch.resample.vsync-adjust",       TouchVsyncSampleAdjust, int32_t, 5);
   DECL_GFX_PREF(Once, "gfx.touch.resample.delay-threshold",    TouchResampleVsyncDelayThreshold, int32_t, 20);
   DECL_GFX_PREF(Once, "gfx.touch.resample.old-touch-threshold",TouchResampleOldTouchThreshold, int32_t, 17);
 
   DECL_GFX_PREF(Live, "gl.msaa-level",                         MSAALevel, uint32_t, 2);
 
   DECL_GFX_PREF(Once, "image.cache.timeweight",                ImageCacheTimeWeight, int32_t, 500);
   DECL_GFX_PREF(Once, "image.cache.size",                      ImageCacheSize, int32_t, 5*1024*1024);
+  DECL_GFX_PREF(Live, "image.downscale-during-decode.enabled", ImageDownscaleDuringDecodeEnabled, bool, false);
   DECL_GFX_PREF(Live, "image.high_quality_downscaling.enabled", ImageHQDownscalingEnabled, bool, false);
   DECL_GFX_PREF(Live, "image.high_quality_downscaling.min_factor", ImageHQDownscalingMinFactor, uint32_t, 1000);
   DECL_GFX_PREF(Live, "image.high_quality_upscaling.max_size", ImageHQUpscalingMaxSize, uint32_t, 20971520);
   DECL_GFX_PREF(Once, "image.mem.decode_bytes_at_a_time",      ImageMemDecodeBytesAtATime, uint32_t, 200000);
   DECL_GFX_PREF(Live, "image.mem.decodeondraw",                ImageMemDecodeOnDraw, bool, false);
   DECL_GFX_PREF(Live, "image.mem.discardable",                 ImageMemDiscardable, bool, false);
   DECL_GFX_PREF(Once, "image.mem.surfacecache.discard_factor", ImageMemSurfaceCacheDiscardFactor, uint32_t, 1);
   DECL_GFX_PREF(Once, "image.mem.surfacecache.max_size_kb",    ImageMemSurfaceCacheMaxSizeKB, uint32_t, 100 * 1024);
--- a/image/build/nsImageModule.cpp
+++ b/image/build/nsImageModule.cpp
@@ -4,18 +4,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsImageModule.h"
 
 #include "mozilla/ModuleUtils.h"
 #include "nsMimeTypes.h"
 
+#include "DecodePool.h"
 #include "ImageFactory.h"
-#include "RasterImage.h"
 #include "ShutdownTracker.h"
 #include "SurfaceCache.h"
 
 #include "gfxPrefs.h"
 #include "imgLoader.h"
 #include "imgRequest.h"
 #include "imgRequestProxy.h"
 #include "imgTools.h"
@@ -87,17 +87,17 @@ nsresult
 mozilla::image::InitModule()
 {
   MOZ_ASSERT(NS_IsMainThread());
   // Make sure the preferences are initialized
   gfxPrefs::GetSingleton();
 
   mozilla::image::ShutdownTracker::Initialize();
   mozilla::image::ImageFactory::Initialize();
-  mozilla::image::RasterImage::Initialize();
+  mozilla::image::DecodePool::Initialize();
   mozilla::image::SurfaceCache::Initialize();
   imgLoader::GlobalInit();
   sInitialized = true;
   return NS_OK;
 }
 
 void
 mozilla::image::ShutdownModule()
--- a/image/decoders/nsICODecoder.cpp
+++ b/image/decoders/nsICODecoder.cpp
@@ -633,29 +633,29 @@ nsICODecoder::NeedsNewFrame() const
   if (mContainedDecoder) {
     return mContainedDecoder->NeedsNewFrame();
   }
 
   return Decoder::NeedsNewFrame();
 }
 
 nsresult
-nsICODecoder::AllocateFrame()
+nsICODecoder::AllocateFrame(const nsIntSize& aTargetSize /* = nsIntSize() */)
 {
   nsresult rv;
 
   if (mContainedDecoder) {
-    rv = mContainedDecoder->AllocateFrame();
+    rv = mContainedDecoder->AllocateFrame(aTargetSize);
     mCurrentFrame = mContainedDecoder->GetCurrentFrameRef();
     mProgress |= mContainedDecoder->TakeProgress();
     mInvalidRect.Union(mContainedDecoder->TakeInvalidRect());
     return rv;
   }
 
   // Grab a strong ref that we'll later hand over to the contained decoder. This
   // lets us avoid creating a RawAccessFrameRef off-main-thread.
-  rv = Decoder::AllocateFrame();
+  rv = Decoder::AllocateFrame(aTargetSize);
   mRefForContainedDecoder = GetCurrentFrameRef();
   return rv;
 }
 
 } // namespace image
 } // namespace mozilla
--- a/image/decoders/nsICODecoder.h
+++ b/image/decoders/nsICODecoder.h
@@ -35,17 +35,18 @@ public:
   // Obtains the height of the icon directory entry
   uint32_t GetRealHeight() const
   {
     return mDirEntry.mHeight == 0 ? 256 : mDirEntry.mHeight;
   }
 
   virtual void WriteInternal(const char* aBuffer, uint32_t aCount) MOZ_OVERRIDE;
   virtual void FinishInternal() MOZ_OVERRIDE;
-  virtual nsresult AllocateFrame() MOZ_OVERRIDE;
+  virtual nsresult AllocateFrame(const nsIntSize& aTargetSize
+                                   /* = nsIntSize() */) MOZ_OVERRIDE;
 
 protected:
   virtual bool NeedsNewFrame() const MOZ_OVERRIDE;
 
 private:
   // Writes to the contained decoder and sets the appropriate errors
   // Returns true if there are no errors.
   bool WriteToContainedDecoder(const char* aBuffer, uint32_t aCount);
--- a/image/public/imgIContainer.idl
+++ b/image/public/imgIContainer.idl
@@ -64,17 +64,17 @@ native nsIntSizeByVal(nsIntSize);
 
 /**
  * imgIContainer is the interface that represents an image. It allows
  * access to frames as Thebes surfaces. It also allows drawing of images
  * onto Thebes contexts.
  *
  * Internally, imgIContainer also manages animation of images.
  */
-[scriptable, builtinclass, uuid(14ea6fa5-183e-4409-ac88-110bd2e05292)]
+[scriptable, builtinclass, uuid(4adb4c92-284d-4f5a-85e7-e924ec57510f)]
 interface imgIContainer : nsISupports
 {
   /**
    * The width of the container rectangle.  In the case of any error,
    * zero is returned, and an exception will be thrown.
    */
   readonly attribute int32_t width;
 
@@ -340,22 +340,38 @@ interface imgIContainer : nsISupports
    * the image will at some point fire off decode notifications. Calling draw()
    * or getFrame() triggers the same mechanism internally. Thus, if you want to
    * be sure that the image will be decoded but don't want to access it until
    * then, you must call requestDecode().
    */
   void requestDecode();
 
   /*
-   * This is equivalent to requestDecode() but it also decodes some of the
-   * image.
+   * This is equivalent to requestDecode() but it also synchronously decodes
+   * images that can be decoded "quickly" according to some heuristic.
    */
   [noscript] void startDecoding();
 
   /*
+   * This method is equivalent to requestDecode(), but enables the caller to
+   * provide more detailed information about the decode request.
+   *
+   * @param aSize The size to which the image should be scaled while decoding,
+   *              if possible. If the image cannot be scaled to this size while
+   *              being decoded, it will be decoded at its intrinsic size.
+   * @param aFlags Flags of the FLAG_* variety. Only the decode flags
+   *               (FLAG_DECODE_*) and FLAG_SYNC_DECODE (which will
+   *               synchronously decode images that can be decoded "quickly",
+   *               just like startDecoding() does) are accepted; others will be
+   *               ignored.
+   */
+  [noscript] void requestDecodeForSize([const] in nsIntSize aSize,
+                                       in uint32_t aFlags);
+
+  /*
    * Returns true if no more decoding can be performed on this image. Images
    * with errors return true since they cannot be decoded any further. Note that
    * because decoded images may be discarded, isDecoded() may return false even
    * if it has returned true in the past.
    */
   [noscript, notxpcom, nostdcall] bool isDecoded();
 
   /**
--- a/image/src/DecodePool.cpp
+++ b/image/src/DecodePool.cpp
@@ -7,16 +7,17 @@
 
 #include <algorithm>
 
 #include "mozilla/ClearOnShutdown.h"
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIObserverService.h"
 #include "nsIThreadPool.h"
+#include "nsThreadUtils.h"
 #include "nsXPCOMCIDInternal.h"
 #include "prsystem.h"
 
 #ifdef MOZ_NUWA_PROCESS
 #include "ipc/Nuwa.h"
 #endif
 
 #include "gfxPrefs.h"
@@ -122,71 +123,80 @@ public:
   }
 
 private:
   nsRefPtr<Decoder> mDecoder;
 };
 
 #ifdef MOZ_NUWA_PROCESS
 
-class RIDThreadPoolListener MOZ_FINAL : public nsIThreadPoolListener
+class DecodePoolNuwaListener MOZ_FINAL : public nsIThreadPoolListener
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSITHREADPOOLLISTENER
-  
-  RIDThreadPoolListener() { }
+
+  NS_IMETHODIMP OnThreadCreated()
+  {
+    if (IsNuwaProcess()) {
+      NuwaMarkCurrentThread(static_cast<void(*)(void*)>(nullptr), nullptr);
+    }
+    return NS_OK;
+  }
+
+  NS_IMETHODIMP OnThreadShuttingDown() { return NS_OK; }
 
 private:
-  ~RIDThreadPoolListener() { }
+  ~DecodePoolNuwaListener() { }
 };
 
-NS_IMPL_ISUPPORTS(RIDThreadPoolListener, nsIThreadPoolListener)
+NS_IMPL_ISUPPORTS(DecodePoolNuwaListener, nsIThreadPoolListener)
 
-NS_IMETHODIMP
-RIDThreadPoolListener::OnThreadCreated()
+class RegisterDecodeIOThreadWithNuwaRunnable : public nsRunnable
 {
-    if (IsNuwaProcess()) {
-        NuwaMarkCurrentThread(static_cast<void(*)(void*)>(nullptr), nullptr);
-    }
+public:
+  NS_IMETHOD Run()
+  {
+    NuwaMarkCurrentThread(static_cast<void(*)(void*)>(nullptr), nullptr);
     return NS_OK;
-}
-
-NS_IMETHODIMP
-RIDThreadPoolListener::OnThreadShuttingDown()
-{
-    return NS_OK;
-}
-
+  }
+};
 #endif // MOZ_NUWA_PROCESS
 
 
 ///////////////////////////////////////////////////////////////////////////////
 // DecodePool implementation.
 ///////////////////////////////////////////////////////////////////////////////
 
 /* static */ StaticRefPtr<DecodePool> DecodePool::sSingleton;
 
 NS_IMPL_ISUPPORTS(DecodePool, nsIObserver)
 
+/* static */ void
+DecodePool::Initialize()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  DecodePool::Singleton();
+}
+
 /* static */ DecodePool*
 DecodePool::Singleton()
 {
   if (!sSingleton) {
     MOZ_ASSERT(NS_IsMainThread());
     sSingleton = new DecodePool();
     ClearOnShutdown(&sSingleton);
   }
 
   return sSingleton;
 }
 
 DecodePool::DecodePool()
-  : mThreadPoolMutex("Thread Pool")
+  : mMutex("image::DecodePool")
 {
+  // Initialize the thread pool.
   mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
   MOZ_RELEASE_ASSERT(mThreadPool,
                      "Should succeed in creating image decoding thread pool");
 
   mThreadPool->SetName(NS_LITERAL_CSTRING("ImageDecoder"));
   int32_t prefLimit = gfxPrefs::ImageMTDecodingLimit();
   uint32_t limit;
   if (prefLimit <= 0) {
@@ -195,20 +205,32 @@ DecodePool::DecodePool()
     limit = static_cast<uint32_t>(prefLimit);
   }
 
   mThreadPool->SetThreadLimit(limit);
   mThreadPool->SetIdleThreadLimit(limit);
 
 #ifdef MOZ_NUWA_PROCESS
   if (IsNuwaProcess()) {
-    mThreadPool->SetListener(new RIDThreadPoolListener());
+    mThreadPool->SetListener(new DecodePoolNuwaListener());
   }
 #endif
 
+  // Initialize the I/O thread.
+  nsresult rv = NS_NewNamedThread("ImageIO", getter_AddRefs(mIOThread));
+  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mIOThread,
+                     "Should successfully create image I/O thread");
+
+#ifdef MOZ_NUWA_PROCESS
+  nsCOMPtr<nsIRunnable> worker = new RegisterDecodeIOThreadWithNuwaRunnable();
+  rv = mIOThread->Dispatch(worker, NS_DISPATCH_NORMAL);
+  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
+                     "Should register decode IO thread with Nuwa process");
+#endif
+
   nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
   if (obsSvc) {
     obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
   }
 }
 
 DecodePool::~DecodePool()
 {
@@ -216,40 +238,45 @@ DecodePool::~DecodePool()
 }
 
 NS_IMETHODIMP
 DecodePool::Observe(nsISupports*, const char* aTopic, const char16_t*)
 {
   MOZ_ASSERT(strcmp(aTopic, "xpcom-shutdown-threads") == 0, "Unexpected topic");
 
   nsCOMPtr<nsIThreadPool> threadPool;
+  nsCOMPtr<nsIThread> ioThread;
 
   {
-    MutexAutoLock threadPoolLock(mThreadPoolMutex);
-    threadPool = mThreadPool;
-    mThreadPool = nullptr;
+    MutexAutoLock lock(mMutex);
+    threadPool.swap(mThreadPool);
+    ioThread.swap(mIOThread);
   }
 
   if (threadPool) {
     threadPool->Shutdown();
   }
 
+  if (ioThread) {
+    ioThread->Shutdown();
+  }
+
   return NS_OK;
 }
 
 void
 DecodePool::AsyncDecode(Decoder* aDecoder)
 {
   MOZ_ASSERT(aDecoder);
 
   nsCOMPtr<nsIRunnable> worker = new DecodeWorker(aDecoder);
 
   // Dispatch to the thread pool if it exists. If it doesn't, we're currently
   // shutting down, so it's OK to just drop the job on the floor.
-  MutexAutoLock threadPoolLock(mThreadPoolMutex);
+  MutexAutoLock threadPoolLock(mMutex);
   if (mThreadPool) {
     mThreadPool->Dispatch(worker, nsIEventTarget::DISPATCH_NORMAL);
   }
 }
 
 void
 DecodePool::SyncDecodeIfSmall(Decoder* aDecoder)
 {
@@ -269,21 +296,29 @@ DecodePool::SyncDecodeIfPossible(Decoder
 {
   MOZ_ASSERT(NS_IsMainThread());
   Decode(aDecoder);
 }
 
 already_AddRefed<nsIEventTarget>
 DecodePool::GetEventTarget()
 {
-  MutexAutoLock threadPoolLock(mThreadPoolMutex);
+  MutexAutoLock threadPoolLock(mMutex);
   nsCOMPtr<nsIEventTarget> target = do_QueryInterface(mThreadPool);
   return target.forget();
 }
 
+already_AddRefed<nsIEventTarget>
+DecodePool::GetIOEventTarget()
+{
+  MutexAutoLock threadPoolLock(mMutex);
+  nsCOMPtr<nsIEventTarget> target = do_QueryInterface(mIOThread);
+  return target.forget();
+}
+
 already_AddRefed<nsIRunnable>
 DecodePool::CreateDecodeWorker(Decoder* aDecoder)
 {
   MOZ_ASSERT(aDecoder);
   nsCOMPtr<nsIRunnable> worker = new DecodeWorker(aDecoder);
   return worker.forget();
 }
 
--- a/image/src/DecodePool.h
+++ b/image/src/DecodePool.h
@@ -12,16 +12,17 @@
 
 #include "mozilla/Mutex.h"
 #include "mozilla/StaticPtr.h"
 #include <mozilla/TypedEnum.h>
 #include "nsCOMPtr.h"
 #include "nsIEventTarget.h"
 #include "nsIObserver.h"
 
+class nsIThread;
 class nsIThreadPool;
 
 namespace mozilla {
 namespace image {
 
 class Decoder;
 class RasterImage;
 
@@ -36,16 +37,20 @@ class RasterImage;
  * the two.
  */
 class DecodePool : public nsIObserver
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
+  /// Initializes the singleton instance. Should be called from the main thread.
+  static void Initialize();
+
+  /// Returns the singleton instance.
   static DecodePool* Singleton();
 
   /// Ask the DecodePool to run @aDecoder asynchronously and return immediately.
   void AsyncDecode(Decoder* aDecoder);
 
   /**
    * Run @aDecoder synchronously if the image it's decoding is small. If the
    * image is too large, or if the source data isn't complete yet, run @aDecoder
@@ -65,16 +70,25 @@ public:
    * pool. Callers can use this event target to submit work to the image
    * decoding thread pool.
    *
    * @return An nsIEventTarget interface to the thread pool.
    */
   already_AddRefed<nsIEventTarget> GetEventTarget();
 
   /**
+   * Returns an event target interface to the DecodePool's I/O thread. Callers
+   * who want to deliver data to workers on the DecodePool can use this event
+   * target.
+   *
+   * @return An nsIEventTarget interface to the thread pool's I/O thread.
+   */
+  already_AddRefed<nsIEventTarget> GetIOEventTarget();
+
+  /**
    * Creates a worker which can be used to attempt further decoding using the
    * provided decoder.
    *
    * @return The new worker, which should be posted to the event target returned
    *         by GetEventTarget.
    */
   already_AddRefed<nsIRunnable> CreateDecodeWorker(Decoder* aDecoder);
 
@@ -86,19 +100,18 @@ private:
   virtual ~DecodePool();
 
   void Decode(Decoder* aDecoder);
   void NotifyDecodeComplete(Decoder* aDecoder);
   void NotifyProgress(Decoder* aDecoder);
 
   static StaticRefPtr<DecodePool> sSingleton;
 
-  // mThreadPoolMutex protects mThreadPool. For all RasterImages R,
-  // R::mDecodingMonitor must be acquired before mThreadPoolMutex
-  // if both are acquired; the other order may cause deadlock.
-  Mutex                     mThreadPoolMutex;
+  // mMutex protects mThreadPool and mIOThread.
+  Mutex                     mMutex;
   nsCOMPtr<nsIThreadPool>   mThreadPool;
+  nsCOMPtr<nsIThread>       mIOThread;
 };
 
 } // namespace image
 } // namespace mozilla
 
 #endif // MOZILLA_IMAGELIB_DECODEPOOL_H_
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -323,21 +323,28 @@ Decoder::FinishSharedDecoder()
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!HasError()) {
     FinishInternal();
   }
 }
 
 nsresult
-Decoder::AllocateFrame()
+Decoder::AllocateFrame(const nsIntSize& aTargetSize /* = nsIntSize() */)
 {
   MOZ_ASSERT(mNeedsNewFrame);
 
+  nsIntSize targetSize = aTargetSize;
+  if (targetSize == nsIntSize()) {
+    MOZ_ASSERT(HasSize());
+    targetSize = mImageMetadata.GetSize();
+  }
+
   mCurrentFrame = EnsureFrame(mNewFrameData.mFrameNum,
+                              targetSize,
                               mNewFrameData.mFrameRect,
                               mDecodeFlags,
                               mNewFrameData.mFormat,
                               mNewFrameData.mPaletteDepth,
                               mCurrentFrame.get());
 
   if (mCurrentFrame) {
     // Gather the raw pointers the decoders will use.
@@ -361,16 +368,17 @@ Decoder::AllocateFrame()
     mNeedsToFlushData = true;
   }
 
   return mCurrentFrame ? NS_OK : NS_ERROR_FAILURE;
 }
 
 RawAccessFrameRef
 Decoder::EnsureFrame(uint32_t aFrameNum,
+                     const nsIntSize& aTargetSize,
                      const nsIntRect& aFrameRect,
                      uint32_t aDecodeFlags,
                      SurfaceFormat aFormat,
                      uint8_t aPaletteDepth,
                      imgFrame* aPreviousFrame)
 {
   if (mDataError || NS_FAILED(mFailCode)) {
     return RawAccessFrameRef();
@@ -378,18 +386,18 @@ Decoder::EnsureFrame(uint32_t aFrameNum,
 
   MOZ_ASSERT(aFrameNum <= mFrameCount, "Invalid frame index!");
   if (aFrameNum > mFrameCount) {
     return RawAccessFrameRef();
   }
 
   // Adding a frame that doesn't already exist. This is the normal case.
   if (aFrameNum == mFrameCount) {
-    return InternalAddFrame(aFrameNum, aFrameRect, aDecodeFlags, aFormat,
-                            aPaletteDepth, aPreviousFrame);
+    return InternalAddFrame(aFrameNum, aTargetSize, aFrameRect, aDecodeFlags,
+                            aFormat, aPaletteDepth, aPreviousFrame);
   }
 
   // We're replacing a frame. It must be the first frame; there's no reason to
   // ever replace any other frame, since the first frame is the only one we
   // speculatively allocate without knowing what the decoder really needs.
   // XXX(seth): I'm not convinced there's any reason to support this at all. We
   // should figure out how to avoid triggering this and rip it out.
   MOZ_ASSERT(aFrameNum == 0, "Replacing a frame other than the first?");
@@ -410,70 +418,69 @@ Decoder::EnsureFrame(uint32_t aFrameNum,
 
   MOZ_ASSERT(ref, "No ref to current frame?");
 
   // Reinitialize the old frame.
   nsIntSize oldSize = ThebesIntSize(aPreviousFrame->GetImageSize());
   bool nonPremult =
     aDecodeFlags & imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
   if (NS_FAILED(aPreviousFrame->ReinitForDecoder(oldSize, aFrameRect, aFormat,
-                                               aPaletteDepth, nonPremult))) {
+                                                 aPaletteDepth, nonPremult))) {
     NS_WARNING("imgFrame::ReinitForDecoder should succeed");
     mFrameCount = 0;
     aPreviousFrame->Abort();
     return RawAccessFrameRef();
   }
 
   return ref;
 }
 
 RawAccessFrameRef
 Decoder::InternalAddFrame(uint32_t aFrameNum,
+                          const nsIntSize& aTargetSize,
                           const nsIntRect& aFrameRect,
                           uint32_t aDecodeFlags,
                           SurfaceFormat aFormat,
                           uint8_t aPaletteDepth,
                           imgFrame* aPreviousFrame)
 {
   MOZ_ASSERT(aFrameNum <= mFrameCount, "Invalid frame index!");
   if (aFrameNum > mFrameCount) {
     return RawAccessFrameRef();
   }
 
-  MOZ_ASSERT(mImageMetadata.HasSize());
-  nsIntSize imageSize(mImageMetadata.GetWidth(), mImageMetadata.GetHeight());
-  if (imageSize.width <= 0 || imageSize.height <= 0 ||
+  if (aTargetSize.width <= 0 || aTargetSize.height <= 0 ||
       aFrameRect.width <= 0 || aFrameRect.height <= 0) {
     NS_WARNING("Trying to add frame with zero or negative size");
     return RawAccessFrameRef();
   }
 
-  if (!SurfaceCache::CanHold(imageSize.ToIntSize())) {
+  if (!SurfaceCache::CanHold(aTargetSize.ToIntSize())) {
     NS_WARNING("Trying to add frame that's too large for the SurfaceCache");
     return RawAccessFrameRef();
   }
 
   nsRefPtr<imgFrame> frame = new imgFrame();
   bool nonPremult =
     aDecodeFlags & imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
-  if (NS_FAILED(frame->InitForDecoder(imageSize, aFrameRect, aFormat,
+  if (NS_FAILED(frame->InitForDecoder(aTargetSize, aFrameRect, aFormat,
                                       aPaletteDepth, nonPremult))) {
     NS_WARNING("imgFrame::Init should succeed");
     return RawAccessFrameRef();
   }
 
   RawAccessFrameRef ref = frame->RawAccessRef();
   if (!ref) {
     frame->Abort();
     return RawAccessFrameRef();
   }
 
   InsertOutcome outcome =
     SurfaceCache::Insert(frame, ImageKey(mImage.get()),
-                         RasterSurfaceKey(imageSize.ToIntSize(),
+                         RasterSurfaceKey(aTargetSize.ToIntSize(),
                                           aDecodeFlags,
                                           aFrameNum),
                          Lifetime::Persistent);
   if (outcome != InsertOutcome::SUCCESS) {
     // We either hit InsertOutcome::FAILURE, which is a temporary failure due to
     // low memory (we know it's not permanent because we checked CanHold()
     // above), or InsertOutcome::FAILURE_ALREADY_PRESENT, which means that
     // another decoder beat us to decoding this frame. Either way, we should
@@ -594,32 +601,35 @@ Decoder::PostFrameStop(Opacity aFrameOpa
 
   mCurrentFrame->Finish(aFrameOpacity, aDisposalMethod, aTimeout, aBlendMethod);
 
   mProgress |= FLAG_FRAME_COMPLETE | FLAG_ONLOAD_UNBLOCKED;
 
   // If we're not sending partial invalidations, then we send an invalidation
   // here when the first frame is complete.
   if (!mSendPartialInvalidations && !mIsAnimated) {
-    mInvalidRect.UnionRect(mInvalidRect, mCurrentFrame->GetRect());
+    mInvalidRect.UnionRect(mInvalidRect,
+                           nsIntRect(nsIntPoint(0, 0), GetSize()));
   }
 }
 
 void
-Decoder::PostInvalidation(nsIntRect& aRect)
+Decoder::PostInvalidation(const nsIntRect& aRect,
+                          const Maybe<nsIntRect>& aRectAtTargetSize
+                            /* = Nothing() */)
 {
   // We should be mid-frame
   NS_ABORT_IF_FALSE(mInFrame, "Can't invalidate when not mid-frame!");
   NS_ABORT_IF_FALSE(mCurrentFrame, "Can't invalidate when not mid-frame!");
 
   // Record this invalidation, unless we're not sending partial invalidations
   // or we're past the first frame.
   if (mSendPartialInvalidations && !mIsAnimated) {
     mInvalidRect.UnionRect(mInvalidRect, aRect);
-    mCurrentFrame->ImageUpdated(aRect);
+    mCurrentFrame->ImageUpdated(aRectAtTargetSize.valueOr(aRect));
   }
 }
 
 void
 Decoder::PostDecodeDone(int32_t aLoopCount /* = 0 */)
 {
   NS_ABORT_IF_FALSE(!IsSizeDecode(), "Can't be done with decoding with size decode!");
   NS_ABORT_IF_FALSE(!mInFrame, "Can't be done decoding if we're mid-frame!");
--- a/image/src/Decoder.h
+++ b/image/src/Decoder.h
@@ -123,16 +123,34 @@ public:
   bool IsSizeDecode() { return mSizeDecode; }
   void SetSizeDecode(bool aSizeDecode)
   {
     MOZ_ASSERT(!mInitialized, "Shouldn't be initialized yet");
     mSizeDecode = aSizeDecode;
   }
 
   /**
+   * If this decoder supports downscale-during-decode, sets the target size that
+   * this image should be decoded to.
+   *
+   * If this decoder *doesn't* support downscale-during-decode, returns
+   * NS_ERROR_NOT_AVAILABLE. If the provided size is unacceptable, returns
+   * another error.
+   *
+   * Returning NS_OK from this method is a promise that the decoder will decode
+   * the image to the requested target size unless it encounters an error.
+   *
+   * This must be called before Init() is called.
+   */
+  virtual nsresult SetTargetSize(const nsIntSize& aSize)
+  {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  /**
    * Set whether should send partial invalidations.
    *
    * If @aSend is true, we'll send partial invalidations when decoding the first
    * frame of the image, so image notifications observers will be able to
    * gradually draw in the image as it downloads.
    *
    * If @aSend is false (the default), we'll only send an invalidation when we
    * complete the first frame.
@@ -228,16 +246,22 @@ public:
   bool HasSize() const { return mImageMetadata.HasSize(); }
   void SetSizeOnImage();
 
   void SetSize(const nsIntSize& aSize, const Orientation& aOrientation)
   {
     PostSize(aSize.width, aSize.height, aOrientation);
   }
 
+  nsIntSize GetSize() const
+  {
+    MOZ_ASSERT(HasSize());
+    return mImageMetadata.GetSize();
+  }
+
   // Use HistogramCount as an invalid Histogram ID
   virtual Telemetry::ID SpeedHistogram() { return Telemetry::HistogramCount; }
 
   ImageMetadata& GetImageMetadata() { return mImageMetadata; }
 
   /**
    * Returns a weak pointer to the image associated with this decoder.
    */
@@ -254,17 +278,17 @@ public:
                     uint32_t width, uint32_t height,
                     gfx::SurfaceFormat format,
                     uint8_t palette_depth = 0);
   virtual bool NeedsNewFrame() const { return mNeedsNewFrame; }
 
 
   // Try to allocate a frame as described in mNewFrameData and return the
   // status code from that attempt. Clears mNewFrameData.
-  virtual nsresult AllocateFrame();
+  virtual nsresult AllocateFrame(const nsIntSize& aTargetSize = nsIntSize());
 
   already_AddRefed<imgFrame> GetCurrentFrame()
   {
     nsRefPtr<imgFrame> frame = mCurrentFrame.get();
     return frame.forget();
   }
 
   RawAccessFrameRef GetCurrentFrameRef()
@@ -327,19 +351,29 @@ protected:
   // Specify whether this frame is opaque as an optimization.
   // For animated images, specify the disposal, blend method and timeout for
   // this frame.
   void PostFrameStop(Opacity aFrameOpacity = Opacity::SOME_TRANSPARENCY,
                      DisposalMethod aDisposalMethod = DisposalMethod::KEEP,
                      int32_t aTimeout = 0,
                      BlendMethod aBlendMethod = BlendMethod::OVER);
 
-  // Called by the decoders when they have a region to invalidate. We may not
-  // actually pass these invalidations on right away.
-  void PostInvalidation(nsIntRect& aRect);
+  /**
+   * Called by the decoders when they have a region to invalidate. We may not
+   * actually pass these invalidations on right away.
+   *
+   * @param aRect The invalidation rect in the coordinate system of the unscaled
+   *              image (that is, the image at its intrinsic size).
+   * @param aRectAtTargetSize If not Nothing(), the invalidation rect in the
+   *                          coordinate system of the scaled image (that is,
+   *                          the image at our target decoding size). This must
+   *                          be supplied if we're downscaling during decode.
+   */
+  void PostInvalidation(const nsIntRect& aRect,
+                        const Maybe<nsIntRect>& aRectAtTargetSize = Nothing());
 
   // Called by the decoders when they have successfully decoded the image. This
   // may occur as the result of the decoder getting to the appropriate point in
   // the stream, or by us calling FinishInternal().
   //
   // May not be called mid-frame.
   //
   // For animated images, specify the loop count. -1 means loop forever, 0
@@ -356,26 +390,32 @@ protected:
   bool NeedsToFlushData() const { return mNeedsToFlushData; }
 
   /**
    * Ensures that a given frame number exists with the given parameters, and
    * returns a RawAccessFrameRef for that frame.
    * It is not possible to create sparse frame arrays; you can only append
    * frames to the current frame array, or if there is only one frame in the
    * array, replace that frame.
+   * @aTargetSize specifies the target size we're decoding to. If we're not
+   * downscaling during decode, this will always be the same as the image's
+   * intrinsic size.
+   *
    * If a non-paletted frame is desired, pass 0 for aPaletteDepth.
    */
   RawAccessFrameRef EnsureFrame(uint32_t aFrameNum,
+                                const nsIntSize& aTargetSize,
                                 const nsIntRect& aFrameRect,
                                 uint32_t aDecodeFlags,
                                 gfx::SurfaceFormat aFormat,
                                 uint8_t aPaletteDepth,
                                 imgFrame* aPreviousFrame);
 
   RawAccessFrameRef InternalAddFrame(uint32_t aFrameNum,
+                                     const nsIntSize& aTargetSize,
                                      const nsIntRect& aFrameRect,
                                      uint32_t aDecodeFlags,
                                      gfx::SurfaceFormat aFormat,
                                      uint8_t aPaletteDepth,
                                      imgFrame* aPreviousFrame);
 
   /*
    * Member variables.
--- a/image/src/DynamicImage.cpp
+++ b/image/src/DynamicImage.cpp
@@ -250,16 +250,22 @@ DynamicImage::RequestDecode()
 }
 
 NS_IMETHODIMP
 DynamicImage::StartDecoding()
 {
   return NS_OK;
 }
 
+NS_IMETHODIMP
+DynamicImage::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags)
+{
+  return NS_OK;
+}
+
 bool
 DynamicImage::IsDecoded()
 {
   return true;
 }
 
 NS_IMETHODIMP
 DynamicImage::LockImage()
--- a/image/src/Image.h
+++ b/image/src/Image.h
@@ -47,21 +47,26 @@ public:
    * INIT_FLAG_DECODE_ON_DRAW: The container should decode on draw rather than
    * decoding on load.
    *
    * INIT_FLAG_TRANSIENT: The container is likely to exist for only a short time
    * before being destroyed. (For example, containers for
    * multipart/x-mixed-replace image parts fall into this category.) If this
    * flag is set, INIT_FLAG_DISCARDABLE and INIT_FLAG_DECODE_ON_DRAW must not be
    * set.
+   *
+   * INIT_FLAG_DOWNSCALE_DURING_DECODE: The container should attempt to
+   * downscale images during decoding instead of decoding them to their
+   * intrinsic size.
    */
-  static const uint32_t INIT_FLAG_NONE           = 0x0;
-  static const uint32_t INIT_FLAG_DISCARDABLE    = 0x1;
-  static const uint32_t INIT_FLAG_DECODE_ON_DRAW = 0x2;
-  static const uint32_t INIT_FLAG_TRANSIENT      = 0x4;
+  static const uint32_t INIT_FLAG_NONE                     = 0x0;
+  static const uint32_t INIT_FLAG_DISCARDABLE              = 0x1;
+  static const uint32_t INIT_FLAG_DECODE_ON_DRAW           = 0x2;
+  static const uint32_t INIT_FLAG_TRANSIENT                = 0x4;
+  static const uint32_t INIT_FLAG_DOWNSCALE_DURING_DECODE  = 0x8;
 
   /**
    * Creates a new image container.
    *
    * @param aMimeType The mimetype of the image.
    * @param aFlags Initialization flags of the INIT_FLAG_* variety.
    */
   virtual nsresult Init(const char* aMimeType,
--- a/image/src/ImageFactory.cpp
+++ b/image/src/ImageFactory.cpp
@@ -26,24 +26,32 @@
 
 namespace mozilla {
 namespace image {
 
 /*static*/ void
 ImageFactory::Initialize()
 { }
 
+static bool
+ShouldDownscaleDuringDecode(const nsCString& aMimeType)
+{
+  // Not enabled for anything yet.
+  return false;
+}
+
 static uint32_t
-ComputeImageFlags(ImageURL* uri, bool isMultiPart)
+ComputeImageFlags(ImageURL* uri, const nsCString& aMimeType, bool isMultiPart)
 {
   nsresult rv;
 
   // We default to the static globals.
   bool isDiscardable = gfxPrefs::ImageMemDiscardable();
   bool doDecodeOnDraw = gfxPrefs::ImageMemDecodeOnDraw();
+  bool doDownscaleDuringDecode = gfxPrefs::ImageDownscaleDuringDecodeEnabled();
 
   // We want UI to be as snappy as possible and not to flicker. Disable
   // discarding and decode-on-draw for chrome URLS.
   bool isChrome = false;
   rv = uri->SchemeIs("chrome", &isChrome);
   if (NS_SUCCEEDED(rv) && isChrome) {
     isDiscardable = doDecodeOnDraw = false;
   }
@@ -51,50 +59,58 @@ ComputeImageFlags(ImageURL* uri, bool is
   // We don't want resources like the "loading" icon to be discardable or
   // decode-on-draw either.
   bool isResource = false;
   rv = uri->SchemeIs("resource", &isResource);
   if (NS_SUCCEEDED(rv) && isResource) {
     isDiscardable = doDecodeOnDraw = false;
   }
 
+  // Downscale-during-decode is only enabled for certain content types.
+  if (doDownscaleDuringDecode && !ShouldDownscaleDuringDecode(aMimeType)) {
+    doDownscaleDuringDecode = false;
+  }
+
   // For multipart/x-mixed-replace, we basically want a direct channel to the
-  // decoder. Disable both for this case as well.
+  // decoder. Disable everything for this case.
   if (isMultiPart) {
-    isDiscardable = doDecodeOnDraw = false;
+    isDiscardable = doDecodeOnDraw = doDownscaleDuringDecode = false;
   }
 
   // We have all the information we need.
   uint32_t imageFlags = Image::INIT_FLAG_NONE;
   if (isDiscardable) {
     imageFlags |= Image::INIT_FLAG_DISCARDABLE;
   }
   if (doDecodeOnDraw) {
     imageFlags |= Image::INIT_FLAG_DECODE_ON_DRAW;
   }
   if (isMultiPart) {
     imageFlags |= Image::INIT_FLAG_TRANSIENT;
   }
+  if (doDownscaleDuringDecode) {
+    imageFlags |= Image::INIT_FLAG_DOWNSCALE_DURING_DECODE;
+  }
 
   return imageFlags;
 }
 
 /* static */ already_AddRefed<Image>
 ImageFactory::CreateImage(nsIRequest* aRequest,
                           ProgressTracker* aProgressTracker,
                           const nsCString& aMimeType,
                           ImageURL* aURI,
                           bool aIsMultiPart,
                           uint32_t aInnerWindowId)
 {
   MOZ_ASSERT(gfxPrefs::SingletonExists(),
              "Pref observers should have been initialized already");
 
   // Compute the image's initialization flags.
-  uint32_t imageFlags = ComputeImageFlags(aURI, aIsMultiPart);
+  uint32_t imageFlags = ComputeImageFlags(aURI, aMimeType, aIsMultiPart);
 
   // Select the type of image to create based on MIME type.
   if (aMimeType.EqualsLiteral(IMAGE_SVG_XML)) {
     return CreateVectorImage(aRequest, aProgressTracker, aMimeType,
                              aURI, imageFlags, aInnerWindowId);
   } else {
     return CreateRasterImage(aRequest, aProgressTracker, aMimeType,
                              aURI, imageFlags, aInnerWindowId);
--- a/image/src/ImageMetadata.h
+++ b/image/src/ImageMetadata.h
@@ -48,16 +48,17 @@ public:
     }
   }
 
   bool HasSize() const { return mSize.isSome(); }
   bool HasOrientation() const { return mOrientation.isSome(); }
 
   int32_t GetWidth() const { return mSize->width; }
   int32_t GetHeight() const { return mSize->height; }
+  nsIntSize GetSize() const { return *mSize; }
   Orientation GetOrientation() const { return *mOrientation; }
 
 private:
   // The hotspot found on cursors, or -1 if none was found.
   int32_t mHotspotX;
   int32_t mHotspotY;
 
   // The loop count for animated images, or -1 for infinite loop.
--- a/image/src/ImageWrapper.cpp
+++ b/image/src/ImageWrapper.cpp
@@ -219,16 +219,22 @@ ImageWrapper::RequestDecode()
 }
 
 NS_IMETHODIMP
 ImageWrapper::StartDecoding()
 {
   return mInnerImage->StartDecoding();
 }
 
+NS_IMETHODIMP
+ImageWrapper::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags)
+{
+  return mInnerImage->RequestDecodeForSize(aSize, aFlags);
+}
+
 bool
 ImageWrapper::IsDecoded()
 {
   return mInnerImage->IsDecoded();
 }
 
 NS_IMETHODIMP
 ImageWrapper::LockImage()
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -29,16 +29,17 @@
 #include "nsJPEGDecoder.h"
 #include "nsBMPDecoder.h"
 #include "nsICODecoder.h"
 #include "nsIconDecoder.h"
 
 #include "gfxContext.h"
 
 #include "mozilla/gfx/2D.h"
+#include "mozilla/DebugOnly.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Move.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Services.h"
 #include <stdint.h>
 #include "mozilla/Telemetry.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/ClearOnShutdown.h"
@@ -292,24 +293,16 @@ RasterImage::~RasterImage()
   if (!mSourceBuffer->IsComplete()) {
     mSourceBuffer->Complete(NS_ERROR_ABORT);
   }
 
   // Release all frames from the surface cache.
   SurfaceCache::RemoveImage(ImageKey(this));
 }
 
-/* static */ void
-RasterImage::Initialize()
-{
-  // Create our singletons now, so we don't have to worry about what thread
-  // they're created on.
-  DecodePool::Singleton();
-}
-
 nsresult
 RasterImage::Init(const char* aMimeType,
                   uint32_t aFlags)
 {
   // We don't support re-initialization
   if (mInitialized)
     return NS_ERROR_ILLEGAL_VALUE;
 
@@ -318,33 +311,39 @@ RasterImage::Init(const char* aMimeType,
     return NS_ERROR_FAILURE;
 
   NS_ENSURE_ARG_POINTER(aMimeType);
 
   // We must be non-discardable and non-decode-on-draw for
   // transient images.
   MOZ_ASSERT(!(aFlags & INIT_FLAG_TRANSIENT) ||
                (!(aFlags & INIT_FLAG_DISCARDABLE) &&
-                !(aFlags & INIT_FLAG_DECODE_ON_DRAW)),
-             "Transient images can't be discardable or decode-on-draw");
+                !(aFlags & INIT_FLAG_DECODE_ON_DRAW) &&
+                !(aFlags & INIT_FLAG_DOWNSCALE_DURING_DECODE)),
+             "Illegal init flags for transient image");
 
   // Store initialization data
   mSourceDataMimeType.Assign(aMimeType);
   mDiscardable = !!(aFlags & INIT_FLAG_DISCARDABLE);
   mDecodeOnDraw = !!(aFlags & INIT_FLAG_DECODE_ON_DRAW);
   mTransient = !!(aFlags & INIT_FLAG_TRANSIENT);
+  mDownscaleDuringDecode = !!(aFlags & INIT_FLAG_DOWNSCALE_DURING_DECODE);
+
+#ifndef MOZ_ENABLE_SKIA
+  // Downscale-during-decode requires Skia.
+  mDownscaleDuringDecode = false;
+#endif
 
   // Lock this image's surfaces in the SurfaceCache if we're not discardable.
   if (!mDiscardable) {
     SurfaceCache::LockImage(ImageKey(this));
   }
 
   // Create the initial size decoder.
-  nsresult rv = Decode(DecodeStrategy::ASYNC, DECODE_FLAGS_DEFAULT,
-                       /* aDoSizeDecode = */ true);
+  nsresult rv = Decode(DecodeStrategy::ASYNC, Nothing(), DECODE_FLAGS_DEFAULT);
   if (NS_FAILED(rv)) {
     return NS_ERROR_FAILURE;
   }
 
   // Mark us as initialized
   mInitialized = true;
 
   return NS_OK;
@@ -466,69 +465,91 @@ RasterImage::GetType(uint16_t *aType)
 NS_IMETHODIMP_(uint16_t)
 RasterImage::GetType()
 {
   return imgIContainer::TYPE_RASTER;
 }
 
 DrawableFrameRef
 RasterImage::LookupFrameInternal(uint32_t aFrameNum,
-                                 const nsIntSize& aSize,
+                                 const IntSize& aSize,
                                  uint32_t aFlags)
 {
   if (!mAnim) {
     NS_ASSERTION(aFrameNum == 0,
                  "Don't ask for a frame > 0 if we're not animated!");
     aFrameNum = 0;
   }
 
   if (mAnim && aFrameNum > 0) {
     MOZ_ASSERT(DecodeFlags(aFlags) == DECODE_FLAGS_DEFAULT,
                "Can't composite frames with non-default decode flags");
     return mAnim->GetCompositedFrame(aFrameNum);
   }
 
-  return SurfaceCache::Lookup(ImageKey(this),
-                              RasterSurfaceKey(aSize.ToIntSize(),
-                                               DecodeFlags(aFlags),
-                                               aFrameNum));
+  Maybe<uint32_t> alternateFlags;
+  if (IsOpaque()) {
+    // If we're opaque, we can always substitute a frame that was decoded with a
+    // different decode flag for premultiplied alpha, because that can only
+    // matter for frames with transparency.
+    alternateFlags = Some(aFlags ^ FLAG_DECODE_NO_PREMULTIPLY_ALPHA);
+  }
+
+  // We don't want any substitution for sync decodes (except the premultiplied
+  // alpha optimization above), so we use SurfaceCache::Lookup in this case.
+  if (aFlags & FLAG_SYNC_DECODE) {
+    return SurfaceCache::Lookup(ImageKey(this),
+                                RasterSurfaceKey(aSize,
+                                                 DecodeFlags(aFlags),
+                                                 aFrameNum),
+                                alternateFlags);
+  }
+
+  // We'll return the best match we can find to the requested frame.
+  return SurfaceCache::LookupBestMatch(ImageKey(this),
+                                       RasterSurfaceKey(aSize,
+                                                        DecodeFlags(aFlags),
+                                                        aFrameNum),
+                                       alternateFlags);
 }
 
 DrawableFrameRef
 RasterImage::LookupFrame(uint32_t aFrameNum,
                          const nsIntSize& aSize,
                          uint32_t aFlags,
                          bool aShouldSyncNotify /* = true */)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  DrawableFrameRef ref = LookupFrameInternal(aFrameNum, aSize, aFlags);
+  IntSize requestedSize = CanDownscaleDuringDecode(aSize, aFlags)
+                        ? aSize.ToIntSize()
+                        : mSize.ToIntSize();
+
+  DrawableFrameRef ref = LookupFrameInternal(aFrameNum, requestedSize, aFlags);
+
+  if (!ref && !mHasSize) {
+    // We can't request a decode without knowing our intrinsic size. Give up.
+    return DrawableFrameRef();
+  }
 
-  if (!ref && IsOpaque() && aFrameNum == 0) {
-    // We can use non-premultiplied alpha frames when premultipled alpha is
-    // requested, or vice versa, if this image is opaque. Try again with the bit
-    // toggled.
-    ref = LookupFrameInternal(aFrameNum, aSize,
-                              aFlags ^ FLAG_DECODE_NO_PREMULTIPLY_ALPHA);
+  if (!ref || ref->GetImageSize() != requestedSize) {
+    // The OS threw this frame away. We need to redecode if we can.
+    MOZ_ASSERT(!mAnim, "Animated frames should be locked");
+
+    WantDecodedFrames(ThebesIntSize(requestedSize), aFlags, aShouldSyncNotify);
+
+    // If we can sync decode, we should already have the frame.
+    if ((aFlags & FLAG_SYNC_DECODE) && aShouldSyncNotify) {
+      ref = LookupFrameInternal(aFrameNum, requestedSize, aFlags);
+    }
   }
 
   if (!ref) {
-    // The OS threw this frame away. We need to redecode if we can.
-    MOZ_ASSERT(!mAnim, "Animated frames should be locked");
-
-    WantDecodedFrames(aFlags, aShouldSyncNotify);
-
-    // If we were able to sync decode, we should already have the frame. If we
-    // had to decode asynchronously, maybe we've gotten lucky.
-    ref = LookupFrameInternal(aFrameNum, aSize, aFlags);
-
-    if (!ref) {
-      // We didn't successfully redecode, so just fail.
-      return DrawableFrameRef();
-    }
+    // We still weren't able to get a frame. Give up.
+    return DrawableFrameRef();
   }
 
   if (ref->GetCompositingFailed()) {
     return DrawableFrameRef();
   }
 
   MOZ_ASSERT(!ref || !ref->GetIsPaletted(), "Should not have paletted frame");
 
@@ -1125,18 +1146,17 @@ RasterImage::OnImageDataComplete(nsIRequ
 
   // Let decoders know that there won't be any more data coming.
   mSourceBuffer->Complete(aStatus);
 
   if (!mHasSize) {
     // We need to guarantee that we've gotten the image's size, or at least
     // determined that we won't be able to get it, before we deliver the load
     // event. That means we have to do a synchronous size decode here.
-    Decode(DecodeStrategy::SYNC_IF_POSSIBLE, DECODE_FLAGS_DEFAULT,
-           /* aDoSizeDecode = */ true);
+    Decode(DecodeStrategy::SYNC_IF_POSSIBLE, Nothing(), DECODE_FLAGS_DEFAULT);
   }
 
   // Determine our final status, giving precedence to Necko failure codes. We
   // check after running the size decode above in case it triggered an error.
   nsresult finalStatus = mError ? NS_ERROR_FAILURE : NS_OK;
   if (NS_FAILED(aStatus)) {
     finalStatus = aStatus;
   }
@@ -1252,23 +1272,26 @@ RasterImage::Discard()
 bool
 RasterImage::CanDiscard() {
   return mHasSourceData &&       // ...have the source data...
          !mAnim;                 // Can never discard animated images
 }
 
 // Sets up a decoder for this image.
 already_AddRefed<Decoder>
-RasterImage::CreateDecoder(bool aDoSizeDecode, uint32_t aFlags)
+RasterImage::CreateDecoder(const Maybe<nsIntSize>& aSize, uint32_t aFlags)
 {
   // Make sure we actually get size before doing a full decode.
-  if (aDoSizeDecode) {
+  if (aSize) {
+    MOZ_ASSERT(mHasSize, "Must do a size decode before a full decode!");
+    MOZ_ASSERT(mDownscaleDuringDecode || *aSize == mSize,
+               "Can only decode to our intrinsic size if we're not allowed to "
+               "downscale-during-decode");
+  } else {
     MOZ_ASSERT(!mHasSize, "Should not do unnecessary size decodes");
-  } else {
-    MOZ_ASSERT(mHasSize, "Must do a size decode before a full decode!");
   }
 
   // Figure out which decoder we want.
   eDecoderType type = GetDecoderType(mSourceDataMimeType.get());
   if (type == eDecoderType_unknown) {
     return nullptr;
   }
 
@@ -1299,37 +1322,48 @@ RasterImage::CreateDecoder(bool aDoSizeD
       break;
     default:
       MOZ_ASSERT_UNREACHABLE("Unknown decoder type");
   }
 
   MOZ_ASSERT(decoder, "Should have a decoder now");
 
   // Initialize the decoder.
-  decoder->SetSizeDecode(aDoSizeDecode);
+  decoder->SetSizeDecode(!aSize);
   decoder->SetSendPartialInvalidations(!mHasBeenDecoded);
   decoder->SetImageIsTransient(mTransient);
   decoder->SetDecodeFlags(DecodeFlags(aFlags));
-  if (!aDoSizeDecode) {
+  if (aSize) {
     // We already have the size; tell the decoder so it can preallocate a
     // frame.  By default, we create an ARGB frame with no offset. If decoders
     // need a different type, they need to ask for it themselves.
+    // XXX(seth): Note that we call SetSize() and NeedNewFrame() with the
+    // image's intrinsic size, but AllocateFrame with our target size.
     decoder->SetSize(mSize, mOrientation);
-    decoder->NeedNewFrame(0, 0, 0, mSize.width, mSize.height,
+    decoder->NeedNewFrame(0, 0, 0, aSize->width, aSize->height,
                           SurfaceFormat::B8G8R8A8);
-    decoder->AllocateFrame();
+    decoder->AllocateFrame(*aSize);
   }
   decoder->SetIterator(mSourceBuffer->Iterator());
+
+  // Set a target size for downscale-during-decode if applicable.
+  if (mDownscaleDuringDecode && aSize && *aSize != mSize) {
+    DebugOnly<nsresult> rv = decoder->SetTargetSize(*aSize);
+    MOZ_ASSERT(nsresult(rv) != NS_ERROR_NOT_AVAILABLE,
+               "We're downscale-during-decode but decoder doesn't support it?");
+    MOZ_ASSERT(NS_SUCCEEDED(rv), "Bad downscale-during-decode target size?");
+  }
+
   decoder->Init();
 
   if (NS_FAILED(decoder->GetDecoderError())) {
     return nullptr;
   }
 
-  if (!aDoSizeDecode) {
+  if (!aSize) {
     Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Subtract(mDecodeCount);
     mDecodeCount++;
     Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(mDecodeCount);
 
     if (mDecodeCount > sMaxDecodeCount) {
       // Don't subtract out 0 from the histogram, because that causes its count
       // to go negative, which is not kosher.
       if (sMaxDecodeCount > 0) {
@@ -1339,121 +1373,124 @@ RasterImage::CreateDecoder(bool aDoSizeD
       Telemetry::GetHistogramById(Telemetry::IMAGE_MAX_DECODE_COUNT)->Add(sMaxDecodeCount);
     }
   }
 
   return decoder.forget();
 }
 
 void
-RasterImage::WantDecodedFrames(uint32_t aFlags, bool aShouldSyncNotify)
+RasterImage::WantDecodedFrames(const nsIntSize& aSize, uint32_t aFlags,
+                               bool aShouldSyncNotify)
 {
   if (aShouldSyncNotify) {
     // We can sync notify, which means we can also sync decode.
     if (aFlags & FLAG_SYNC_DECODE) {
-      Decode(DecodeStrategy::SYNC_IF_POSSIBLE, aFlags);
+      Decode(DecodeStrategy::SYNC_IF_POSSIBLE, Some(aSize), aFlags);
       return;
     }
 
     // Here we are explicitly trading off flashing for responsiveness in the
     // case that we're redecoding an image (see bug 845147).
     Decode(mHasBeenDecoded ? DecodeStrategy::ASYNC
                            : DecodeStrategy::SYNC_FOR_SMALL_IMAGES,
-           aFlags);
+           Some(aSize), aFlags);
     return;
   }
 
   // We can't sync notify, so do an async decode.
-  Decode(DecodeStrategy::ASYNC, aFlags);
+  Decode(DecodeStrategy::ASYNC, Some(aSize), aFlags);
 }
 
 //******************************************************************************
 /* void requestDecode() */
 NS_IMETHODIMP
 RasterImage::RequestDecode()
 {
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (mError) {
-    return NS_ERROR_FAILURE;
-  }
-  if (!mHasSize) {
-    mWantFullDecode = true;
-    return NS_OK;
-  }
-
-  // Look up the first frame of the image, which will implicitly start decoding
-  // if it's not available right now.
-  // XXX(seth): Passing false for aShouldSyncNotify here has the effect of
-  // decoding asynchronously, but that's not obvious from the argument name.
-  // This API needs to be reworked.
-  LookupFrame(0, mSize, DECODE_FLAGS_DEFAULT, /* aShouldSyncNotify = */ false);
-
-  return NS_OK;
+  return RequestDecodeForSize(mSize, DECODE_FLAGS_DEFAULT);
 }
 
 /* void startDecode() */
 NS_IMETHODIMP
 RasterImage::StartDecoding()
 {
   if (!NS_IsMainThread()) {
     return NS_DispatchToMainThread(
       NS_NewRunnableMethod(this, &RasterImage::StartDecoding));
   }
 
+  return RequestDecodeForSize(mSize, FLAG_SYNC_DECODE);
+}
+
+NS_IMETHODIMP
+RasterImage::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
   if (mError) {
     return NS_ERROR_FAILURE;
   }
+
   if (!mHasSize) {
     mWantFullDecode = true;
     return NS_OK;
   }
 
+  // Fall back to our intrinsic size if we don't support
+  // downscale-during-decode.
+  nsIntSize targetSize = mDownscaleDuringDecode ? aSize : mSize;
+
+  // Sync decode small images if requested.
+  bool shouldSyncDecodeSmallImages = aFlags & FLAG_SYNC_DECODE;
+
   // Look up the first frame of the image, which will implicitly start decoding
   // if it's not available right now.
   // XXX(seth): Passing true for aShouldSyncNotify here has the effect of
-  // synchronously decoding small images, but that's not obvious from the
-  // argument name. This API needs to be reworked.
-  LookupFrame(0, mSize, DECODE_FLAGS_DEFAULT, /* aShouldSyncNotify = */ true);
+  // synchronously decoding small images, while passing false has the effect of
+  // decoding asynchronously, but that's not obvious from the argument name.
+  // This API needs to be reworked.
+  LookupFrame(0, targetSize, DecodeFlags(aFlags),
+              /* aShouldSyncNotify = */ shouldSyncDecodeSmallImages);
 
   return NS_OK;
 }
 
 bool
 RasterImage::IsDecoded()
 {
   // XXX(seth): We need to get rid of this; it's not reliable.
   return mHasBeenDecoded || mError;
 }
 
 NS_IMETHODIMP
 RasterImage::Decode(DecodeStrategy aStrategy,
-                    uint32_t aFlags,
-                    bool aDoSizeDecode /* = false */)
+                    const Maybe<nsIntSize>& aSize,
+                    uint32_t aFlags)
 {
-  MOZ_ASSERT(aDoSizeDecode || NS_IsMainThread());
+  MOZ_ASSERT(!aSize || NS_IsMainThread());
 
   if (mError) {
     return NS_ERROR_FAILURE;
   }
 
   // If we don't have a size yet, we can't do any other decoding.
-  if (!mHasSize && !aDoSizeDecode) {
+  if (!mHasSize && aSize) {
     mWantFullDecode = true;
     return NS_OK;
   }
 
   // Create a decoder.
-  nsRefPtr<Decoder> decoder = CreateDecoder(aDoSizeDecode, aFlags);
+  nsRefPtr<Decoder> decoder = CreateDecoder(aSize, aFlags);
   if (!decoder) {
     return NS_ERROR_FAILURE;
   }
 
-  if (!aDoSizeDecode) {
-    // Send out early notifications right away.
+  if (aSize) {
+    // This isn't a size decode (which doesn't send any early notifications), so
+    // send out notifications right away.
     NotifyProgress(decoder->TakeProgress(),
                    decoder->TakeInvalidRect(),
                    decoder->GetDecodeFlags());
 
     // Lock the image while we're decoding, so that it doesn't get evicted from
     // the SurfaceCache before we have a chance to realize that it's animated.
     // The corresponding unlock happens in FinalizeDecoder.
     LockImage();
@@ -1498,16 +1535,21 @@ RasterImage::CanScale(GraphicsFilter aFi
   // we usually have no way of updating what we've drawn, so HQ scaling is
   // useless.
   if (!gfxPrefs::ImageHQDownscalingEnabled() || !mHasSize || !mHasSourceData ||
       !(aFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING) ||
       aFilter != GraphicsFilter::FILTER_GOOD) {
     return false;
   }
 
+  // We don't HQ scale images that we can downscale during decode.
+  if (mDownscaleDuringDecode) {
+    return false;
+  }
+
   // We don't use the scaler for animated or transient images to avoid doing a
   // bunch of work on an image that just gets thrown away.
   if (mAnim || mTransient) {
     return false;
   }
 
   // If target size is 1:1 with original, don't scale.
   if (aSize == mSize) {
@@ -1533,16 +1575,50 @@ RasterImage::CanScale(GraphicsFilter aFi
   // If that's all it's getting us, I'd rather we just forbid that explicitly.
   gfx::Size scale(double(aSize.width) / mSize.width,
                   double(aSize.height) / mSize.height);
   gfxFloat minFactor = gfxPrefs::ImageHQDownscalingMinFactor() / 1000.0;
   return (scale.width < minFactor || scale.height < minFactor);
 #endif
 }
 
+bool
+RasterImage::CanDownscaleDuringDecode(const nsIntSize& aSize, uint32_t aFlags)
+{
+  // Check basic requirements: downscale-during-decode is enabled for this
+  // image, we have all the source data and know our size, the flags allow us to
+  // do it, and a 'good' filter is being used.
+  if (!mDownscaleDuringDecode || !mHasSize ||
+      !(aFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING)) {
+    return false;
+  }
+
+  // We don't downscale animated images during decode.
+  if (mAnim) {
+    return false;
+  }
+
+  // Never upscale.
+  if (aSize.width >= mSize.width || aSize.height >= mSize.height) {
+    return false;
+  }
+
+  // Zero or negative width or height is unacceptable.
+  if (aSize.width < 1 || aSize.height < 1) {
+    return false;
+  }
+
+  // There's no point in scaling if we can't store the result.
+  if (!SurfaceCache::CanHold(aSize.ToIntSize())) {
+    return false;
+  }
+
+  return true;
+}
+
 void
 RasterImage::NotifyNewScaledFrame()
 {
   // Send an invalidation so observers will repaint and can take advantage of
   // the new scaled frame if possible.
   NotifyProgress(NoProgress, nsIntRect(0, 0, mSize.width, mSize.height));
 }
 
@@ -1657,36 +1733,37 @@ RasterImage::Draw(gfxContext* aContext,
     return NS_ERROR_FAILURE;
 
   NS_ENSURE_ARG_POINTER(aContext);
 
   if (IsUnlocked() && mProgressTracker) {
     mProgressTracker->OnUnlockedDraw();
   }
 
-  // XXX(seth): For now, we deliberately don't look up a frame of size aSize
-  // (though DrawWithPreDownscaleIfNeeded will do so later). It doesn't make
-  // sense to do so until we support downscale-during-decode. Right now we need
-  // to make sure that we always touch an mSize-sized frame so that we have
-  // something to HQ scale.
+  // If we're not using GraphicsFilter::FILTER_GOOD, we shouldn't high-quality
+  // scale or downscale during decode.
+  uint32_t flags = aFilter == GraphicsFilter::FILTER_GOOD
+                 ? aFlags
+                 : aFlags & ~FLAG_HIGH_QUALITY_SCALING;
+
   DrawableFrameRef ref =
-    LookupFrame(GetRequestedFrameIndex(aWhichFrame), mSize, aFlags);
+    LookupFrame(GetRequestedFrameIndex(aWhichFrame), aSize, flags);
   if (!ref) {
     // Getting the frame (above) touches the image and kicks off decoding.
     if (mDrawStartTime.IsNull()) {
       mDrawStartTime = TimeStamp::Now();
     }
     return NS_OK;
   }
 
   bool shouldRecordTelemetry = !mDrawStartTime.IsNull() &&
                                ref->IsImageComplete();
 
   DrawWithPreDownscaleIfNeeded(Move(ref), aContext, aSize,
-                               aRegion, aFilter, aFlags);
+                               aRegion, aFilter, flags);
 
   if (shouldRecordTelemetry) {
       TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
       Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY,
                             int32_t(drawLatency.ToMicroseconds()));
       mDrawStartTime = TimeStamp();
   }
 
@@ -1941,17 +2018,20 @@ RasterImage::OptimalImageSizeForDest(con
              "Unexpected destination size");
 
   if (mSize.IsEmpty() || aDest.IsEmpty()) {
     return nsIntSize(0, 0);
   }
 
   nsIntSize destSize(ceil(aDest.width), ceil(aDest.height));
 
-  if (CanScale(aFilter, destSize, aFlags)) {
+  if (aFilter == GraphicsFilter::FILTER_GOOD &&
+      CanDownscaleDuringDecode(destSize, aFlags)) {
+    return destSize;
+  } else if (CanScale(aFilter, destSize, aFlags)) {
     DrawableFrameRef frameRef =
       SurfaceCache::Lookup(ImageKey(this),
                            RasterSurfaceKey(destSize.ToIntSize(),
                                             DecodeFlags(aFlags),
                                             0));
 
     if (frameRef && frameRef->IsImageComplete()) {
         return destSize;  // We have an existing HQ scale for this size.
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -275,35 +275,33 @@ public:
  nsCString GetURIString() {
     nsCString spec;
     if (GetURI()) {
       GetURI()->GetSpec(spec);
     }
     return spec;
   }
 
-  static void Initialize();
-
 private:
   void DrawWithPreDownscaleIfNeeded(DrawableFrameRef&& aFrameRef,
                                     gfxContext* aContext,
                                     const nsIntSize& aSize,
                                     const ImageRegion& aRegion,
                                     GraphicsFilter aFilter,
                                     uint32_t aFlags);
 
   TemporaryRef<gfx::SourceSurface> CopyFrame(uint32_t aWhichFrame,
                                              uint32_t aFlags,
                                              bool aShouldSyncNotify = true);
   TemporaryRef<gfx::SourceSurface> GetFrameInternal(uint32_t aWhichFrame,
                                                     uint32_t aFlags,
                                                     bool aShouldSyncNotify = true);
 
   DrawableFrameRef LookupFrameInternal(uint32_t aFrameNum,
-                                       const nsIntSize& aSize,
+                                       const gfx::IntSize& aSize,
                                        uint32_t aFlags);
   DrawableFrameRef LookupFrame(uint32_t aFrameNum,
                                const nsIntSize& aSize,
                                uint32_t aFlags,
                                bool aShouldSyncNotify = true);
   uint32_t GetCurrentFrameIndex() const;
   uint32_t GetRequestedFrameIndex(uint32_t aWhichFrame) const;
 
@@ -322,22 +320,36 @@ private:
   // that case we use our animation consumers count as a proxy for lock count.
   bool IsUnlocked() { return (mLockCount == 0 || (mAnim && mAnimationConsumers == 0)); }
 
 
   //////////////////////////////////////////////////////////////////////////////
   // Decoding.
   //////////////////////////////////////////////////////////////////////////////
 
-  already_AddRefed<Decoder> CreateDecoder(bool aDoSizeDecode, uint32_t aFlags);
+  /**
+   * Creates and runs a decoder, either synchronously or asynchronously
+   * according to @aStrategy. Passes the provided target size @aSize and decode
+   * flags @aFlags to CreateDecoder. If a size decode is desired, pass Nothing
+   * for @aSize.
+   */
+  NS_IMETHOD Decode(DecodeStrategy aStrategy,
+                    const Maybe<nsIntSize>& aSize,
+                    uint32_t aFlags);
 
-  void WantDecodedFrames(uint32_t aFlags, bool aShouldSyncNotify);
+  /**
+   * Creates a new decoder with a target size of @aSize and decode flags
+   * specified by @aFlags. If a size decode is desired, pass Nothing() for
+   * @aSize.
+   */
+  already_AddRefed<Decoder> CreateDecoder(const Maybe<nsIntSize>& aSize,
+                                          uint32_t aFlags);
 
-  NS_IMETHOD Decode(DecodeStrategy aStrategy, uint32_t aFlags,
-                    bool aDoSizeDecode = false);
+  void WantDecodedFrames(const nsIntSize& aSize, uint32_t aFlags,
+                         bool aShouldSyncNotify);
 
 private: // data
   nsIntSize                  mSize;
   Orientation                mOrientation;
 
   nsCOMPtr<nsIProperties>   mProperties;
 
   /// If this image is animated, a FrameAnimator which manages its animation.
@@ -375,16 +387,17 @@ private: // data
 
   // Boolean flags (clustered together to conserve space):
   bool                       mHasSize:1;       // Has SetSize() been called?
   bool                       mDecodeOnDraw:1;  // Decoding on draw?
   bool                       mTransient:1;     // Is the image short-lived?
   bool                       mDiscardable:1;   // Is container discardable?
   bool                       mHasSourceData:1; // Do we have source data?
   bool                       mHasBeenDecoded:1; // Decoded at least once?
+  bool                       mDownscaleDuringDecode:1;
 
   // Whether we're waiting to start animation. If we get a StartAnimation() call
   // but we don't yet have more than one frame, mPendingAnimation is set so that
   // we know to start animation later if/when we have more frames.
   bool                       mPendingAnimation:1;
 
   // Whether the animation can stop, due to running out
   // of frames, or no more owning request
@@ -405,16 +418,19 @@ private: // data
   //////////////////////////////////////////////////////////////////////////////
 
   // Initiates an HQ scale for the given frame, if possible.
   void RequestScale(imgFrame* aFrame, uint32_t aFlags, const nsIntSize& aSize);
 
   // Determines whether we can perform an HQ scale with the given parameters.
   bool CanScale(GraphicsFilter aFilter, const nsIntSize& aSize, uint32_t aFlags);
 
+  // Determines whether we can downscale during decode with the given parameters.
+  bool CanDownscaleDuringDecode(const nsIntSize& aSize, uint32_t aFlags);
+
   // Called by the HQ scaler when a new scaled frame is ready.
   void NotifyNewScaledFrame();
 
   friend class ScaleRunner;
 
 
   // Error handling.
   void DoError();
--- a/image/src/SurfaceCache.cpp
+++ b/image/src/SurfaceCache.cpp
@@ -243,25 +243,111 @@ public:
 
   already_AddRefed<CachedSurface> Lookup(const SurfaceKey& aSurfaceKey)
   {
     nsRefPtr<CachedSurface> surface;
     mSurfaces.Get(aSurfaceKey, getter_AddRefs(surface));
     return surface.forget();
   }
 
+  already_AddRefed<CachedSurface>
+  LookupBestMatch(const SurfaceKey&      aSurfaceKey,
+                  const Maybe<uint32_t>& aAlternateFlags)
+  {
+    // Try for a perfect match first.
+    nsRefPtr<CachedSurface> surface;
+    mSurfaces.Get(aSurfaceKey, getter_AddRefs(surface));
+    if (surface) {
+      return surface.forget();
+    }
+
+    // There's no perfect match, so find the best match we can.
+    MatchContext matchContext(aSurfaceKey, aAlternateFlags);
+    ForEach(TryToImproveMatch, &matchContext);
+    return matchContext.mBestMatch.forget();
+  }
+
   void ForEach(SurfaceTable::EnumReadFunction aFunction, void* aData)
   {
     mSurfaces.EnumerateRead(aFunction, aData);
   }
 
   void SetLocked(bool aLocked) { mLocked = aLocked; }
   bool IsLocked() const { return mLocked; }
 
 private:
+  struct MatchContext
+  {
+    MatchContext(const SurfaceKey& aIdealKey,
+                 const Maybe<uint32_t>& aAlternateFlags)
+      : mIdealKey(aIdealKey)
+      , mAlternateFlags(aAlternateFlags)
+    { }
+
+    const SurfaceKey& mIdealKey;
+    const Maybe<uint32_t> mAlternateFlags;
+    nsRefPtr<CachedSurface> mBestMatch;
+  };
+
+  static PLDHashOperator TryToImproveMatch(const SurfaceKey& aSurfaceKey,
+                                           CachedSurface*    aSurface,
+                                           void*             aContext)
+  {
+    auto context = static_cast<MatchContext*>(aContext);
+    const SurfaceKey& idealKey = context->mIdealKey;
+
+    // Matching the animation time and SVG context is required.
+    if (aSurfaceKey.AnimationTime() != idealKey.AnimationTime() ||
+        aSurfaceKey.SVGContext() != idealKey.SVGContext()) {
+      return PL_DHASH_NEXT;
+    }
+
+    // Matching the flags is required, but we can match the alternate flags as
+    // well if some were provided.
+    if (aSurfaceKey.Flags() != idealKey.Flags() &&
+        Some(aSurfaceKey.Flags()) != context->mAlternateFlags) {
+      return PL_DHASH_NEXT;
+    }
+
+    // Anything is better than nothing! (Within the constraints we just
+    // checked, of course.)
+    if (!context->mBestMatch) {
+      context->mBestMatch = aSurface;
+      return PL_DHASH_NEXT;
+    }
+
+    MOZ_ASSERT(context->mBestMatch, "Should have a current best match");
+    SurfaceKey bestMatchKey = context->mBestMatch->GetSurfaceKey();
+
+    // Compare sizes. We use an area-based heuristic here instead of computing a
+    // truly optimal answer, since it seems very unlikely to make a difference
+    // for realistic sizes.
+    int64_t idealArea = idealKey.Size().width * idealKey.Size().height;
+    int64_t surfaceArea = aSurfaceKey.Size().width * aSurfaceKey.Size().height;
+    int64_t bestMatchArea =
+      bestMatchKey.Size().width * bestMatchKey.Size().height;
+
+    // If the best match is smaller than the ideal size, prefer bigger sizes.
+    if (bestMatchArea < idealArea) {
+      if (surfaceArea > bestMatchArea) {
+        context->mBestMatch = aSurface;
+      }
+      return PL_DHASH_NEXT;
+    }
+
+    // Other, prefer sizes closer to the ideal size, but still not smaller.
+    if (idealArea <= surfaceArea && surfaceArea < bestMatchArea) {
+      context->mBestMatch = aSurface;
+      return PL_DHASH_NEXT;
+    }
+
+    // This surface isn't an improvement over the current best match.
+    return PL_DHASH_NEXT;
+  }
+
   SurfaceTable mSurfaces;
   bool         mLocked;
 };
 
 /**
  * SurfaceCacheImpl is responsible for determining which surfaces will be cached
  * and managing the surface cache data structures. Rather than interact with
  * SurfaceCacheImpl directly, client code interacts with SurfaceCache, which
@@ -448,16 +534,55 @@ public:
 
     if (!surface->IsLocked()) {
       mExpirationTracker.MarkUsed(surface);
     }
 
     return ref;
   }
 
+  DrawableFrameRef LookupBestMatch(const ImageKey         aImageKey,
+                                   const SurfaceKey&      aSurfaceKey,
+                                   const Maybe<uint32_t>& aAlternateFlags)
+  {
+    nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
+    if (!cache)
+      return DrawableFrameRef();  // No cached surfaces for this image.
+
+    // Repeatedly look up the best match, trying again if the resulting surface
+    // has been freed by the operating system, until we can either lock a
+    // surface for drawing or there are no matching surfaces left.
+    // XXX(seth): This is O(N^2), but N is expected to be very small. If we
+    // encounter a performance problem here we can revisit this.
+
+    nsRefPtr<CachedSurface> surface;
+    DrawableFrameRef ref;
+    while (true) {
+      surface = cache->LookupBestMatch(aSurfaceKey, aAlternateFlags);
+      if (!surface) {
+        return DrawableFrameRef();  // Lookup in the per-image cache missed.
+      }
+
+      ref = surface->DrawableRef();
+      if (ref) {
+        break;
+      }
+
+      // The surface was released by the operating system. Remove the cache
+      // entry as well.
+      Remove(surface);
+    }
+
+    if (!surface->IsLocked()) {
+      mExpirationTracker.MarkUsed(surface);
+    }
+
+    return ref;
+  }
+
   void RemoveSurface(const ImageKey    aImageKey,
                      const SurfaceKey& aSurfaceKey)
   {
     nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
     if (!cache)
       return;  // No cached surfaces for this image.
 
     nsRefPtr<CachedSurface> surface = cache->Lookup(aSurfaceKey);
@@ -782,25 +907,47 @@ SurfaceCache::Initialize()
 SurfaceCache::Shutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(sInstance, "No singleton - was Shutdown() called twice?");
   sInstance = nullptr;
 }
 
 /* static */ DrawableFrameRef
-SurfaceCache::Lookup(const ImageKey    aImageKey,
-                     const SurfaceKey& aSurfaceKey)
+SurfaceCache::Lookup(const ImageKey         aImageKey,
+                     const SurfaceKey&      aSurfaceKey,
+                     const Maybe<uint32_t>& aAlternateFlags /* = Nothing() */)
 {
   if (!sInstance) {
     return DrawableFrameRef();
   }
 
   MutexAutoLock lock(sInstance->GetMutex());
-  return sInstance->Lookup(aImageKey, aSurfaceKey);
+
+  DrawableFrameRef ref = sInstance->Lookup(aImageKey, aSurfaceKey);
+  if (!ref && aAlternateFlags) {
+    ref = sInstance->Lookup(aImageKey,
+                            aSurfaceKey.WithNewFlags(*aAlternateFlags));
+  }
+
+  return ref;
+}
+
+/* static */ DrawableFrameRef
+SurfaceCache::LookupBestMatch(const ImageKey         aImageKey,
+                              const SurfaceKey&      aSurfaceKey,
+                              const Maybe<uint32_t>& aAlternateFlags
+                                /* = Nothing() */)
+{
+  if (!sInstance) {
+    return DrawableFrameRef();
+  }
+
+  MutexAutoLock lock(sInstance->GetMutex());
+  return sInstance->LookupBestMatch(aImageKey, aSurfaceKey, aAlternateFlags);
 }
 
 /* static */ InsertOutcome
 SurfaceCache::Insert(imgFrame*         aSurface,
                      const ImageKey    aImageKey,
                      const SurfaceKey& aSurfaceKey,
                      Lifetime          aLifetime)
 {
--- a/image/src/SurfaceCache.h
+++ b/image/src/SurfaceCache.h
@@ -58,16 +58,24 @@ public:
   {
     uint32_t hash = HashGeneric(mSize.width, mSize.height);
     hash = AddToHash(hash, mSVGContext.map(HashSIC).valueOr(0));
     hash = AddToHash(hash, mAnimationTime, mFlags);
     return hash;
   }
 
   IntSize Size() const { return mSize; }
+  Maybe<SVGImageContext> SVGContext() const { return mSVGContext; }
+  float AnimationTime() const { return mAnimationTime; }
+  uint32_t Flags() const { return mFlags; }
+
+  SurfaceKey WithNewFlags(uint32_t aFlags) const
+  {
+    return SurfaceKey(mSize, mSVGContext, mAnimationTime, aFlags);
+  }
 
 private:
   SurfaceKey(const IntSize& aSize,
              const Maybe<SVGImageContext>& aSVGContext,
              const float aAnimationTime,
              const uint32_t aFlags)
     : mSize(aSize)
     , mSVGContext(aSVGContext)
@@ -166,22 +174,51 @@ struct SurfaceCache
    * If the imgFrame was found in the cache, but had stored its surface in a
    * volatile buffer which was discarded by the OS, then it is automatically
    * removed from the cache and an empty DrawableFrameRef is returned. Note that
    * this will never happen to persistent surfaces associated with a locked
    * image; the cache keeps a strong reference to such surfaces internally.
    *
    * @param aImageKey    Key data identifying which image the surface belongs to.
    * @param aSurfaceKey  Key data which uniquely identifies the requested surface.
+   * @param aAlternateFlags If not Nothing(), a different set of flags than the
+   *                        ones specified in @aSurfaceKey which are also
+   *                        acceptable to the caller. This is more efficient
+   *                        than calling Lookup() twice, which requires taking a
+   *                        lock each time.
    *
    * @return a DrawableFrameRef to the imgFrame wrapping the requested surface,
    *         or an empty DrawableFrameRef if not found.
    */
   static DrawableFrameRef Lookup(const ImageKey    aImageKey,
-                                 const SurfaceKey& aSurfaceKey);
+                                 const SurfaceKey& aSurfaceKey,
+                                 const Maybe<uint32_t>& aAlternateFlags
+                                   = Nothing());
+
+  /**
+   * Looks up the best matching surface in the cache and returns a drawable
+   * reference to the imgFrame containing it.
+   *
+   * Returned surfaces may vary from the requested surface only in terms of
+   * size, unless @aAlternateFlags is specified.
+   *
+   * @param aImageKey    Key data identifying which image the surface belongs to.
+   * @param aSurfaceKey  Key data which identifies the ideal surface to return.
+   * @param aAlternateFlags If not Nothing(), a different set of flags than the
+   *                        ones specified in @aSurfaceKey which are also
+   *                        acceptable to the caller. This is much more
+   *                        efficient than calling LookupBestMatch() twice.
+   *
+   * @return a DrawableFrameRef to the imgFrame wrapping a surface similar to
+   *         the requested surface, or an empty DrawableFrameRef if not found.
+   */
+  static DrawableFrameRef LookupBestMatch(const ImageKey    aImageKey,
+                                          const SurfaceKey& aSurfaceKey,
+                                          const Maybe<uint32_t>& aAlternateFlags
+                                            = Nothing());
 
   /**
    * Insert a surface into the cache. If a surface with the same ImageKey and
    * SurfaceKey is already in the cache, Insert returns FAILURE_ALREADY_PRESENT.
    *
    * Each surface in the cache has a lifetime, either Transient or Persistent.
    * Transient surfaces can expire from the cache at any time. Persistent
    * surfaces can ordinarily also expire from the cache at any time, but if the
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -894,16 +894,25 @@ VectorImage::RequestDecode()
 
 NS_IMETHODIMP
 VectorImage::StartDecoding()
 {
   // Nothing to do for SVG images
   return NS_OK;
 }
 
+NS_IMETHODIMP
+VectorImage::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags)
+{
+  // Nothing to do for SVG images, though in theory we could rasterize to the
+  // provided size ahead of time if we supported off-main-thread SVG
+  // rasterization...
+  return NS_OK;
+}
+
 bool
 VectorImage::IsDecoded()
 {
   return mIsFullyLoaded || mError;
 }
 
 //******************************************************************************
 /* void lockImage() */
--- a/image/src/imgRequest.cpp
+++ b/image/src/imgRequest.cpp
@@ -694,20 +694,19 @@ NS_IMETHODIMP imgRequest::OnStartRequest
   // Try to retarget OnDataAvailable to a decode thread.
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
   nsCOMPtr<nsIThreadRetargetableRequest> retargetable =
     do_QueryInterface(aRequest);
   if (httpChannel && retargetable) {
     nsAutoCString mimeType;
     nsresult rv = httpChannel->GetContentType(mimeType);
     if (NS_SUCCEEDED(rv) && !mimeType.EqualsLiteral(IMAGE_SVG_XML)) {
-      // Image object not created until OnDataAvailable, so forward to static
-      // DecodePool directly.
+      // Retarget OnDataAvailable to the DecodePool's IO thread.
       nsCOMPtr<nsIEventTarget> target =
-        DecodePool::Singleton()->GetEventTarget();
+        DecodePool::Singleton()->GetIOEventTarget();
       rv = retargetable->RetargetDeliveryTo(target);
     }
     PR_LOG(GetImgLog(), PR_LOG_WARNING,
            ("[this=%p] imgRequest::OnStartRequest -- "
             "RetargetDeliveryTo rv %d=%s\n",
             this, rv, NS_SUCCEEDED(rv) ? "succeeded" : "failed"));
   }
 
--- a/image/test/crashtests/crashtests.list
+++ b/image/test/crashtests/crashtests.list
@@ -26,17 +26,17 @@ load invalid-icc-profile.jpg
 load 256-width.ico
 load 256-height.ico
 
 # A 3-frame animated GIF with an inordinate delay between the second and third
 # frame.
 HTTP load delayedframe.sjs
 
 asserts(0-1) load 681190.html # asserts can't create such a big surface
-skip-if(Android&&smallScreen) skip-if(B2G) load 694165-1.xhtml # nexus-s Android 2.3.6, bug 876275 for B2G on a VM
+skip-if(Android&&smallScreen) skip-if(B2G) skip-if(OSX==1010&&isDebugBuild) load 694165-1.xhtml # nexus-s Android 2.3.6, bug 876275 for B2G on a VM; bug 1123195 for OS X 10.10 debug
 load 732319-1.html
 load 844403-1.html
 
 load truncated-second-frame.png # bug 863975
 
 # Bug 863958
 # This icon's size is such that it leads to multiple writes to the PNG decoder
 # after we've gotten our size.
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -457,17 +457,17 @@ struct JSClass {
 // Implementing this efficiently requires that global objects have classes
 // with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
 // previously allowed, but is now an ES5 violation and thus unsupported.
 //
 // JSCLASS_GLOBAL_APPLICATION_SLOTS is the number of slots reserved at
 // the beginning of every global object's slots for use by the
 // application.
 #define JSCLASS_GLOBAL_APPLICATION_SLOTS 4
-#define JSCLASS_GLOBAL_SLOT_COUNT      (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 3 + 30)
+#define JSCLASS_GLOBAL_SLOT_COUNT      (JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 3 + 31)
 #define JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(n)                                    \
     (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + (n)))
 #define JSCLASS_GLOBAL_FLAGS                                                  \
     JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0)
 #define JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(clasp)                              \
   (((clasp)->flags & JSCLASS_IS_GLOBAL)                                       \
    && JSCLASS_RESERVED_SLOTS(clasp) >= JSCLASS_GLOBAL_SLOT_COUNT)
 
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -26,16 +26,17 @@ using namespace js;
 
 using mozilla::ArrayLength;
 using mozilla::IsFinite;
 using mozilla::IsNaN;
 using mozilla::FloorLog2;
 
 namespace js {
 extern const JSFunctionSpec Float32x4Methods[];
+extern const JSFunctionSpec Float64x2Methods[];
 extern const JSFunctionSpec Int32x4Methods[];
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // SIMD
 
 static const char *laneNames[] = {"lane 0", "lane 1", "lane 2", "lane3"};
 
@@ -60,16 +61,17 @@ template<class V>
 bool
 js::IsVectorObject(HandleValue v)
 {
     return CheckVectorObject(v, V::type);
 }
 
 template bool js::IsVectorObject<Int32x4>(HandleValue v);
 template bool js::IsVectorObject<Float32x4>(HandleValue v);
+template bool js::IsVectorObject<Float64x2>(HandleValue v);
 
 template<typename V>
 bool
 js::ToSimdConstant(JSContext *cx, HandleValue v, jit::SimdConstant *out)
 {
     typedef typename V::Elem Elem;
     if (!IsVectorObject<V>(v)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SIMD_NOT_A_VECTOR);
@@ -119,16 +121,19 @@ static bool type##Lane##lane(JSContext *
     LANE_ACCESSOR(type, 0); \
     LANE_ACCESSOR(type, 1); \
     LANE_ACCESSOR(type, 2); \
     LANE_ACCESSOR(type, 3);
 
     FOUR_LANES_ACCESSOR(Int32x4);
     FOUR_LANES_ACCESSOR(Float32x4);
 #undef FOUR_LANES_ACCESSOR
+
+    LANE_ACCESSOR(Float64x2, 0);
+    LANE_ACCESSOR(Float64x2, 1);
 #undef LANE_ACCESSOR
 
 template<typename SimdType>
 static bool SignMask(JSContext *cx, unsigned argc, Value *vp)
 {
     typedef typename SimdType::Elem Elem;
 
     CallArgs args = CallArgsFromVp(argc, vp);
@@ -163,16 +168,17 @@ static bool SignMask(JSContext *cx, unsi
     return true;
 }
 
 #define SIGN_MASK(type) \
 static bool type##SignMask(JSContext *cx, unsigned argc, Value *vp) { \
     return SignMask<type>(cx, argc, vp); \
 }
     SIGN_MASK(Float32x4);
+    SIGN_MASK(Float64x2);
     SIGN_MASK(Int32x4);
 #undef SIGN_MASK
 
 const Class SimdTypeDescr::class_ = {
     "SIMD",
     JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
     nullptr, /* addProperty */
     nullptr, /* delProperty */
@@ -196,16 +202,23 @@ class Int32x4Defn {
 };
 class Float32x4Defn {
   public:
     static const SimdTypeDescr::Type type = SimdTypeDescr::TYPE_FLOAT32;
     static const JSFunctionSpec TypeDescriptorMethods[];
     static const JSPropertySpec TypedObjectProperties[];
     static const JSFunctionSpec TypedObjectMethods[];
 };
+class Float64x2Defn {
+  public:
+    static const SimdTypeDescr::Type type = SimdTypeDescr::TYPE_FLOAT64;
+    static const JSFunctionSpec TypeDescriptorMethods[];
+    static const JSPropertySpec TypedObjectProperties[];
+    static const JSFunctionSpec TypedObjectMethods[];
+};
 } // namespace js
 
 const JSFunctionSpec js::Float32x4Defn::TypeDescriptorMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
     JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
     JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0),
     JS_FS_END
 };
@@ -219,16 +232,35 @@ const JSPropertySpec js::Float32x4Defn::
     JS_PS_END
 };
 
 const JSFunctionSpec js::Float32x4Defn::TypedObjectMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "SimdToSource", 0, 0),
     JS_FS_END
 };
 
+const JSFunctionSpec js::Float64x2Defn::TypeDescriptorMethods[] = {
+    JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
+    JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
+    JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0),
+    JS_FS_END
+};
+
+const JSPropertySpec js::Float64x2Defn::TypedObjectProperties[] = {
+    JS_PSG("x", Float64x2Lane0, JSPROP_PERMANENT),
+    JS_PSG("y", Float64x2Lane1, JSPROP_PERMANENT),
+    JS_PSG("signMask", Float64x2SignMask, JSPROP_PERMANENT),
+    JS_PS_END
+};
+
+const JSFunctionSpec js::Float64x2Defn::TypedObjectMethods[] = {
+    JS_SELF_HOSTED_FN("toSource", "SimdToSource", 0, 0),
+    JS_FS_END
+};
+
 const JSFunctionSpec js::Int32x4Defn::TypeDescriptorMethods[] = {
     JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
     JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
     JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0),
     JS_FS_END,
 };
 
 const JSPropertySpec js::Int32x4Defn::TypedObjectProperties[] = {
@@ -263,16 +295,17 @@ CreateSimdClass(JSContext *cx, Handle<Gl
         return nullptr;
 
     typeDescr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(type::Simd));
     typeDescr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
     typeDescr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(SimdTypeDescr::alignment(type)));
     typeDescr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(SimdTypeDescr::size(type)));
     typeDescr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(false));
     typeDescr->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(T::type));
+    typeDescr->initReservedSlot(JS_DESCR_SLOT_LANES, Int32Value(SimdTypeDescr::lanes(type)));
 
     if (!CreateUserSizeAndAlignmentProperties(cx, typeDescr))
         return nullptr;
 
     // Create prototype property, which inherits from Object.prototype.
 
     RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
     if (!objProto)
@@ -293,16 +326,28 @@ CreateSimdClass(JSContext *cx, Handle<Gl
                                       T::TypedObjectMethods))
     {
         return nullptr;
     }
 
     return typeDescr;
 }
 
+const char*
+SimdTypeToMinimumLanesNumber(SimdTypeDescr &descr) {
+    switch (descr.type()) {
+      case SimdTypeDescr::TYPE_INT32:
+      case SimdTypeDescr::TYPE_FLOAT32:
+        return "3";
+      case SimdTypeDescr::TYPE_FLOAT64:
+        return "1";
+    }
+    MOZ_CRASH("Unexpected SIMD type description.");
+}
+
 bool
 SimdTypeDescr::call(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     Rooted<SimdTypeDescr*> descr(cx, &args.callee().as<SimdTypeDescr>());
     if (args.length() == 1) {
         // SIMD type used as a coercion
@@ -331,16 +376,23 @@ SimdTypeDescr::call(JSContext *cx, unsig
       case SimdTypeDescr::TYPE_FLOAT32: {
         float *mem = reinterpret_cast<float*>(result->typedMem());
         for (unsigned i = 0; i < 4; i++) {
             if (!RoundFloat32(cx, args.get(i), &mem[i]))
                 return false;
         }
         break;
       }
+      case SimdTypeDescr::TYPE_FLOAT64: {
+        double *mem = reinterpret_cast<double*>(result->typedMem());
+        for (unsigned i = 0; i < 2; i++) {
+            if (!ToNumber(cx, args[i], &mem[i]))
+                return false;
+        }
+      }
     }
     args.rval().setObject(*result);
     return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // SIMD class
 
@@ -381,16 +433,33 @@ SIMDObject::initClass(JSContext *cx, Han
     if (!JS_DefineFunctions(cx, float32x4Object, Float32x4Methods) ||
         !DefineProperty(cx, SIMD, cx->names().float32x4,
                         float32x4Value, nullptr, nullptr,
                         JSPROP_READONLY | JSPROP_PERMANENT))
     {
         return nullptr;
     }
 
+    // float64x2
+    RootedObject float64x2Object(cx);
+    float64x2Object = CreateSimdClass<Float64x2Defn>(cx, global,
+                                                     cx->names().float64x2);
+    if (!float64x2Object)
+        return nullptr;
+
+    // Define float64x2 functions and install as a property of the SIMD object.
+    RootedValue float64x2Value(cx, ObjectValue(*float64x2Object));
+    if (!JS_DefineFunctions(cx, float64x2Object, Float64x2Methods) ||
+        !DefineProperty(cx, SIMD, cx->names().float64x2,
+                        float64x2Value, nullptr, nullptr,
+                        JSPROP_READONLY | JSPROP_PERMANENT))
+    {
+        return nullptr;
+    }
+
     // int32x4
     RootedObject int32x4Object(cx);
     int32x4Object = CreateSimdClass<Int32x4Defn>(cx, global,
                                                  cx->names().int32x4);
     if (!int32x4Object)
         return nullptr;
 
     // Define int32x4 functions and install as a property of the SIMD object.
@@ -406,16 +475,17 @@ SIMDObject::initClass(JSContext *cx, Han
     RootedValue SIMDValue(cx, ObjectValue(*SIMD));
 
     // Everything is set up, install SIMD on the global object.
     if (!DefineProperty(cx, global, cx->names().SIMD, SIMDValue, nullptr, nullptr, 0))
         return nullptr;
 
     global->setConstructor(JSProto_SIMD, SIMDValue);
     global->setFloat32x4TypeDescr(*float32x4Object);
+    global->setFloat64x2TypeDescr(*float64x2Object);
     global->setInt32x4TypeDescr(*int32x4Object);
     return SIMD;
 }
 
 JSObject *
 js_InitSIMDClass(JSContext *cx, HandleObject obj)
 {
     MOZ_ASSERT(obj->is<GlobalObject>());
@@ -436,16 +506,17 @@ js::CreateSimd(JSContext *cx, typename V
         return nullptr;
 
     Elem *resultMem = reinterpret_cast<Elem *>(result->typedMem());
     memcpy(resultMem, data, sizeof(Elem) * V::lanes);
     return result;
 }
 
 template JSObject *js::CreateSimd<Float32x4>(JSContext *cx, Float32x4::Elem *data);
+template JSObject *js::CreateSimd<Float64x2>(JSContext *cx, Float64x2::Elem *data);
 template JSObject *js::CreateSimd<Int32x4>(JSContext *cx, Int32x4::Elem *data);
 
 namespace js {
 // Unary SIMD operators
 template<typename T>
 struct Abs {
     static inline T apply(T x) { return mozilla::Abs(x); }
 };
@@ -762,18 +833,20 @@ CompareFunc(JSContext *cx, unsigned argc
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 2 || !IsVectorObject<In>(args[0]) || !IsVectorObject<In>(args[1]))
         return ErrorBadArgs(cx);
 
     int32_t result[Int32x4::lanes];
     InElem *left = TypedObjectMemory<InElem *>(args[0]);
     InElem *right = TypedObjectMemory<InElem *>(args[1]);
-    for (unsigned i = 0; i < Int32x4::lanes; i++)
-        result[i] = Op<InElem>::apply(left[i], right[i]);
+    for (unsigned i = 0; i < Int32x4::lanes; i++) {
+        unsigned j = (i * In::lanes) / Int32x4::lanes;
+        result[i] = Op<InElem>::apply(left[j], right[j]);
+    }
 
     return StoreResult<Int32x4>(cx, args, result);
 }
 
 template<typename V, typename Vret>
 static bool
 FuncConvert(JSContext *cx, unsigned argc, Value *vp)
 {
@@ -782,17 +855,18 @@ FuncConvert(JSContext *cx, unsigned argc
 
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 1 || !IsVectorObject<V>(args[0]))
         return ErrorBadArgs(cx);
 
     Elem *val = TypedObjectMemory<Elem *>(args[0]);
     RetElem result[Vret::lanes];
     for (unsigned i = 0; i < Vret::lanes; i++)
-        result[i] = ConvertScalar<RetElem>(val[i]);
+        result[i] = i < V::lanes ? ConvertScalar<RetElem>(val[i]) : 0;
+
     return StoreResult<Vret>(cx, args, result);
 }
 
 template<typename V, typename Vret>
 static bool
 FuncConvertBits(JSContext *cx, unsigned argc, Value *vp)
 {
     typedef typename Vret::Elem RetElem;
@@ -853,37 +927,39 @@ Int32x4Bool(JSContext *cx, unsigned argc
     }
 
     int32_t result[Int32x4::lanes];
     for (unsigned i = 0; i < Int32x4::lanes; i++)
         result[i] = args[i].toBoolean() ? 0xFFFFFFFF : 0x0;
     return StoreResult<Int32x4>(cx, args, result);
 }
 
+template<typename In>
 static bool
-Float32x4Clamp(JSContext *cx, unsigned argc, Value *vp)
+Clamp(JSContext *cx, unsigned argc, Value *vp)
 {
+    typedef typename In::Elem InElem;
     CallArgs args = CallArgsFromVp(argc, vp);
-    if (args.length() != 3 || !IsVectorObject<Float32x4>(args[0]) ||
-        !IsVectorObject<Float32x4>(args[1]) || !IsVectorObject<Float32x4>(args[2]))
+    if (args.length() != 3 || !IsVectorObject<In>(args[0]) ||
+        !IsVectorObject<In>(args[1]) || !IsVectorObject<In>(args[2]))
     {
         return ErrorBadArgs(cx);
     }
 
-    float *val = TypedObjectMemory<float *>(args[0]);
-    float *lowerLimit = TypedObjectMemory<float *>(args[1]);
-    float *upperLimit = TypedObjectMemory<float *>(args[2]);
+    InElem *val = TypedObjectMemory<InElem *>(args[0]);
+    InElem *lowerLimit = TypedObjectMemory<InElem *>(args[1]);
+    InElem *upperLimit = TypedObjectMemory<InElem *>(args[2]);
 
-    float result[Float32x4::lanes];
-    for (unsigned i = 0; i < Float32x4::lanes; i++) {
+    InElem result[In::lanes];
+    for (unsigned i = 0; i < In::lanes; i++) {
         result[i] = val[i] < lowerLimit[i] ? lowerLimit[i] : val[i];
         result[i] = result[i] > upperLimit[i] ? upperLimit[i] : result[i];
     }
 
-    return StoreResult<Float32x4>(cx, args, result);
+    return StoreResult<In>(cx, args, result);
 }
 
 template<typename V>
 static bool
 BitSelect(JSContext *cx, unsigned argc, Value *vp)
 {
     typedef typename V::Elem Elem;
 
@@ -982,18 +1058,17 @@ Load(JSContext *cx, unsigned argc, Value
 
     Rooted<TypeDescr*> typeDescr(cx, &V::GetTypeDescr(*cx->global()));
     MOZ_ASSERT(typeDescr);
     Rooted<TypedObject *> result(cx, OutlineTypedObject::createZeroed(cx, typeDescr, 0));
     if (!result)
         return false;
 
     Elem *dest = reinterpret_cast<Elem*>(result->typedMem());
-    for (unsigned i = 0; i < NumElem; i++)
-        dest[i] = typedArrayData[i];
+    memcpy(dest, typedArrayData, sizeof(Elem) * NumElem);
 
     args.rval().setObject(*result);
     return true;
 }
 
 template<class V, unsigned NumElem>
 static bool
 Store(JSContext *cx, unsigned argc, Value *vp)
@@ -1007,31 +1082,39 @@ Store(JSContext *cx, unsigned argc, Valu
     Elem *typedArrayData = nullptr;
     if (!TypedArrayDataPtrFromArgs<Elem, NumElem>(cx, args, &typedArrayData))
         return false;
 
     if (!IsVectorObject<V>(args[2]))
         return ErrorBadArgs(cx);
 
     Elem *src = TypedObjectMemory<Elem*>(args[2]);
-    for (unsigned i = 0; i < NumElem; i++)
-        typedArrayData[i] = src[i];
+    memcpy(typedArrayData, src, sizeof(Elem) * NumElem);
 
     args.rval().setObject(args[2].toObject());
     return true;
 }
 
 #define DEFINE_SIMD_FLOAT32X4_FUNCTION(Name, Func, Operands, Flags) \
 bool                                                                \
 js::simd_float32x4_##Name(JSContext *cx, unsigned argc, Value *vp)  \
 {                                                                   \
     return Func(cx, argc, vp);                                      \
 }
 FLOAT32X4_FUNCTION_LIST(DEFINE_SIMD_FLOAT32X4_FUNCTION)
-#undef DEFINE_SIMD_FLOAT32x4_FUNCTION
+#undef DEFINE_SIMD_FLOAT32X4_FUNCTION
+
+#define DEFINE_SIMD_FLOAT64X2_FUNCTION(Name, Func, Operands, Flags) \
+bool                                                                \
+js::simd_float64x2_##Name(JSContext *cx, unsigned argc, Value *vp)  \
+{                                                                   \
+    return Func(cx, argc, vp);                                      \
+}
+FLOAT64X2_FUNCTION_LIST(DEFINE_SIMD_FLOAT64X2_FUNCTION)
+#undef DEFINE_SIMD_FLOAT64X2_FUNCTION
 
 #define DEFINE_SIMD_INT32X4_FUNCTION(Name, Func, Operands, Flags)   \
 bool                                                                \
 js::simd_int32x4_##Name(JSContext *cx, unsigned argc, Value *vp)    \
 {                                                                   \
     return Func(cx, argc, vp);                                      \
 }
 INT32X4_FUNCTION_LIST(DEFINE_SIMD_INT32X4_FUNCTION)
@@ -1040,15 +1123,23 @@ INT32X4_FUNCTION_LIST(DEFINE_SIMD_INT32X
 const JSFunctionSpec js::Float32x4Methods[] = {
 #define SIMD_FLOAT32X4_FUNCTION_ITEM(Name, Func, Operands, Flags)   \
         JS_FN(#Name, js::simd_float32x4_##Name, Operands, Flags),
         FLOAT32X4_FUNCTION_LIST(SIMD_FLOAT32X4_FUNCTION_ITEM)
 #undef SIMD_FLOAT32x4_FUNCTION_ITEM
         JS_FS_END
 };
 
+const JSFunctionSpec js::Float64x2Methods[] = {
+#define SIMD_FLOAT64X2_FUNCTION_ITEM(Name, Func, Operands, Flags)       \
+        JS_FN(#Name, js::simd_float64x2_##Name, Operands, Flags),
+        FLOAT64X2_FUNCTION_LIST(SIMD_FLOAT64X2_FUNCTION_ITEM)
+#undef SIMD_FLOAT64X2_FUNCTION_ITEM
+        JS_FS_END
+};
+
 const JSFunctionSpec js::Int32x4Methods[] = {
 #define SIMD_INT32X4_FUNCTION_ITEM(Name, Func, Operands, Flags)     \
         JS_FN(#Name, js::simd_int32x4_##Name, Operands, Flags),
         INT32X4_FUNCTION_LIST(SIMD_INT32X4_FUNCTION_ITEM)
 #undef SIMD_INT32X4_FUNCTION_ITEM
         JS_FS_END
 };
--- a/js/src/builtin/SIMD.h
+++ b/js/src/builtin/SIMD.h
@@ -17,16 +17,18 @@
 /*
  * JS SIMD functions.
  * Spec matching polyfill:
  * https://github.com/johnmccutchan/ecmascript_simd/blob/master/src/ecmascript_simd.js
  */
 
 #define FLOAT32X4_UNARY_FUNCTION_LIST(V)                                            \
   V(abs, (UnaryFunc<Float32x4, Abs, Float32x4>), 1, 0)                              \
+  V(fromFloat64x2, (FuncConvert<Float64x2, Float32x4> ), 1, 0)                      \
+  V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Float32x4>), 1, 0)               \
   V(fromInt32x4, (FuncConvert<Int32x4, Float32x4> ), 1, 0)                          \
   V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Float32x4>), 1, 0)                   \
   V(neg, (UnaryFunc<Float32x4, Neg, Float32x4>), 1, 0)                              \
   V(not, (CoercedUnaryFunc<Float32x4, Int32x4, Not, Float32x4>), 1, 0)              \
   V(reciprocal, (UnaryFunc<Float32x4, Rec, Float32x4>), 1, 0)                       \
   V(reciprocalSqrt, (UnaryFunc<Float32x4, RecSqrt, Float32x4>), 1, 0)               \
   V(splat, (FuncSplat<Float32x4>), 1, 0)                                            \
   V(sqrt, (UnaryFunc<Float32x4, Sqrt, Float32x4>), 1, 0)
@@ -59,32 +61,83 @@
   V(withX, (FuncWith<Float32x4, WithX>), 2, 0)                                      \
   V(withY, (FuncWith<Float32x4, WithY>), 2, 0)                                      \
   V(withZ, (FuncWith<Float32x4, WithZ>), 2, 0)                                      \
   V(withW, (FuncWith<Float32x4, WithW>), 2, 0)                                      \
   V(xor, (CoercedBinaryFunc<Float32x4, Int32x4, Xor, Float32x4>), 2, 0)
 
 #define FLOAT32X4_TERNARY_FUNCTION_LIST(V)                                          \
   V(bitselect, BitSelect<Float32x4>, 3, 0)                                          \
-  V(clamp, Float32x4Clamp, 3, 0)                                                    \
+  V(clamp, Clamp<Float32x4>, 3, 0)                                                  \
   V(select, Select<Float32x4>, 3, 0)
 
 #define FLOAT32X4_SHUFFLE_FUNCTION_LIST(V)                                          \
   V(swizzle, Swizzle<Float32x4>, 2, 0)                                              \
   V(shuffle, Shuffle<Float32x4>, 3, 0)
 
 #define FLOAT32X4_FUNCTION_LIST(V)                                                  \
   FLOAT32X4_UNARY_FUNCTION_LIST(V)                                                  \
   FLOAT32X4_BINARY_FUNCTION_LIST(V)                                                 \
   FLOAT32X4_TERNARY_FUNCTION_LIST(V)                                                \
   FLOAT32X4_SHUFFLE_FUNCTION_LIST(V)
 
+#define FLOAT64X2_UNARY_FUNCTION_LIST(V)                                            \
+  V(abs, (UnaryFunc<Float64x2, Abs, Float64x2>), 1, 0)                              \
+  V(fromFloat32x4, (FuncConvert<Float32x4, Float64x2> ), 1, 0)                      \
+  V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Float64x2>), 1, 0)               \
+  V(fromInt32x4, (FuncConvert<Int32x4, Float64x2> ), 1, 0)                          \
+  V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Float64x2>), 1, 0)                   \
+  V(neg, (UnaryFunc<Float64x2, Neg, Float64x2>), 1, 0)                              \
+  V(reciprocal, (UnaryFunc<Float64x2, Rec, Float64x2>), 1, 0)                       \
+  V(reciprocalSqrt, (UnaryFunc<Float64x2, RecSqrt, Float64x2>), 1, 0)               \
+  V(splat, (FuncSplat<Float64x2>), 1, 0)                                            \
+  V(sqrt, (UnaryFunc<Float64x2, Sqrt, Float64x2>), 1, 0)
+
+#define FLOAT64X2_BINARY_FUNCTION_LIST(V)                                           \
+  V(add, (BinaryFunc<Float64x2, Add, Float64x2>), 2, 0)                             \
+  V(div, (BinaryFunc<Float64x2, Div, Float64x2>), 2, 0)                             \
+  V(equal, (CompareFunc<Float64x2, Equal>), 2, 0)                                   \
+  V(greaterThan, (CompareFunc<Float64x2, GreaterThan>), 2, 0)                       \
+  V(greaterThanOrEqual, (CompareFunc<Float64x2, GreaterThanOrEqual>), 2, 0)         \
+  V(lessThan, (CompareFunc<Float64x2, LessThan>), 2, 0)                             \
+  V(lessThanOrEqual, (CompareFunc<Float64x2, LessThanOrEqual>), 2, 0)               \
+  V(load,    (Load<Float64x2, 2>), 2, 0)                                            \
+  V(loadX,   (Load<Float64x2, 1>), 2, 0)                                            \
+  V(max, (BinaryFunc<Float64x2, Maximum, Float64x2>), 2, 0)                         \
+  V(maxNum, (BinaryFunc<Float64x2, MaxNum, Float64x2>), 2, 0)                       \
+  V(min, (BinaryFunc<Float64x2, Minimum, Float64x2>), 2, 0)                         \
+  V(minNum, (BinaryFunc<Float64x2, MinNum, Float64x2>), 2, 0)                       \
+  V(mul, (BinaryFunc<Float64x2, Mul, Float64x2>), 2, 0)                             \
+  V(notEqual, (CompareFunc<Float64x2, NotEqual>), 2, 0)                             \
+  V(store,    (Store<Float64x2, 2>), 3, 0)                                          \
+  V(storeX,   (Store<Float64x2, 1>), 3, 0)                                          \
+  V(sub, (BinaryFunc<Float64x2, Sub, Float64x2>), 2, 0)                             \
+  V(withX, (FuncWith<Float64x2, WithX>), 2, 0)                                      \
+  V(withY, (FuncWith<Float64x2, WithY>), 2, 0)
+
+#define FLOAT64X2_TERNARY_FUNCTION_LIST(V)                                          \
+  V(bitselect, BitSelect<Float64x2>, 3, 0)                                          \
+  V(clamp, Clamp<Float64x2>, 3, 0)                                                  \
+  V(select, Select<Float64x2>, 3, 0)
+
+#define FLOAT64X2_SHUFFLE_FUNCTION_LIST(V)                                          \
+  V(swizzle, Swizzle<Float64x2>, 2, 0)                                              \
+  V(shuffle, Shuffle<Float64x2>, 3, 0)
+
+#define FLOAT64X2_FUNCTION_LIST(V)                                                  \
+  FLOAT64X2_UNARY_FUNCTION_LIST(V)                                                  \
+  FLOAT64X2_BINARY_FUNCTION_LIST(V)                                                 \
+  FLOAT64X2_TERNARY_FUNCTION_LIST(V)                                                \
+  FLOAT64X2_SHUFFLE_FUNCTION_LIST(V)
+
 #define INT32X4_UNARY_FUNCTION_LIST(V)                                              \
   V(fromFloat32x4, (FuncConvert<Float32x4, Int32x4>), 1, 0)                         \
   V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Int32x4>), 1, 0)                 \
+  V(fromFloat64x2, (FuncConvert<Float64x2, Int32x4>), 1, 0)                         \
+  V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Int32x4>), 1, 0)                 \
   V(neg, (UnaryFunc<Int32x4, Neg, Int32x4>), 1, 0)                                  \
   V(not, (UnaryFunc<Int32x4, Not, Int32x4>), 1, 0)                                  \
   V(splat, (FuncSplat<Int32x4>), 0, 0)
 
 #define INT32X4_BINARY_FUNCTION_LIST(V)                                             \
   V(add, (BinaryFunc<Int32x4, Add, Int32x4>), 2, 0)                                 \
   V(and, (BinaryFunc<Int32x4, And, Int32x4>), 2, 0)                                 \
   V(equal, (CompareFunc<Int32x4, Equal>), 2, 0)                                     \
@@ -207,16 +260,36 @@ struct Float32x4 {
         *out = v.toNumber();
         return true;
     }
     static void setReturn(CallArgs &args, Elem value) {
         args.rval().setDouble(JS::CanonicalizeNaN(value));
     }
 };
 
+struct Float64x2 {
+    typedef double Elem;
+    static const unsigned lanes = 2;
+    static const SimdTypeDescr::Type type = SimdTypeDescr::TYPE_FLOAT64;
+
+    static TypeDescr &GetTypeDescr(GlobalObject &global) {
+        return global.float64x2TypeDescr().as<TypeDescr>();
+    }
+    static Elem toType(Elem a) {
+        return a;
+    }
+    static bool toType(JSContext *cx, JS::HandleValue v, Elem *out) {
+        *out = v.toNumber();
+        return true;
+    }
+    static void setReturn(CallArgs &args, Elem value) {
+        args.rval().setDouble(JS::CanonicalizeNaN(value));
+    }
+};
+
 struct Int32x4 {
     typedef int32_t Elem;
     static const unsigned lanes = 4;
     static const SimdTypeDescr::Type type = SimdTypeDescr::TYPE_INT32;
 
     static TypeDescr &GetTypeDescr(GlobalObject &global) {
         return global.int32x4TypeDescr().as<TypeDescr>();
     }
@@ -241,16 +314,22 @@ template<typename V>
 bool ToSimdConstant(JSContext *cx, HandleValue v, jit::SimdConstant *out);
 
 #define DECLARE_SIMD_FLOAT32X4_FUNCTION(Name, Func, Operands, Flags) \
 extern bool                                                          \
 simd_float32x4_##Name(JSContext *cx, unsigned argc, Value *vp);
 FLOAT32X4_FUNCTION_LIST(DECLARE_SIMD_FLOAT32X4_FUNCTION)
 #undef DECLARE_SIMD_FLOAT32X4_FUNCTION
 
+#define DECLARE_SIMD_FLOAT64X2_FUNCTION(Name, Func, Operands, Flags) \
+extern bool                                                          \
+simd_float64x2_##Name(JSContext *cx, unsigned argc, Value *vp);
+FLOAT64X2_FUNCTION_LIST(DECLARE_SIMD_FLOAT64X2_FUNCTION)
+#undef DECLARE_SIMD_FLOAT64X2_FUNCTION
+
 #define DECLARE_SIMD_INT32x4_FUNCTION(Name, Func, Operands, Flags)   \
 extern bool                                                          \
 simd_int32x4_##Name(JSContext *cx, unsigned argc, Value *vp);
 INT32X4_FUNCTION_LIST(DECLARE_SIMD_INT32x4_FUNCTION)
 #undef DECLARE_SIMD_INT32x4_FUNCTION
 
 }  /* namespace js */
 
--- a/js/src/builtin/TypedObject.cpp
+++ b/js/src/builtin/TypedObject.cpp
@@ -401,40 +401,53 @@ js::ReferenceTypeDescr::call(JSContext *
         return true;
       }
     }
 
     MOZ_CRASH("Unhandled Reference type");
 }
 
 /***************************************************************************
- * X4 type objects
+ * SIMD type objects
  *
  * Note: these are partially defined in SIMD.cpp
  */
 
 static const int32_t SimdSizes[] = {
-#define SIMD_SIZE(_kind, _type, _name)                        \
-    sizeof(_type) * 4,
+#define SIMD_SIZE(_kind, _type, _name, _lanes)                 \
+    sizeof(_type) * _lanes,
     JS_FOR_EACH_SIMD_TYPE_REPR(SIMD_SIZE) 0
 #undef SIMD_SIZE
 };
 
+static int32_t SimdLanes[] = {
+#define SIMD_LANE(_kind, _type, _name, _lanes)                 \
+    _lanes,
+    JS_FOR_EACH_SIMD_TYPE_REPR(SIMD_LANE) 0
+#undef SIMD_LANE
+};
+
 int32_t
 SimdTypeDescr::size(Type t)
 {
     return SimdSizes[t];
 }
 
 int32_t
 SimdTypeDescr::alignment(Type t)
 {
     return SimdSizes[t];
 }
 
+int32_t
+SimdTypeDescr::lanes(Type t)
+{
+    return SimdLanes[t];
+}
+
 /***************************************************************************
  * ArrayMetaTypeDescr class
  */
 
 /*
  * For code like:
  *
  *   var A = new TypedObject.ArrayType(uint8, 10);
@@ -2690,16 +2703,26 @@ js::GetFloat32x4TypeDescr(JSContext *cx,
     CallArgs args = CallArgsFromVp(argc, vp);
     Rooted<GlobalObject*> global(cx, cx->global());
     MOZ_ASSERT(global);
     args.rval().setObject(global->float32x4TypeDescr());
     return true;
 }
 
 bool
+js::GetFloat64x2TypeDescr(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    Rooted<GlobalObject*> global(cx, cx->global());
+    MOZ_ASSERT(global);
+    args.rval().setObject(global->float64x2TypeDescr());
+    return true;
+}
+
+bool
 js::GetInt32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     Rooted<GlobalObject*> global(cx, cx->global());
     MOZ_ASSERT(global);
     args.rval().setObject(global->int32x4TypeDescr());
     return true;
 }
--- a/js/src/builtin/TypedObject.h
+++ b/js/src/builtin/TypedObject.h
@@ -320,43 +320,46 @@ class ComplexTypeDescr : public TypeDesc
     // Returns the prototype that instances of this type descriptor
     // will have.
     TypedProto &instancePrototype() const {
         return getReservedSlot(JS_DESCR_SLOT_TYPROTO).toObject().as<TypedProto>();
     }
 };
 
 /*
- * Type descriptors `float32x4` and `int32x4`
+ * Type descriptors `float32x4`, `int32x4` and `float64x2`
  */
 class SimdTypeDescr : public ComplexTypeDescr
 {
   public:
     enum Type {
         TYPE_INT32 = JS_SIMDTYPEREPR_INT32,
         TYPE_FLOAT32 = JS_SIMDTYPEREPR_FLOAT32,
+        TYPE_FLOAT64 = JS_SIMDTYPEREPR_FLOAT64
     };
 
     static const type::Kind Kind = type::Simd;
     static const bool Opaque = false;
     static const Class class_;
     static int32_t size(Type t);
     static int32_t alignment(Type t);
+    static int32_t lanes(Type t);
 
     SimdTypeDescr::Type type() const {
         return (SimdTypeDescr::Type) getReservedSlot(JS_DESCR_SLOT_TYPE).toInt32();
     }
 
     static bool call(JSContext *cx, unsigned argc, Value *vp);
     static bool is(const Value &v);
 };
 
 #define JS_FOR_EACH_SIMD_TYPE_REPR(macro_)                             \
-    macro_(SimdTypeDescr::TYPE_INT32, int32_t, int32)                  \
-    macro_(SimdTypeDescr::TYPE_FLOAT32, float, float32)
+    macro_(SimdTypeDescr::TYPE_INT32, int32_t, int32, 4)               \
+    macro_(SimdTypeDescr::TYPE_FLOAT32, float, float32, 4)             \
+    macro_(SimdTypeDescr::TYPE_FLOAT64, double, float64, 2)
 
 bool IsTypedObjectClass(const Class *clasp); // Defined below
 bool IsTypedObjectArray(JSObject& obj);
 
 bool CreateUserSizeAndAlignmentProperties(JSContext *cx, HandleTypeDescr obj);
 
 class ArrayTypeDescr;
 
@@ -876,16 +879,24 @@ bool GetTypedObjectModule(JSContext *cx,
  * Usage: GetFloat32x4TypeDescr()
  *
  * Returns the float32x4 type object. SIMD pseudo-module must have
  * been initialized for this to be safe.
  */
 bool GetFloat32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp);
 
 /*
+ * Usage: GetFloat64x2TypeDescr()
+ *
+ * Returns the float64x2 type object. SIMD pseudo-module must have
+ * been initialized for this to be safe.
+ */
+bool GetFloat64x2TypeDescr(JSContext *cx, unsigned argc, Value *vp);
+
+/*
  * Usage: GetInt32x4TypeDescr()
  *
  * Returns the int32x4 type object. SIMD pseudo-module must have
  * been initialized for this to be safe.
  */
 bool GetInt32x4TypeDescr(JSContext *cx, unsigned argc, Value *vp);
 
 /*
--- a/js/src/builtin/TypedObject.js
+++ b/js/src/builtin/TypedObject.js
@@ -147,16 +147,21 @@ function TypedObjectGetSimd(descr, typed
   switch (type) {
   case JS_SIMDTYPEREPR_FLOAT32:
     var x = Load_float32(typedObj, offset + 0);
     var y = Load_float32(typedObj, offset + 4);
     var z = Load_float32(typedObj, offset + 8);
     var w = Load_float32(typedObj, offset + 12);
     return GetFloat32x4TypeDescr()(x, y, z, w);
 
+  case JS_SIMDTYPEREPR_FLOAT64:
+    var x = Load_float64(typedObj, offset + 0);
+    var y = Load_float64(typedObj, offset + 8);
+    return GetFloat64x2TypeDescr()(x, y);
+
   case JS_SIMDTYPEREPR_INT32:
     var x = Load_int32(typedObj, offset + 0);
     var y = Load_int32(typedObj, offset + 4);
     var z = Load_int32(typedObj, offset + 8);
     var w = Load_int32(typedObj, offset + 12);
     return GetInt32x4TypeDescr()(x, y, z, w);
   }
 
@@ -317,16 +322,20 @@ function TypedObjectSetSimd(descr, typed
   var type = DESCR_TYPE(descr);
   switch (type) {
     case JS_SIMDTYPEREPR_FLOAT32:
       Store_float32(typedObj, offset + 0, Load_float32(fromValue, 0));
       Store_float32(typedObj, offset + 4, Load_float32(fromValue, 4));
       Store_float32(typedObj, offset + 8, Load_float32(fromValue, 8));
       Store_float32(typedObj, offset + 12, Load_float32(fromValue, 12));
       break;
+    case JS_SIMDTYPEREPR_FLOAT64:
+      Store_float64(typedObj, offset + 0, Load_float64(fromValue, 0));
+      Store_float64(typedObj, offset + 8, Load_float64(fromValue, 8));
+      break;
     case JS_SIMDTYPEREPR_INT32:
       Store_int32(typedObj, offset + 0, Load_int32(fromValue, 0));
       Store_int32(typedObj, offset + 4, Load_int32(fromValue, 4));
       Store_int32(typedObj, offset + 8, Load_int32(fromValue, 8));
       Store_int32(typedObj, offset + 12, Load_int32(fromValue, 12));
       break;
     default:
       assert(false, "Unhandled Simd type: " + type);
@@ -452,33 +461,53 @@ function TypedObjectArrayRedimension(new
 // SIMD
 
 function SimdProtoString(type) {
   switch (type) {
   case JS_SIMDTYPEREPR_INT32:
     return "int32x4";
   case JS_SIMDTYPEREPR_FLOAT32:
     return "float32x4";
+  case JS_SIMDTYPEREPR_FLOAT64:
+    return "float64x2";
+  }
+
+  assert(false, "Unhandled type constant");
+  return undefined;
+}
+
+function SimdTypeToLength(type) {
+  switch (type) {
+  case JS_SIMDTYPEREPR_INT32:
+  case JS_SIMDTYPEREPR_FLOAT32:
+    return 4;
+  case JS_SIMDTYPEREPR_FLOAT64:
+    return 2;
   }
 
   assert(false, "Unhandled type constant");
   return undefined;
 }
 
 function SimdToSource() {
   if (!IsObject(this) || !ObjectIsTypedObject(this))
     ThrowError(JSMSG_INCOMPATIBLE_PROTO, "SIMD", "toSource", typeof this);
 
   var descr = TypedObjectTypeDescr(this);
 
   if (DESCR_KIND(descr) != JS_TYPEREPR_SIMD_KIND)
     ThrowError(JSMSG_INCOMPATIBLE_PROTO, "SIMD", "toSource", typeof this);
 
   var type = DESCR_TYPE(descr);
-  return SimdProtoString(type)+"("+this.x+", "+this.y+", "+this.z+", "+this.w+")";
+  var protoString = SimdProtoString(type);
+  var length = SimdTypeToLength(type);
+  if (length == 4)
+    return protoString+"("+this.x+", "+this.y+", "+this.z+", "+this.w+")";
+  else if (length == 2)
+    return protoString+"("+this.x+", "+this.y+")";
 }
 
 ///////////////////////////////////////////////////////////////////////////
 // Miscellaneous
 
 function DescrsEquiv(descr1, descr2) {
   assert(IsObject(descr1) && ObjectIsTypeDescr(descr1), "descr1 not descr");
   assert(IsObject(descr2) && ObjectIsTypeDescr(descr2), "descr2 not descr");
--- a/js/src/builtin/TypedObjectConstants.h
+++ b/js/src/builtin/TypedObjectConstants.h
@@ -27,18 +27,19 @@
 #define JS_DESCR_SLOT_STRING_REPR        1  // Atomized string representation
 #define JS_DESCR_SLOT_ALIGNMENT          2  // Alignment in bytes
 #define JS_DESCR_SLOT_SIZE               3  // Size in bytes, else 0
 #define JS_DESCR_SLOT_OPAQUE             4  // Atomized string representation
 #define JS_DESCR_SLOT_TYPROTO            5  // Prototype for instances, if any
 #define JS_DESCR_SLOT_ARRAYPROTO         6  // Lazily created prototype for arrays
 #define JS_DESCR_SLOT_TRACE_LIST         7  // List of references for use in tracing
 
-// Slots on scalars, references, and x4s
+// Slots on scalars, references, and SIMD objects
 #define JS_DESCR_SLOT_TYPE               8  // Type code
+#define JS_DESCR_SLOT_LANES              9
 
 // Slots on array descriptors
 #define JS_DESCR_SLOT_ARRAY_ELEM_TYPE    8
 #define JS_DESCR_SLOT_ARRAY_LENGTH       9
 
 // Slots on struct type objects
 #define JS_DESCR_SLOT_STRUCT_FIELD_NAMES 8
 #define JS_DESCR_SLOT_STRUCT_FIELD_TYPES 9
@@ -80,10 +81,11 @@
 #define JS_REFERENCETYPEREPR_STRING     2
 
 // These constants are for use exclusively in JS code.  In C++ code,
 // prefer SimdTypeRepresentation::TYPE_INT32 etc, since that allows
 // you to write a switch which will receive a warning if you omit a
 // case.
 #define JS_SIMDTYPEREPR_INT32         0
 #define JS_SIMDTYPEREPR_FLOAT32       1
+#define JS_SIMDTYPEREPR_FLOAT64       2
 
 #endif
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1672,18 +1672,20 @@ js_fun_bind(JSContext *cx, HandleObject 
         unsigned nargs = target->as<JSFunction>().nargs();
         if (nargs > argslen)
             length = nargs - argslen;
     }
 
     /* Step 4-6, 10-11. */
     RootedAtom name(cx, target->is<JSFunction>() ? target->as<JSFunction>().atom() : nullptr);
 
+    JSFunction::Flags flags = target->isConstructor() ? JSFunction::NATIVE_CTOR
+                                                      : JSFunction::NATIVE_FUN;
     RootedObject funobj(cx, NewFunction(cx, js::NullPtr(), CallOrConstructBoundFunction, length,
-                                        JSFunction::NATIVE_CTOR, target, name));
+                                        flags, target, name));
     if (!funobj)
         return nullptr;
 
     /* NB: Bound functions abuse |parent| to store their target. */
     if (!JSObject::setParent(cx, funobj, target))
         return nullptr;
 
     if (!funobj->as<JSFunction>().initBoundFunction(cx, thisArg, boundArgs, argslen))
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Function/bound-non-constructable.js
@@ -0,0 +1,17 @@
+var objects = [
+    Math.sin.bind(null),
+    new Proxy(Math.sin.bind(null), {}),
+    Function.prototype.bind.call(new Proxy(Math.sin, {}))
+]
+
+for (var obj of objects) {
+    // Target is not constructable, so a new array should be created internally.
+    assertDeepEq(Array.from.call(obj, [1, 2, 3]), [1, 2, 3]);
+    assertDeepEq(Array.of.call(obj, 1, 2, 3), [1, 2, 3]);
+
+    // Make sure they are callable, but not constructable.
+    obj();
+    assertThrowsInstanceOf(() => new obj, TypeError);
+}
+
+reportCompare(0, 0, 'ok');
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/clamp.js
@@ -0,0 +1,60 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var float32x4 = SIMD.float32x4;
+var float64x2 = SIMD.float64x2;
+
+var summary = 'float32x4/float64x2 clamp';
+
+function test() {
+  // FIXME -- Bug 1068028: Amend to check for correctness of NaN/-0 border cases once the semantics are defined.
+
+  var a = float32x4(-20, 10, 30, 0.5);
+  var lower = float32x4(2, 1, 50, 0);
+  var upper = float32x4(2.5, 5, 55, 1);
+  var c = SIMD.float32x4.clamp(a, lower, upper);
+  assertEq(c.x, 2);
+  assertEq(c.y, 5);
+  assertEq(c.z, 50);
+  assertEq(c.w, 0.5);
+
+  var d = float32x4(-13.37, 10.46, 31.79, 0.54);
+  var lower1 = float32x4(2.1, 1.1, 50.13, 0.0);
+  var upper1 = float32x4(2.56, 5.55, 55.93, 1.1);
+  var f = SIMD.float32x4.clamp(d, lower1, upper1);
+  assertEq(f.x, Math.fround(2.1));
+  assertEq(f.y, Math.fround(5.55));
+  assertEq(f.z, Math.fround(50.13));
+  assertEq(f.w, Math.fround(0.54));
+
+  var g = float32x4(4, -Infinity, 10, -10);
+  var lower2 = float32x4(5, -Infinity, -Infinity, -Infinity);
+  var upper2 = float32x4(Infinity, 5, Infinity, Infinity);
+  var i = SIMD.float32x4.clamp(g, lower2, upper2);
+  assertEq(i.x, 5);
+  assertEq(i.y, -Infinity);
+  assertEq(i.z, 10);
+  assertEq(i.w, -10);
+
+  var j = float64x2(-20, 10);
+  var k = float64x2(2.125, 3);
+  var lower3 = float64x2(2, 1);
+  var upper3 = float64x2(2.5, 5);
+  var l = float64x2.clamp(j, lower3, upper3);
+  assertEq(l.x, 2);
+  assertEq(l.y, 5);
+  var m = float64x2.clamp(k, lower3, upper3);
+  assertEq(m.x, 2.125);
+  assertEq(m.y, 3);
+
+  var n = float64x2(-5, 5);
+  var lower4 = float64x2(-Infinity, 0);
+  var upper4 = float64x2(+Infinity, +Infinity);
+  var p = float64x2.clamp(n, lower4, upper4);
+  assertEq(p.x, -5);
+  assertEq(p.y, 5);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
--- a/js/src/tests/ecma_7/SIMD/comparisons.js
+++ b/js/src/tests/ecma_7/SIMD/comparisons.js
@@ -1,60 +1,80 @@
 // |reftest| skip-if(!this.hasOwnProperty("SIMD"))
 
 /*
  * Any copyright is dedicated to the Public Domain.
  * https://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var float32x4 = SIMD.float32x4;
+var float64x2 = SIMD.float64x2;
 var int32x4 = SIMD.int32x4;
 
 var fround = Math.fround;
 
 function boolToSimdLogical(b) {
     return b ? 0xFFFFFFFF | 0 : 0x0;
 }
 
 function testEqualFloat32x4(v, w) {
-    testBinaryFunc(v, w, float32x4.equal, (x, y) => boolToSimdLogical(fround(x) == fround(y)));
+    testBinaryCompare(v, w, float32x4.equal, (x, y) => boolToSimdLogical(fround(x) == fround(y)));
 }
 function testNotEqualFloat32x4(v, w) {
-    testBinaryFunc(v, w, float32x4.notEqual, (x, y) => boolToSimdLogical(fround(x) != fround(y)));
+    testBinaryCompare(v, w, float32x4.notEqual, (x, y) => boolToSimdLogical(fround(x) != fround(y)));
 }
 function testLessThanFloat32x4(v, w) {
-    testBinaryFunc(v, w, float32x4.lessThan, (x, y) => boolToSimdLogical(fround(x) < fround(y)));
+    testBinaryCompare(v, w, float32x4.lessThan, (x, y) => boolToSimdLogical(fround(x) < fround(y)));
 }
 function testLessThanOrEqualFloat32x4(v, w) {
-    testBinaryFunc(v, w, float32x4.lessThanOrEqual, (x, y) => boolToSimdLogical(fround(x) <= fround(y)));
+    testBinaryCompare(v, w, float32x4.lessThanOrEqual, (x, y) => boolToSimdLogical(fround(x) <= fround(y)));
 }
 function testGreaterThanFloat32x4(v, w) {
-    testBinaryFunc(v, w, float32x4.greaterThan, (x, y) => boolToSimdLogical(fround(x) > fround(y)));
+    testBinaryCompare(v, w, float32x4.greaterThan, (x, y) => boolToSimdLogical(fround(x) > fround(y)));
 }
 function testGreaterThanOrEqualFloat32x4(v, w) {
-    testBinaryFunc(v, w, float32x4.greaterThanOrEqual, (x, y) => boolToSimdLogical(fround(x) >= fround(y)));
+    testBinaryCompare(v, w, float32x4.greaterThanOrEqual, (x, y) => boolToSimdLogical(fround(x) >= fround(y)));
+}
+
+function testEqualFloat64x2(v, w) {
+    testBinaryCompare(v, w, float64x2.equal, (x, y) => boolToSimdLogical(x == y));
+}
+function testNotEqualFloat64x2(v, w) {
+    testBinaryCompare(v, w, float64x2.notEqual, (x, y) => boolToSimdLogical(x != y));
+}
+function testLessThanFloat64x2(v, w) {
+    testBinaryCompare(v, w, float64x2.lessThan, (x, y) => boolToSimdLogical(x < y));
+}
+function testLessThanOrEqualFloat64x2(v, w) {
+    testBinaryCompare(v, w, float64x2.lessThanOrEqual, (x, y) => boolToSimdLogical(x <= y));
+}
+function testGreaterThanFloat64x2(v, w) {
+    testBinaryCompare(v, w, float64x2.greaterThan, (x, y) => boolToSimdLogical(x > y));
+}
+function testGreaterThanOrEqualFloat64x2(v, w) {
+    testBinaryCompare(v, w, float64x2.greaterThanOrEqual, (x, y) => boolToSimdLogical(x >= y));
 }
 
 function testEqualInt32x4(v, w) {
-    testBinaryFunc(v, w, int32x4.equal, (x, y) => boolToSimdLogical(x == y));
+    testBinaryCompare(v, w, int32x4.equal, (x, y) => boolToSimdLogical(x == y));
 }
 function testNotEqualInt32x4(v, w) {
-    testBinaryFunc(v, w, int32x4.notEqual, (x, y) => boolToSimdLogical(x != y));
+    testBinaryCompare(v, w, int32x4.notEqual, (x, y) => boolToSimdLogical(x != y));
 }
 function testLessThanInt32x4(v, w) {
-    testBinaryFunc(v, w, int32x4.lessThan, (x, y) => boolToSimdLogical(x < y));
+    testBinaryCompare(v, w, int32x4.lessThan, (x, y) => boolToSimdLogical(x < y));
 }
 function testLessThanOrEqualInt32x4(v, w) {
-    testBinaryFunc(v, w, int32x4.lessThanOrEqual, (x, y) => boolToSimdLogical(x <= y));
+    testBinaryCompare(v, w, int32x4.lessThanOrEqual, (x, y) => boolToSimdLogical(x <= y));
 }
 function testGreaterThanInt32x4(v, w) {
-    testBinaryFunc(v, w, int32x4.greaterThan, (x, y) => boolToSimdLogical(x > y));
+    testBinaryCompare(v, w, int32x4.greaterThan, (x, y) => boolToSimdLogical(x > y));
 }
 function testGreaterThanOrEqualInt32x4(v, w) {
-    testBinaryFunc(v, w, int32x4.greaterThanOrEqual, (x, y) => boolToSimdLogical(x >= y));
+    testBinaryCompare(v, w, int32x4.greaterThanOrEqual, (x, y) => boolToSimdLogical(x >= y));
 }
 
 function test() {
   var float32x4val = [
       float32x4(1, 20, 30, 4),
       float32x4(10, 2, 3, 40),
       float32x4(9.999, 2.1234, 30.4443, 4),
       float32x4(10, 2.1233, 30.4444, 4.0001),
@@ -70,16 +90,42 @@ function test() {
           testNotEqualFloat32x4(v, w);
           testLessThanFloat32x4(v, w);
           testLessThanOrEqualFloat32x4(v, w);
           testGreaterThanFloat32x4(v, w);
           testGreaterThanOrEqualFloat32x4(v, w);
       }
   }
 
+  var float64x2val = [
+      float64x2(1, 20),
+      float64x2(10, 2),
+      float64x2(9.999, 2.1234),
+      float64x2(10, 2.1233),
+      float64x2(30.4443, 4),
+      float64x2(30.4444, 4.0001),
+      float64x2(NaN, -Infinity),
+      float64x2(+Infinity, NaN),
+      float64x2(+Infinity, -0),
+      float64x2(-0, -Infinity),
+      float64x2(13.37, 42.42),
+      float64x2(NaN, 0)
+  ];
+
+  for (v of float64x2val) {
+      for (w of float64x2val) {
+          testEqualFloat64x2(v, w);
+          testNotEqualFloat64x2(v, w);
+          testLessThanFloat64x2(v, w);
+          testLessThanOrEqualFloat64x2(v, w);
+          testGreaterThanFloat64x2(v, w);
+          testGreaterThanOrEqualFloat64x2(v, w);
+      }
+  }
+
   var int32x4val = [
       int32x4(1, 2, 3, 4),
       int32x4(-1, -2, -3, -4),
       int32x4(-1, 2, -3, 4),
       int32x4(1, -2, 3, -4),
       int32x4(INT32_MAX, INT32_MAX, INT32_MIN, INT32_MIN),
       int32x4(INT32_MAX, INT32_MIN, INT32_MAX, INT32_MIN)
   ];
deleted file mode 100644
--- a/js/src/tests/ecma_7/SIMD/float32x4-minmax.js
+++ /dev/null
@@ -1,54 +0,0 @@
-// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
-var BUGNUMBER = 946042;
-
-var float32x4 = SIMD.float32x4;
-
-function testMaxFloat32(v, w) {
-    return testBinaryFunc(v, w, float32x4.max, (x, y) => Math.fround(Math.max(x, y)));
-}
-function testMinFloat32(v, w) {
-    return testBinaryFunc(v, w, float32x4.min, (x, y) => Math.fround(Math.min(x, y)));
-}
-
-function maxNum(x, y) {
-    if (x != x)
-        return y;
-    if (y != y)
-        return x;
-    return Math.max(x, y);
-}
-
-function minNum(x, y) {
-    if (x != x)
-        return y;
-    if (y != y)
-        return x;
-    return Math.min(x, y);
-}
-
-function testMaxNumFloat32(v, w) {
-    return testBinaryFunc(v, w, float32x4.maxNum, maxNum);
-}
-function testMinNumFloat32(v, w) {
-    return testBinaryFunc(v, w, float32x4.minNum, minNum);
-}
-
-function test() {
-  print(BUGNUMBER + ": " + summary);
-
-  for ([v, w] of [[float32x4(1, 20, 30, 4), float32x4(10, 2, 3, 40)],
-                  [float32x4(9.999, 2.1234, 30.4443, 4), float32x4(10, 2.1233, 30.4444, 4.0001)],
-                  [float32x4(NaN, -Infinity, +Infinity, -0), float32x4(13.37, 42.42, NaN, 0)]])
-  {
-      testMinFloat32(v, w);
-      testMaxFloat32(v, w);
-      testMinNumFloat32(v, w);
-      testMaxNumFloat32(v, w);
-  }
-
-  if (typeof reportCompare === "function")
-    reportCompare(true, true);
-}
-
-test();
-
deleted file mode 100644
--- a/js/src/tests/ecma_7/SIMD/float32x4clamp.js
+++ /dev/null
@@ -1,45 +0,0 @@
-// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
-var BUGNUMBER = 946042;
-var float32x4 = SIMD.float32x4;
-var int32x4 = SIMD.int32x4;
-
-var summary = 'float32x4 clamp';
-
-function test() {
-  print(BUGNUMBER + ": " + summary);
-
-  // FIXME -- Bug 1068028: Amend to check for correctness of NaN border cases once the semantics are defined.
-
-  var a = float32x4(-20, 10, 30, 0.5);
-  var lower = float32x4(2, 1, 50, 0);
-  var upper = float32x4(2.5, 5, 55, 1);
-  var c = SIMD.float32x4.clamp(a, lower, upper);
-  assertEq(c.x, 2);
-  assertEq(c.y, 5);
-  assertEq(c.z, 50);
-  assertEq(c.w, 0.5);
-
-  var d = float32x4(-13.37, 10.46, 31.79, 0.54);
-  var lower1 = float32x4(2.1, 1.1, 50.13, 0.0);
-  var upper1 = float32x4(2.56, 5.55, 55.93, 1.1);
-  var f = SIMD.float32x4.clamp(d, lower1, upper1);
-  assertEq(f.x, Math.fround(2.1));
-  assertEq(f.y, Math.fround(5.55));
-  assertEq(f.z, Math.fround(50.13));
-  assertEq(f.w, Math.fround(0.54));
-
-  var g = float32x4(4, -Infinity, 10, -10);
-  var lower2 = float32x4(5, -Infinity, -Infinity, -Infinity);
-  var upper2 = float32x4(Infinity, 5, Infinity, Infinity);
-  var i = SIMD.float32x4.clamp(g, lower2, upper2);
-  assertEq(i.x, 5);
-  assertEq(i.y, -Infinity);
-  assertEq(i.z, 10);
-  assertEq(i.w, -10);
-
-  if (typeof reportCompare === "function")
-    reportCompare(true, true);
-}
-
-test();
-
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float32x4fromfloat64x2.js
@@ -0,0 +1,60 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float32x4 = SIMD.float32x4;
+var float64x2 = SIMD.float64x2;
+
+var summary = 'float32x4 fromFloat64x2';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var a = float64x2(1, 2);
+  var c = float32x4.fromFloat64x2(a);
+  assertEq(c.x, 1);
+  assertEq(c.y, 2);
+  assertEq(c.z, 0);
+  assertEq(c.w, 0);
+
+  var d = float64x2(-0, NaN);
+  var f = float32x4.fromFloat64x2(d);
+  assertEq(f.x, -0);
+  assertEq(f.y, NaN);
+  assertEq(f.z, 0);
+  assertEq(f.w, 0);
+
+  var g = float64x2(Infinity, -Infinity);
+  var i = float32x4.fromFloat64x2(g);
+  assertEq(i.x, Infinity);
+  assertEq(i.y, -Infinity);
+  assertEq(i.z, 0);
+  assertEq(i.w, 0);
+
+  var j = Math.pow(2, 25) - 1;
+  var k = -Math.pow(2, 25);
+  var l = float64x2(j, k);
+  var m = float32x4.fromFloat64x2(l);
+  assertEq(m.x, Math.fround(j));
+  assertEq(m.y, Math.fround(k));
+  assertEq(m.z, 0);
+  assertEq(m.w, 0);
+
+  var o = Math.pow(2, 1000);
+  var p = Math.pow(2, -1000);
+  var q = float64x2(o, p);
+  var r = float32x4.fromFloat64x2(q);
+  assertEq(r.x, Math.fround(o));
+  assertEq(r.y, Math.fround(p));
+  assertEq(r.z, 0);
+  assertEq(r.w, 0);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float32x4fromfloat64x2bits.js
@@ -0,0 +1,43 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float32x4 = SIMD.float32x4;
+var float64x2 = SIMD.float64x2;
+var int32x4 = SIMD.int32x4;
+
+var summary = 'float32x4 fromFloat64x2Bits';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var a = float64x2(2.000000473111868, 512.0001225471497);
+  var b = float32x4.fromFloat64x2Bits(a);
+  assertEq(b.x, 1.0);
+  assertEq(b.y, 2.0);
+  assertEq(b.z, 3.0);
+  assertEq(b.w, 4.0);
+
+  var c = float64x2(-0, NaN);
+  var d = float32x4.fromFloat64x2Bits(c);
+  assertEq(d.x, 0);
+  assertEq(d.y, -0);
+  assertEq(d.z, 0);
+  assertEq(d.w, NaN);
+
+  var e = float64x2(Infinity, -Infinity);
+  var f = float32x4.fromFloat64x2Bits(e);
+  assertEq(f.x, 0);
+  assertEq(f.y, NaN);
+  assertEq(f.z, 0);
+  assertEq(f.w, NaN);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float64x2-arithmetic.js
@@ -0,0 +1,77 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float64x2 = SIMD.float64x2;
+
+var summary = 'float64x2 arithmetic';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function add(a, b) { return a + b; }
+function sub(a, b) { return a - b; }
+function mul(a, b) { return a * b; }
+function div(a, b) { return a / b; }
+function neg(a) { return -a; }
+function reciprocal(a) { return 1 / a; }
+function reciprocalSqrt(a) { return 1 / Math.sqrt(a); }
+
+function testAdd(v, w) {
+    return testBinaryFunc(v, w, float64x2.add, add);
+}
+function testSub(v, w) {
+    return testBinaryFunc(v, w, float64x2.sub, sub);
+}
+function testMul(v, w) {
+    return testBinaryFunc(v, w, float64x2.mul, mul);
+}
+function testDiv(v, w) {
+    return testBinaryFunc(v, w, float64x2.div, div);
+}
+function testAbs(v) {
+    return testUnaryFunc(v, float64x2.abs, Math.abs);
+}
+function testNeg(v) {
+    return testUnaryFunc(v, float64x2.neg, neg);
+}
+function testReciprocal(v) {
+    return testUnaryFunc(v, float64x2.reciprocal, reciprocal);
+}
+function testReciprocalSqrt(v) {
+    return testUnaryFunc(v, float64x2.reciprocalSqrt, reciprocalSqrt);
+}
+function testSqrt(v) {
+    return testUnaryFunc(v, float64x2.sqrt, Math.sqrt);
+}
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var v, w;
+  for ([v, w] of [[float64x2(1, 2), float64x2(3, 4)],
+                  [float64x2(1.894, 2.8909), float64x2(100.764, 200.987)],
+                  [float64x2(-1, -2), float64x2(-14.54, 57)],
+                  [float64x2(+Infinity, -Infinity), float64x2(NaN, -0)]])
+  {
+      testAdd(v, w);
+      testSub(v, w);
+      testMul(v, w);
+      testDiv(v, w);
+      testAbs(v);
+      testNeg(v);
+      testReciprocal(v);
+      testSqrt(v);
+  }
+  for (v of [float64x2(1, 0.25), float64x2(3, 0.5),
+             float64x2(-0, NaN), float64x2(+Infinity, -Infinity)])
+  {
+      testReciprocalSqrt(v);
+  }
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float64x2alignment.js
@@ -0,0 +1,30 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float64x2 = SIMD.float64x2;
+
+var summary = 'float64x2 alignment';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var StructType = TypedObject.StructType;
+var uint8 = TypedObject.uint8;
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  assertEq(float64x2.byteLength, 16);
+  assertEq(float64x2.byteAlignment, 16);
+
+  var Compound = new StructType({c: uint8, d: uint8, f: float64x2});
+  assertEq(Compound.fieldOffsets["c"], 0);
+  assertEq(Compound.fieldOffsets["d"], 1);
+  assertEq(Compound.fieldOffsets["f"], 16);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float64x2fromfloat32x4.js
@@ -0,0 +1,41 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float32x4 = SIMD.float32x4;
+var float64x2 = SIMD.float64x2;
+
+var summary = 'float64x2 fromFloat32x4';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var a = float32x4(100, 200, 300, 400);
+  var c = float64x2.fromFloat32x4(a);
+  assertEq(c.x, 100);
+  assertEq(c.y, 200);
+
+  var d = float32x4(NaN, -0, NaN, -0);
+  var f = float64x2.fromFloat32x4(d);
+  assertEq(f.x, NaN);
+  assertEq(f.y, -0);
+
+  var g = float32x4(Infinity, -Infinity, Infinity, -Infinity);
+  var i = float64x2.fromFloat32x4(g);
+  assertEq(i.x, Infinity);
+  assertEq(i.y, -Infinity);
+
+  var j = float32x4(13.37, 12.853, 49.97, 53.124);
+  var l = float64x2.fromFloat32x4(j);
+  assertEq(l.x, Math.fround(13.37));
+  assertEq(l.y, Math.fround(12.853));
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float64x2fromfloat32x4bits.js
@@ -0,0 +1,32 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float32x4 = SIMD.float32x4;
+var float64x2 = SIMD.float64x2;
+var int32x4 = SIMD.int32x4;
+
+var summary = 'float64x2 fromFloat32x4Bits';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var a = float32x4(0, 1.875, 0, 2);
+  var c = float64x2.fromFloat32x4Bits(a);
+  assertEq(c.x, 1.0);
+  assertEq(c.y, 2.0);
+
+  var d = float32x4(NaN, -0, Infinity, -Infinity);
+  var f = float64x2.fromFloat32x4Bits(d);
+  assertEq(f.x, -1.058925634e-314);
+  assertEq(f.y, -1.404448428688076e+306);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float64x2fromint32x4.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float64x2 = SIMD.float64x2;
+var int32x4 = SIMD.int32x4;
+
+var summary = 'float64x2 fromInt32x4';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var a = int32x4(1, 2, 3, 4);
+  var c = float64x2.fromInt32x4(a);
+  assertEq(c.x, 1);
+  assertEq(c.y, 2);
+
+  var d = int32x4(INT32_MAX, INT32_MIN, 0, 0);
+  var f = float64x2.fromInt32x4(d);
+  assertEq(f.x, INT32_MAX);
+  assertEq(f.y, INT32_MIN);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float64x2fromint32x4bits.js
@@ -0,0 +1,31 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float64x2 = SIMD.float64x2;
+var int32x4 = SIMD.int32x4;
+
+var summary = 'float64x2 fromInt32x4Bits';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var a = int32x4(0x00000000, 0x3ff00000, 0x0000000, 0x40000000);
+  var c = float64x2.fromInt32x4Bits(a);
+  assertEq(c.x, 1.0);
+  assertEq(c.y, 2.0);
+
+  var d = int32x4(0xabcdef12, 0x3ff00000, 0x21fedcba, 0x40000000);
+  var f = float64x2.fromInt32x4Bits(d);
+  assertEq(f.x, 1.0000006400213732);
+  assertEq(f.y, 2.0000002532866263);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float64x2getters.js
@@ -0,0 +1,47 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float64x2 = SIMD.float64x2;
+var int32x4 = SIMD.int32x4;
+
+var summary = 'float64x2 getters';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  // Create a float64x2 and check that the getters work:
+  var f = float64x2(11, 22);
+  assertEq(f.x, 11);
+  assertEq(f.y, 22);
+
+  // Test that the getters work when called reflectively:
+  var g = f.__lookupGetter__("x");
+  assertEq(g.call(f), 11);
+
+  // Test that getters cannot be applied to various incorrect things:
+  assertThrowsInstanceOf(function() {
+    g.call({})
+  }, TypeError, "Getter applicable to random objects");
+  assertThrowsInstanceOf(function() {
+    g.call(0xDEADBEEF)
+  }, TypeError, "Getter applicable to integers");
+  assertThrowsInstanceOf(function() {
+    var T = new TypedObject.StructType({x: TypedObject.float64,
+                                        y: TypedObject.float64});
+    var v = new T({x: 11, y: 22});
+    g.call(v)
+  }, TypeError, "Getter applicable to structs");
+  assertThrowsInstanceOf(function() {
+    var t = new int32x4(1, 2, 3, 4);
+    g.call(t)
+  }, TypeError, "Getter applicable to int32x4");
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float64x2handle.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float64x2 = SIMD.float64x2;
+var int32x4 = SIMD.int32x4;
+
+var summary = 'float64x2 handles';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var ArrayType = TypedObject.ArrayType;
+
+var float64 = TypedObject.float64;
+var Handle = TypedObject.Handle;
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var Array = float64x2.array(3);
+  var array = new Array([float64x2(1, 2),
+                         float64x2(3, 4),
+                         float64x2(5, 6)]);
+
+  // Test that trying to create handle into the interior of a
+  // float64x2 fails.
+
+  assertThrowsInstanceOf(function() {
+    var h = float64.handle(array, 1, "w");
+  }, TypeError, "Creating a float64 handle to prop via ctor");
+
+  assertThrowsInstanceOf(function() {
+    var h = float64.handle();
+    Handle.move(h, array, 1, "w");
+  }, TypeError, "Creating a float64 handle to prop via move");
+
+  assertThrowsInstanceOf(function() {
+    var h = float64.handle(array, 1, 0);
+  }, TypeError, "Creating a float64 handle to elem via ctor");
+
+  assertThrowsInstanceOf(function() {
+    var h = float64.handle();
+    Handle.move(h, array, 1, 0);
+  }, TypeError, "Creating a float64 handle to elem via move");
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float64x2reify.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float64x2 = SIMD.float64x2;
+
+var summary = 'float64x2 reify';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var ArrayType = TypedObject.ArrayType;
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var Array = float64x2.array(3);
+  var array = new Array([float64x2(1, 2),
+                         float64x2(3, 4),
+                         float64x2(5, 6)]);
+
+  // Test that reading array[1] produces a *copy* of float64x2, not an
+  // alias into the array.
+
+  var f = array[1];
+  assertEq(f.y, 4);
+  assertEq(array[1].y, 4);
+  array[1] = float64x2(7, 8);
+  assertEq(f.y, 4);
+  assertEq(array[1].y, 8);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float64x2setter.js
@@ -0,0 +1,45 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float64x2 = SIMD.float64x2;
+
+var summary = 'float64x2 setter';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var ArrayType = TypedObject.ArrayType;
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var Array = float64x2.array(3);
+  var array = new Array([float64x2(1, 2),
+                         float64x2(3, 4),
+                         float64x2(5, 6)]);
+  assertEq(array[1].y, 4);
+
+  // Test that we are allowed to write float64x2 values into array,
+  // but not other things.
+
+  array[1] = float64x2(7, 8);
+  assertEq(array[1].y, 8);
+
+  assertThrowsInstanceOf(function() {
+    array[1] = {x: 7, y: 8 };
+  }, TypeError, "Setting float64x2 from an object");
+
+  assertThrowsInstanceOf(function() {
+    array[1] = [ 7, 8 ];
+  }, TypeError, "Setting float64x2 from an array");
+
+  assertThrowsInstanceOf(function() {
+    array[1] = 9;
+  }, TypeError, "Setting float64x2 from a number");
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/float64x2with.js
@@ -0,0 +1,36 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float64x2 = SIMD.float64x2;
+
+var summary = 'float64x2 with';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var a = float64x2(1, 2);
+  var x = float64x2.withX(a, 5);
+  var y = float64x2.withY(a, 5);
+  assertEq(x.x, 5);
+  assertEq(x.y, 2);
+  assertEq(y.x, 1);
+  assertEq(y.y, 5);
+
+  var b = float64x2(NaN, -0);
+  var x1 = float64x2.withX(b, Infinity);
+  var y1 = float64x2.withY(b, -Infinity);
+  assertEq(x1.x, Infinity);
+  assertEq(x1.y, -0);
+  assertEq(y1.x, NaN);
+  assertEq(y1.y, -Infinity);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/int32x4fromfloat64x2.js
@@ -0,0 +1,51 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float64x2 = SIMD.float64x2;
+var int32x4 = SIMD.int32x4;
+
+var summary = 'int32x4 fromFloat64x2';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var a = float64x2(1, 2.2);
+  var c = int32x4.fromFloat64x2(a);
+  assertEq(c.x, 1);
+  assertEq(c.y, 2);
+  assertEq(c.z, 0);
+  assertEq(c.w, 0);
+
+  var d = float64x2(NaN, -0);
+  var f = int32x4.fromFloat64x2(d);
+  assertEq(f.x, 0);
+  assertEq(f.y, 0);
+  assertEq(f.z, 0);
+  assertEq(f.w, 0);
+
+  var g = float64x2(Infinity, -Infinity);
+  var i = int32x4.fromFloat64x2(g);
+  assertEq(i.x, 0);
+  assertEq(i.y, 0);
+  assertEq(i.z, 0);
+  assertEq(i.w, 0);
+
+  var j = Math.pow(2, 31);
+  var k = -Math.pow(2, 31) - 1;
+  var m = float64x2(j, k);
+  var l = int32x4.fromFloat64x2(m);
+  assertEq(l.x, INT32_MIN);
+  assertEq(l.y, INT32_MAX);
+  assertEq(l.z, 0);
+  assertEq(l.w, 0);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/int32x4fromfloat64x2bits.js
@@ -0,0 +1,49 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+var BUGNUMBER = 1031203;
+var float64x2 = SIMD.float64x2;
+var int32x4 = SIMD.int32x4;
+
+var summary = 'int32x4 fromFloat64x2Bits';
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function test() {
+  print(BUGNUMBER + ": " + summary);
+
+  var a = float64x2(1.0, 2.0);
+  var c = int32x4.fromFloat64x2Bits(a);
+  assertEq(c.x, 0x00000000);
+  assertEq(c.y, 0x3FF00000);
+  assertEq(c.z, 0x00000000);
+  assertEq(c.w, 0x40000000);
+
+  var d = float64x2(+Infinity, -Infinity);
+  var f = int32x4.fromFloat64x2Bits(d);
+  assertEq(f.x, 0x00000000);
+  assertEq(f.y, 0x7ff00000);
+  assertEq(f.z, 0x00000000);
+  assertEq(f.w, -0x100000);
+
+  var g = float64x2(-0, NaN);
+  var i = int32x4.fromFloat64x2Bits(g);
+  assertEq(i.x, 0x00000000);
+  assertEq(i.y, -0x80000000);
+  assertEq(i.z, 0x00000000);
+  assertEq(i.w, 0x7ff80000);
+
+  var j = float64x2(1.0000006400213732, 2.0000002532866263);
+  var l = int32x4.fromFloat64x2Bits(j);
+  assertEq(l.x, -0x543210ee);
+  assertEq(l.y, 0x3ff00000);
+  assertEq(l.z, 0x21fedcba);
+  assertEq(l.w, 0x40000000);
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
--- a/js/src/tests/ecma_7/SIMD/load.js
+++ b/js/src/tests/ecma_7/SIMD/load.js
@@ -1,20 +1,20 @@
 // |reftest| skip-if(!this.hasOwnProperty("SIMD"))
 
 /*
  * Any copyright is dedicated to the Public Domain.
  * https://creativecommons.org/publicdomain/zero/1.0/
  */
 
-// Our float32array will have 16 elements
-const SIZE_ARRAY = 16;
+// Our array for int32x4 and float32x4 will have 16 elements
+const SIZE_32_ARRAY = 16;
+const SIZE_64_ARRAY = 8;
 
-// 1 float32 == 4 bytes
-const SIZE_BYTES = SIZE_ARRAY * 4;
+const SIZE_BYTES = SIZE_32_ARRAY * 4;
 
 function MakeComparator(kind, arr) {
     var bpe = arr.BYTES_PER_ELEMENT;
     var uint8 = (bpe != 1) ? new Uint8Array(arr.buffer) : arr;
 
     // Size in bytes of a single element in the SIMD vector.
     var sizeOfLaneElem;
     // Typed array constructor corresponding to the SIMD kind.
@@ -23,20 +23,24 @@ function MakeComparator(kind, arr) {
       case 'int32x4':
         sizeOfLaneElem = 4;
         typedArrayCtor = Int32Array;
         break;
       case 'float32x4':
         sizeOfLaneElem = 4;
         typedArrayCtor = Float32Array;
         break;
+      case 'float64x2':
+        sizeOfLaneElem = 8;
+        typedArrayCtor = Float64Array;
+        break;
       default:
         assertEq(true, false, "unknown SIMD kind");
     }
-
+    var lanes = 16 / sizeOfLaneElem;
     // Reads (numElemToRead * sizeOfLaneElem) bytes in arr, and reinterprets
     // these bytes as a typed array equivalent to the typed SIMD vector.
     var slice = function(start, numElemToRead) {
         // Read enough bytes
         var startBytes = start * bpe;
         var endBytes = startBytes + numElemToRead * sizeOfLaneElem;
         var asArray = Array.prototype.slice.call(uint8, startBytes, endBytes);
 
@@ -44,41 +48,48 @@ function MakeComparator(kind, arr) {
         // This is needed for loadX, loadXY, loadXYZ which do only partial
         // reads.
         for (var i = asArray.length; i < SIZE_BYTES; i++) asArray[i] = 0;
         assertEq(asArray.length, SIZE_BYTES);
 
         return new typedArrayCtor(new Uint8Array(asArray).buffer);
     }
 
+    var assertFunc = (lanes == 2) ? assertEqX2 : assertEqX4;
+    var type = SIMD[kind];
     return {
         loadX: function(index) {
-            var v = SIMD[kind].loadX(arr, index);
-            assertEqX4(v, slice(index, 1));
+            var v = type.loadX(arr, index);
+            assertFunc(v, slice(index, 1));
         },
 
         loadXY: function(index) {
-            var v = SIMD[kind].loadXY(arr, index);
-            assertEqX4(v, slice(index, 2));
+            if (lanes < 4)
+                return;
+            var v = type.loadXY(arr, index);
+            assertFunc(v, slice(index, 2));
         },
 
-        loadXYZ: function(index) {
-            var v = SIMD[kind].loadXYZ(arr, index);
-            assertEqX4(v, slice(index, 3));
+       loadXYZ: function(index) {
+           if (lanes < 4)
+               return;
+           var v = type.loadXYZ(arr, index);
+           assertFunc(v, slice(index, 3));
         },
 
         load: function(index) {
-            var v = SIMD[kind].load(arr, index);
-            assertEqX4(v, slice(index, 4));
+           var v = type.load(arr, index);
+           assertFunc(v, slice(index, 4));
         }
     }
 }
 
 function testLoad(kind, TA) {
-    for (var i = SIZE_ARRAY; i--;)
+    var lanes = TA.length / 4;
+    for (var i = TA.length; i--;)
         TA[i] = i;
 
     for (var ta of [
                     new Uint8Array(TA.buffer),
                     new Int8Array(TA.buffer),
                     new Uint16Array(TA.buffer),
                     new Int16Array(TA.buffer),
                     new Uint32Array(TA.buffer),
@@ -92,17 +103,17 @@ function testLoad(kind, TA) {
         assertThrowsInstanceOf(() => SIMD[kind].load(ta), TypeError);
         assertThrowsInstanceOf(() => SIMD[kind].load("hello", 0), TypeError);
         assertThrowsInstanceOf(() => SIMD[kind].load(ta, -1), RangeError);
 
         // Valid and invalid reads
         var C = MakeComparator(kind, ta);
         var bpe = ta.BYTES_PER_ELEMENT;
 
-        var lastValidArgLoadX   = (SIZE_BYTES - 4)  / bpe | 0;
+        var lastValidArgLoadX   = (SIZE_BYTES - (lanes == 4 ? 4 : 8))  / bpe | 0;
         var lastValidArgLoadXY  = (SIZE_BYTES - 8)  / bpe | 0;
         var lastValidArgLoadXYZ = (SIZE_BYTES - 12) / bpe | 0;
         var lastValidArgLoad    = (SIZE_BYTES - 16) / bpe | 0;
 
         C.load(0);
         C.load(1);
         C.load(2);
         C.load(3);
@@ -116,39 +127,45 @@ function testLoad(kind, TA) {
         C.loadX(lastValidArgLoadX);
         assertThrowsInstanceOf(() => SIMD[kind].loadX(ta, lastValidArgLoadX + 1), RangeError);
 
         C.loadXY(0);
         C.loadXY(1);
         C.loadXY(2);
         C.loadXY(3);
         C.loadXY(lastValidArgLoadXY);
-        assertThrowsInstanceOf(() => SIMD[kind].loadXY(ta, lastValidArgLoadXY + 1), RangeError);
 
         C.loadXYZ(0);
         C.loadXYZ(1);
         C.loadXYZ(2);
         C.loadXYZ(3);
         C.loadXYZ(lastValidArgLoadXYZ);
-        assertThrowsInstanceOf(() => SIMD[kind].loadXYZ(ta, lastValidArgLoadXYZ + 1), RangeError);
+
+        if (lanes >= 4) {
+            assertThrowsInstanceOf(() => SIMD[kind].loadXY(ta, lastValidArgLoadXY + 1), RangeError);
+            assertThrowsInstanceOf(() => SIMD[kind].loadXYZ(ta, lastValidArgLoadXYZ + 1), RangeError);
+        }
     }
 
-    // Test ToInt32 behavior
-    var v = SIMD[kind].load(TA, 12.5);
-    assertEqX4(v, [12, 13, 14, 15]);
+    if (lanes == 4) {
+        // Test ToInt32 behavior
+        var v = SIMD[kind].load(TA, 12.5);
+        assertEqX4(v, [12, 13, 14, 15]);
 
-    var obj = {
-        valueOf: function() { return 12 }
+        var obj = {
+            valueOf: function() { return 12 }
+        }
+        var v = SIMD[kind].load(TA, obj);
+        assertEqX4(v, [12, 13, 14, 15]);
     }
-    var v = SIMD[kind].load(TA, obj);
-    assertEqX4(v, [12, 13, 14, 15]);
 
     var obj = {
         valueOf: function() { throw new TypeError("i ain't a number"); }
     }
     assertThrowsInstanceOf(() => SIMD[kind].load(TA, obj), TypeError);
 }
 
-testLoad('float32x4', new Float32Array(SIZE_ARRAY));
-testLoad('int32x4', new Int32Array(SIZE_ARRAY));
+testLoad('float32x4', new Float32Array(SIZE_32_ARRAY));
+testLoad('float64x2', new Float64Array(SIZE_64_ARRAY));
+testLoad('int32x4', new Int32Array(SIZE_32_ARRAY));
 
 if (typeof reportCompare === "function")
     reportCompare(true, true);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_7/SIMD/minmax.js
@@ -0,0 +1,85 @@
+// |reftest| skip-if(!this.hasOwnProperty("SIMD"))
+
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * https://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var float32x4 = SIMD.float32x4;
+var float64x2 = SIMD.float64x2;
+
+function testMaxFloat32(v, w) {
+    return testBinaryFunc(v, w, float32x4.max, (x, y) => Math.fround(Math.max(x, y)), 4);
+}
+function testMinFloat32(v, w) {
+    return testBinaryFunc(v, w, float32x4.min, (x, y) => Math.fround(Math.min(x, y)), 4);
+}
+
+function testMaxFloat64(v, w) {
+    return testBinaryFunc(v, w, float64x2.max, (x, y) => Math.max(x, y), 2);
+}
+function testMinFloat64(v, w) {
+    return testBinaryFunc(v, w, float64x2.min, (x, y) => Math.min(x, y), 2);
+}
+
+function maxNum(x, y) {
+    if (x != x)
+        return y;
+    if (y != y)
+        return x;
+    return Math.max(x, y);
+}
+
+function minNum(x, y) {
+    if (x != x)
+        return y;
+    if (y != y)
+        return x;
+    return Math.min(x, y);
+}
+
+function testMaxNumFloat32(v, w) {
+    return testBinaryFunc(v, w, float32x4.maxNum, maxNum, 4);
+}
+function testMinNumFloat32(v, w) {
+    return testBinaryFunc(v, w, float32x4.minNum, minNum, 4);
+}
+
+function testMaxNumFloat64(v, w) {
+    return testBinaryFunc(v, w, float64x2.maxNum, maxNum, 2);
+}
+function testMinNumFloat64(v, w) {
+    return testBinaryFunc(v, w, float64x2.minNum, minNum, 2);
+}
+
+function test() {
+  var v, w;
+  for ([v, w] of [[float32x4(1, 20, 30, 4), float32x4(10, 2, 3, 40)],
+                  [float32x4(9.999, 2.1234, 30.4443, 4), float32x4(10, 2.1233, 30.4444, 4.0001)],
+                  [float32x4(NaN, -Infinity, +Infinity, -0), float32x4(13.37, 42.42, NaN, 0)]])
+  {
+      testMinFloat32(v, w);
+      testMaxFloat32(v, w);
+      testMinNumFloat32(v, w);
+      testMaxNumFloat32(v, w);
+  }
+
+  for ([v, w] of [[float64x2(1, 20), float64x2(10, 2)],
+                  [float64x2(30, 4), float64x2(3, 40)],
+                  [float64x2(9.999, 2.1234), float64x2(10, 2.1233)],
+                  [float64x2(30.4443, 4), float64x2(30.4444, 4.0001)],
+                  [float64x2(NaN, -Infinity), float64x2(13.37, 42.42)],
+                  [float64x2(+Infinity, -0), float64x2(NaN, 0)]])
+  {
+      testMinFloat64(v, w);
+      testMaxFloat64(v, w);
+      testMinNumFloat64(v, w);
+      testMaxNumFloat64(v, w);
+  }
+
+  if (typeof reportCompare === "function")
+    reportCompare(true, true);
+}
+
+test();
+
--- a/js/src/tests/ecma_7/SIMD/select-bitselect.js
+++ b/js/src/tests/ecma_7/SIMD/select-bitselect.js
@@ -1,16 +1,17 @@
 // |reftest| skip-if(!this.hasOwnProperty("SIMD"))
 
 /*
  * Any copyright is dedicated to the Public Domain.
  * https://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var float32x4 = SIMD.float32x4;
+var float64x2 = SIMD.float64x2;
 var int32x4 = SIMD.int32x4;
 
 function select(mask, ifTrue, ifFalse) {
     var m = simdToArray(mask);
     var tv = simdToArray(ifTrue);
     var fv = simdToArray(ifFalse);
     return m.map(function(v, i) {
         return (v < 0 ? tv : fv)[i];
@@ -22,63 +23,63 @@ function select(mask, ifTrue, ifFalse) {
  * has 4 lanes and 2 possible values (true or false), there are 16 possible
  * masks.
  */
 function testSelect(type, inputs) {
     var x, y;
     for (var i = 0; i < 16; i++) {
         var mask = int32x4.bool(!!(i & 1), !!((i >> 1) & 1), !!((i >> 2) & 1), !!((i >> 3) & 1));
         for ([x, y] of inputs)
-            assertEqX4(type.select(mask, x, y), select(mask, x, y));
+            assertEqVec(type.select(mask, x, y), select(mask, x, y));
     }
 }
 
-function bitselect(ScalarTypedArray, mask, ifTrue, ifFalse) {
-    var m = simdToArray(mask);
-
-    var tv = new ScalarTypedArray(simdToArray(ifTrue));
-    var fv = new ScalarTypedArray(simdToArray(ifFalse));
-
-    tv = new Int32Array(tv.buffer);
-    fv = new Int32Array(fv.buffer);
-    var res = new Int32Array(4);
+function int32x4FromTypeBits(type, vec) {
+    switch (type) {
+      case float32x4:
+          return int32x4.fromFloat32x4Bits(vec);
+      case float64x2:
+          return int32x4.fromFloat64x2Bits(vec);
+      case int32x4:
+          return vec;
+      default:
+          throw new TypeError("Unknown SIMD type.");
+    }
+}
 
-    for (var i = 0; i < 4; i++) {
-        var t = 0;
-        for (var bit = 0; bit < 32; bit++) {
-            var readVal = (m[i] >> bit) & 1 ? tv[i] : fv[i];
-            var readBit = (readVal >> bit) & 1;
-            t |= readBit << bit;
-        }
-        res[i] = t;
-    }
-
-    res = new ScalarTypedArray(res.buffer);
-    return Array.prototype.map.call(res, x => x);
+function bitselect(type, mask, ifTrue, ifFalse) {
+    var tv = int32x4FromTypeBits(type, ifTrue);
+    var fv = int32x4FromTypeBits(type, ifFalse);
+    var tr = int32x4.and(mask, tv);
+    var fr = int32x4.and(int32x4.not(mask), fv);
+    var orApplied = int32x4.or(tr, fr);
+    var converted = type == int32x4 ? orApplied : type.fromInt32x4Bits(orApplied);
+    return simdToArray(converted);
 }
 
 function findCorrespondingScalarTypedArray(type) {
     switch (type) {
         case int32x4: return Int32Array;
         case float32x4: return Float32Array;
+        case float64x2: return Float64Array;
         default: throw new Error("undefined scalar typed array");
     }
 }
 
 /**
  * This tests type.bitselect on all boolean masks, as in select. For these,
  *          bitselect(mask, x, y) === select(mask, x, y)
  */
 function testBitSelectSimple(type, inputs) {
     var x, y;
     var ScalarTypedArray = findCorrespondingScalarTypedArray(type);
     for (var i = 0; i < 16; i++) {
         var mask = int32x4.bool(!!(i & 1), !!((i >> 1) & 1), !!((i >> 2) & 1), !!((i >> 3) & 1));
         for ([x, y] of inputs)
-            assertEqX4(type.bitselect(mask, x, y), bitselect(ScalarTypedArray, mask, x, y));
+            assertEqVec(type.bitselect(mask, x, y), bitselect(type, mask, x, y));
     }
 }
 
 /**
  * This tests type.bitselect on a few hand-defined masks. For these,
  *          bitselect(mask, x, y) !== select(mask, x, y)
  */
 function testBitSelectComplex(type, inputs) {
@@ -86,17 +87,17 @@ function testBitSelectComplex(type, inpu
     var masks = [
         int32x4(1337, 0x1337, 0x42, 42),
         int32x4(0x00FF1CE, 0xBAADF00D, 0xDEADBEEF, 0xCAFED00D),
         int32x4(0xD15EA5E, 0xDEADC0DE, 0xFACEB00C, 0x4B1D4B1D)
     ];
     var ScalarTypedArray = findCorrespondingScalarTypedArray(type);
     for (var mask of masks) {
         for ([x, y] of inputs)
-            assertEqX4(type.bitselect(mask, x, y), bitselect(ScalarTypedArray, mask, x, y));
+            assertEqVec(type.bitselect(mask, x, y), bitselect(type, mask, x, y));
     }
 }
 
 function test() {
     var inputs = [
         [int32x4(0,4,9,16), int32x4(1,2,3,4)],
         [int32x4(-1, 2, INT32_MAX, INT32_MIN), int32x4(INT32_MAX, -4, INT32_MIN, 42)]
     ];
@@ -110,13 +111,26 @@ function test() {
         [float32x4(-1.5,-0,NaN,-Infinity), float32x4(1,-2,13.37,3.13)],
         [float32x4(1.5,2.75,NaN,Infinity), float32x4(-NaN,-Infinity,9.75,16.125)]
     ];
 
     testSelect(float32x4, inputs);
     testBitSelectSimple(float32x4, inputs);
     testBitSelectComplex(float32x4, inputs);
 
+    inputs = [
+        [float64x2(0.125,4.25), float64x2(9.75,16.125)],
+        [float64x2(1.5,2.75), float64x2(3.25,4.5)],
+        [float64x2(-1.5,-0), float64x2(NaN,-Infinity)],
+        [float64x2(1,-2), float64x2(13.37,3.13)],
+        [float64x2(1.5,2.75), float64x2(NaN,Infinity)],
+        [float64x2(-NaN,-Infinity), float64x2(9.75,16.125)]
+    ];
+
+    testSelect(float64x2, inputs);
+    testBitSelectSimple(float64x2, inputs);
+    testBitSelectComplex(float64x2, inputs);
+
     if (typeof reportCompare === "function")
         reportCompare(true, true);
 }
 
 test();
--- a/js/src/tests/ecma_7/SIMD/shell.js
+++ b/js/src/tests/ecma_7/SIMD/shell.js
@@ -1,39 +1,99 @@
+function assertEqX2(v, arr) {
+    try {
+        assertEq(v.x, arr[0]);
+        assertEq(v.y, arr[1]);
+    } catch (e) {
+        print("stack trace:", e.stack);
+        throw e;
+    }
+}
+
 function assertEqX4(v, arr) {
     try {
         assertEq(v.x, arr[0]);
         assertEq(v.y, arr[1]);
         assertEq(v.z, arr[2]);
         assertEq(v.w, arr[3]);
     } catch (e) {
         print("stack trace:", e.stack);
         throw e;
     }
 }
 
+function simdLength(v) {
+    var pt = Object.getPrototypeOf(v);
+    if (pt === SIMD.int32x4.prototype || pt === SIMD.float32x4.prototype) {
+        return 4;
+    } else if (pt === SIMD.float64x2.prototype) {
+        return 2;
+    } else {
+        throw new TypeError("Unknown SIMD kind.");
+    }
+}
+
+function assertEqVec(v, arr) {
+    var lanes = simdLength(v);
+    if (lanes == 4)
+        assertEqX4(v, arr);
+    else if (lanes == 2)
+        assertEqX2(v, arr);
+    else
+        throw new TypeError("Unknown SIMD kind.");
+}
+
 function simdToArray(v) {
-    return [v.x, v.y, v.z, v.w];
+    var lanes = simdLength(v);
+    if (lanes == 4)
+        return [v.x, v.y, v.z, v.w];
+    else if (lanes == 2)
+        return [v.x, v.y];
+    else
+        throw new TypeError("Unknown SIMD kind.");
 }
 
 const INT32_MAX = Math.pow(2, 31) - 1;
 const INT32_MIN = -Math.pow(2, 31);
 assertEq(INT32_MAX + 1 | 0, INT32_MIN);
 
+function testUnaryFunc(v, simdFunc, func) {
+    var varr = simdToArray(v);
+
+    var observed = simdToArray(simdFunc(v));
+    var expected = varr.map(function(v, i) { return func(varr[i]); });
+
+    for (var i = 0; i < observed.length; i++)
+        assertEq(observed[i], expected[i]);
+}
+
 function testBinaryFunc(v, w, simdFunc, func) {
     var varr = simdToArray(v);
     var warr = simdToArray(w);
 
     var observed = simdToArray(simdFunc(v, w));
     var expected = varr.map(function(v, i) { return func(varr[i], warr[i]); });
 
     for (var i = 0; i < observed.length; i++)
         assertEq(observed[i], expected[i]);
 }
 
+function testBinaryCompare(v, w, simdFunc, func) {
+    var varr = simdToArray(v);
+    var warr = simdToArray(w);
+
+    var inLanes = simdLength(v);
+    var observed = simdToArray(simdFunc(v, w));
+    assertEq(observed.length, 4);
+    for (var i = 0; i < 4; i++) {
+        var j = ((i * inLanes) / 4) | 0;
+        assertEq(observed[i], func(varr[j], warr[j]));
+    }
+}
+
 function testBinaryScalarFunc(v, scalar, simdFunc, func) {
     var varr = simdToArray(v);
 
     var observed = simdToArray(simdFunc(v, scalar));
     var expected = varr.map(function(v, i) { return func(varr[i], scalar); });
 
     for (var i = 0; i < observed.length; i++)
         assertEq(observed[i], expected[i]);
--- a/js/src/tests/ecma_7/SIMD/store.js
+++ b/js/src/tests/ecma_7/SIMD/store.js
@@ -27,26 +27,28 @@ function assertChanged(ta, from, expecte
 }
 
 function testStore(ta, kind, i, v) {
     reset(ta);
     SIMD[kind].storeX(ta, i, v);
     assertChanged(ta, i, [v.x]);
 
     reset(ta);
-    SIMD[kind].storeXY(ta, i, v);
-    assertChanged(ta, i, [v.x, v.y]);
+    SIMD[kind].store(ta, i, v);
+    assertChanged(ta, i, simdToArray(v));
 
-    reset(ta);
-    SIMD[kind].storeXYZ(ta, i, v);
-    assertChanged(ta, i, [v.x, v.y, v.z]);
+    if (simdLength(v) > 2) {
+        reset(ta);
+        SIMD[kind].storeXY(ta, i, v);
+        assertChanged(ta, i, [v.x, v.y]);
 
-    reset(ta);
-    SIMD[kind].store(ta, i, v);
-    assertChanged(ta, i, [v.x, v.y, v.z, v.w]);
+        reset(ta);
+        SIMD[kind].storeXYZ(ta, i, v);
+        assertChanged(ta, i, [v.x, v.y, v.z]);
+    }
 }
 
 function testStoreInt32x4() {
     var I32 = new Int32Array(16);
 
     var v = SIMD.int32x4(0, 1, Math.pow(2,31) - 1, -Math.pow(2, 31));
     testStore(I32, 'int32x4', 0, v);
     testStore(I32, 'int32x4', 1, v);
@@ -73,13 +75,37 @@ function testStoreFloat32x4() {
     testStore(F32, 'float32x4', 2, v);
     testStore(F32, 'float32x4', 12, v);
 
     assertThrowsInstanceOf(() => SIMD.float32x4.store(F32), TypeError);
     assertThrowsInstanceOf(() => SIMD.float32x4.store(F32, 0), TypeError);
     assertThrowsInstanceOf(() => SIMD.int32x4.store(F32, 0, v), TypeError);
 }
 
+function testStoreFloat64x2() {
+    var F64 = new Float64Array(16);
+
+    var v = SIMD.float64x2(1, 2);
+    testStore(F64, 'float64x2', 0, v);
+    testStore(F64, 'float64x2', 1, v);
+    testStore(F64, 'float64x2', 14, v);
+
+    var v = SIMD.float64x2(NaN, -0);
+    testStore(F64, 'float64x2', 0, v);
+    testStore(F64, 'float64x2', 1, v);
+    testStore(F64, 'float64x2', 14, v);
+
+    var v = SIMD.float64x2(-Infinity, +Infinity);
+    testStore(F64, 'float64x2', 0, v);
+    testStore(F64, 'float64x2', 1, v);
+    testStore(F64, 'float64x2', 14, v);
+
+    assertThrowsInstanceOf(() => SIMD.float64x2.store(F64), TypeError);
+    assertThrowsInstanceOf(() => SIMD.float64x2.store(F64, 0), TypeError);
+    assertThrowsInstanceOf(() => SIMD.float32x4.store(F64, 0, v), TypeError);
+}
+
 testStoreInt32x4();
 testStoreFloat32x4();
+testStoreFloat64x2();
 
 if (typeof reportCompare === "function")
     reportCompare(true, true);
--- a/js/src/tests/ecma_7/SIMD/swizzle-shuffle.js
+++ b/js/src/tests/ecma_7/SIMD/swizzle-shuffle.js
@@ -1,47 +1,85 @@
 // |reftest| skip-if(!this.hasOwnProperty("SIMD"))
 
 /*
  * Any copyright is dedicated to the Public Domain.
  * https://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var float32x4 = SIMD.float32x4;
+var float64x2 = SIMD.float64x2;
 var int32x4 = SIMD.int32x4;
 
-function swizzle(arr, x, y, z, w) {
+function swizzle2(arr, x, y) {
+    return [arr[x], arr[y]];
+}
+
+function swizzle4(arr, x, y, z, w) {
     return [arr[x], arr[y], arr[z], arr[w]];
 }
 
+function getNumberOfLanesFromType(type) {
+    switch (type) {
+      case float32x4:
+      case int32x4:
+        return 4;
+      case float64x2:
+        return 2;
+    }
+    throw new TypeError("Unknown SIMD type.");
+}
+
 function testSwizzleForType(type) {
-    var v = type(1,2,3,4);
+    var lanes = getNumberOfLanesFromType(type);
+    var v = lanes == 4 ? type(1, 2, 3, 4) : type(1, 2);
 
     assertThrowsInstanceOf(() => type.swizzle()               , TypeError);
     assertThrowsInstanceOf(() => type.swizzle(v, 0)           , TypeError);
-    assertThrowsInstanceOf(() => type.swizzle(v, 0, 1)        , TypeError);
     assertThrowsInstanceOf(() => type.swizzle(v, 0, 1, 2)     , TypeError);
     assertThrowsInstanceOf(() => type.swizzle(v, 0, 1, 2, 4)  , TypeError);
     assertThrowsInstanceOf(() => type.swizzle(v, 0, 1, 2, -1) , TypeError);
     assertThrowsInstanceOf(() => type.swizzle(0, 1, 2, 3, v)  , TypeError);
 
+    if (lanes == 2) {
+        assertThrowsInstanceOf(() => type.swizzle(v, 0, -1)   , TypeError);
+        assertThrowsInstanceOf(() => type.swizzle(v, 0, 2)    , TypeError);
+    } else {
+        assertEq(lanes, 4);
+        assertThrowsInstanceOf(() => type.swizzle(v, 0, 1), TypeError);
+    }
+
     // Test all possible swizzles.
-    var x, y, z, w;
-    for (var i = 0; i < Math.pow(4, 4); i++) {
-        [x, y, z, w] = [i & 3, (i >> 2) & 3, (i >> 4) & 3, (i >> 6) & 3];
-        assertEqX4(type.swizzle(v, x, y, z, w), swizzle(simdToArray(v), x, y, z, w));
+    if (lanes == 4) {
+        var x, y, z, w;
+        for (var i = 0; i < Math.pow(4, 4); i++) {
+            [x, y, z, w] = [i & 3, (i >> 2) & 3, (i >> 4) & 3, (i >> 6) & 3];
+            assertEqVec(type.swizzle(v, x, y, z, w), swizzle4(simdToArray(v), x, y, z, w));
+        }
+    } else {
+        assertEq(lanes, 2);
+        var x, y;
+        for (var i = 0; i < Math.pow(2, 2); i++) {
+          [x, y] = [x & 1, (y >> 1) & 1];
+          assertEqVec(type.swizzle(v, x, y), swizzle2(simdToArray(v), x, y));
+        }
     }
 
     // Test that the lane inputs are converted into an int32.
     // n.b, order of evaluation of args is left-to-right.
     var obj = {
         x: 0,
         valueOf: function() { return this.x++ }
     };
-    assertEqX4(type.swizzle(v, obj, obj, obj, obj), swizzle(simdToArray(v), 0, 1, 2, 3));
+    if (lanes == 4) {
+        assertEqVec(type.swizzle(v, obj, obj, obj, obj), swizzle4(simdToArray(v), 0, 1, 2, 3));
+    } else {
+        assertEq(lanes, 2);
+        assertEqVec(type.swizzle(v, obj, obj), swizzle2(simdToArray(v), 0, 1));
+    }
 
     // Object for which ToInt32 will fail.
     obj = {
         valueOf: function() { throw new Error; }
     };
     assertThrowsInstanceOf(() => type.swizzle(v, 0, 1, 2, obj), Error);
 }
 
@@ -60,52 +98,98 @@ function testSwizzleFloat32x4() {
 
     assertThrowsInstanceOf(function() {
         int32x4.swizzle(v, 0, 0, 0, 0);
     }, TypeError);
 
     testSwizzleForType(float32x4);
 }
 
-function shuffle(lhsa, rhsa, x, y, z, w) {
+function testSwizzleFloat64x2() {
+    var v = float64x2(1, 2);
+
+    assertThrowsInstanceOf(function() {
+        float32x4.swizzle(v, 0, 0, 0, 0);
+    }, TypeError);
+
+   testSwizzleForType(float64x2);
+}
+
+function shuffle2(lhsa, rhsa, x, y) {
+    return [(x < 2 ? lhsa : rhsa)[x % 2],
+            (y < 2 ? lhsa : rhsa)[y % 2]];
+}
+function shuffle4(lhsa, rhsa, x, y, z, w) {
     return [(x < 4 ? lhsa : rhsa)[x % 4],
             (y < 4 ? lhsa : rhsa)[y % 4],
             (z < 4 ? lhsa : rhsa)[z % 4],
             (w < 4 ? lhsa : rhsa)[w % 4]];
 }
 
 function testShuffleForType(type) {
-    var lhs = type(1,2,3,4);
-    var rhs = type(5,6,7,8);
+    var lanes = getNumberOfLanesFromType(type);
+    var lhs, rhs;
+    if (lanes == 4) {
+        lhs = type(1, 2, 3, 4);
+        rhs = type(5, 6, 7, 8);
+    } else {
+        assertEq(lanes, 2);
+        lhs = type(1, 2);
+        rhs = type(3, 4);
+    }
 
     assertThrowsInstanceOf(() => type.shuffle(lhs)                   , TypeError);
     assertThrowsInstanceOf(() => type.shuffle(lhs, rhs)              , TypeError);
     assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0)           , TypeError);
-    assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1)        , TypeError);
     assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1, 2)     , TypeError);
     assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1, 2, -1) , TypeError);
     assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1, 2, 8)  , TypeError);
     assertThrowsInstanceOf(() => type.shuffle(lhs, 0, 1, 2, 7, rhs)  , TypeError);
 
+    if (lanes == 2) {
+        assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 4)    , TypeError);
+        assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, -1)   , TypeError);
+    } else {
+        assertEq(lanes, 4);
+        assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1) , TypeError);
+    }
+
     // Test all possible shuffles.
     var x, y, z, w;
-    for (var i = 0; i < Math.pow(8, 4); i++) {
-        [x, y, z, w] = [i & 7, (i >> 3) & 7, (i >> 6) & 7, (i >> 9) & 7];
-        assertEqX4(type.shuffle(lhs, rhs, x, y, z, w),
-                   shuffle(simdToArray(lhs), simdToArray(rhs), x, y, z, w));
+    if (lanes == 4) {
+        var x, y, z, w;
+        for (var i = 0; i < Math.pow(8, 4); i++) {
+            [x, y, z, w] = [i & 7, (i >> 3) & 7, (i >> 6) & 7, (i >> 9) & 7];
+            assertEqVec(type.shuffle(lhs, rhs, x, y, z, w),
+                        shuffle4(simdToArray(lhs), simdToArray(rhs), x, y, z, w));
+        }
+    } else {
+        assertEq(lanes, 2);
+        var x, y;
+        for (var i = 0; i < Math.pow(4, 2); i++) {
+            [x, y] = [i & 3, (i >> 3) & 3];
+            assertEqVec(type.shuffle(lhs, rhs, x, y),
+                        shuffle2(simdToArray(lhs), simdToArray(rhs), x, y));
+        }
     }
 
     // Test that the lane inputs are converted into an int32.
     // n.b, order of evaluation of args is left-to-right.
     var obj = {
         x: 0,
         valueOf: function() { return this.x++ }
     };
-    assertEqX4(type.shuffle(lhs, rhs, obj, obj, obj, obj),
-               shuffle(simdToArray(lhs),simdToArray(rhs), 0, 1, 2, 3));
+    if (lanes == 4) {
+        assertEqVec(type.shuffle(lhs, rhs, obj, obj, obj, obj),
+                    shuffle4(simdToArray(lhs), simdToArray(rhs), 0, 1, 2, 3));
+    } else {
+        assertEq(lanes, 2);
+        assertEqVec(type.shuffle(lhs, rhs, obj, obj),
+                    shuffle2(simdToArray(lhs), simdToArray(rhs), 0, 1));
+    }
 
     // Object for which ToInt32 will fail.
     obj = {
         valueOf: function() { throw new Error; }
     };
     assertThrowsInstanceOf(() => type.shuffle(lhs, rhs, 0, 1, 2, obj), Error);
 }
 
@@ -124,15 +208,27 @@ function testShuffleFloat32x4() {
 
     assertThrowsInstanceOf(function() {
         int32x4.shuffle(v, v, 0, 0, 0, 0);
     }, TypeError);
 
     testShuffleForType(float32x4);
 }
 
+function testShuffleFloat64x2() {
+    var v = float64x2(1, 2);
+
+    assertThrowsInstanceOf(function() {
+        float32x4.shuffle(v, v, 0, 0, 0, 0);
+    }, TypeError);
+
+    testShuffleForType(float64x2);
+}
+
 testSwizzleInt32x4();
 testSwizzleFloat32x4();
+testSwizzleFloat64x2();
 testShuffleInt32x4();
 testShuffleFloat32x4();
+testShuffleFloat64x2();
 
 if (typeof reportCompare === "function")
     reportCompare(true, true);
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -74,16 +74,17 @@
     macro(false, false_, "false") \
     macro(fieldOffsets, fieldOffsets, "fieldOffsets") \
     macro(fieldTypes, fieldTypes, "fieldTypes") \
     macro(fileName, fileName, "fileName") \
     macro(fix, fix, "fix") \
     macro(float32, float32, "float32") \
     macro(float32x4, float32x4, "float32x4") \
     macro(float64, float64, "float64") \
+    macro(float64x2, float64x2, "float64x2") \
     macro(forceInterpreter, forceInterpreter, "forceInterpreter") \
     macro(format, format, "format") \
     macro(frame, frame, "frame") \
     macro(from, from, "from") \
     macro(get, get, "get") \
     macro(getInternals, getInternals, "getInternals") \
     macro(getOwnPropertyDescriptor, getOwnPropertyDescriptor, "getOwnPropertyDescriptor") \
     macro(getOwnPropertyNames, getOwnPropertyNames, "getOwnPropertyNames") \
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -99,18 +99,19 @@ class GlobalObject : public NativeObject
     static const unsigned NUMBER_FORMAT_PROTO     = COLLATOR_PROTO + 1;
     static const unsigned DATE_TIME_FORMAT_PROTO  = NUMBER_FORMAT_PROTO + 1;
     static const unsigned REGEXP_STATICS          = DATE_TIME_FORMAT_PROTO + 1;
     static const unsigned WARNED_WATCH_DEPRECATED = REGEXP_STATICS + 1;
     static const unsigned WARNED_PROTO_SETTING_SLOW = WARNED_WATCH_DEPRECATED + 1;
     static const unsigned RUNTIME_CODEGEN_ENABLED = WARNED_PROTO_SETTING_SLOW + 1;
     static const unsigned DEBUGGERS               = RUNTIME_CODEGEN_ENABLED + 1;
     static const unsigned INTRINSICS              = DEBUGGERS + 1;
-    static const unsigned FLOAT32X4_TYPE_DESCR   = INTRINSICS + 1;
-    static const unsigned INT32X4_TYPE_DESCR     = FLOAT32X4_TYPE_DESCR + 1;
+    static const unsigned FLOAT32X4_TYPE_DESCR    = INTRINSICS + 1;
+    static const unsigned FLOAT64X2_TYPE_DESCR    = FLOAT32X4_TYPE_DESCR + 1;
+    static const unsigned INT32X4_TYPE_DESCR      = FLOAT64X2_TYPE_DESCR + 1;
     static const unsigned FOR_OF_PIC_CHAIN        = INT32X4_TYPE_DESCR + 1;
 
     /* Total reserved-slot count for global objects. */
     static const unsigned RESERVED_SLOTS = FOR_OF_PIC_CHAIN + 1;
 
     /*
      * The slot count must be in the public API for JSCLASS_GLOBAL_FLAGS, and
      * we won't expose GlobalObject, so just assert that the two values are
@@ -403,16 +404,26 @@ class GlobalObject : public NativeObject
         setSlot(FLOAT32X4_TYPE_DESCR, ObjectValue(obj));
     }
 
     JSObject &float32x4TypeDescr() {
         MOZ_ASSERT(getSlotRef(FLOAT32X4_TYPE_DESCR).isObject());
         return getSlotRef(FLOAT32X4_TYPE_DESCR).toObject();
     }
 
+    void setFloat64x2TypeDescr(JSObject &obj) {
+        MOZ_ASSERT(getSlotRef(FLOAT64X2_TYPE_DESCR).isUndefined());
+        setSlot(FLOAT64X2_TYPE_DESCR, ObjectValue(obj));
+    }
+
+    JSObject &float64x2TypeDescr() {
+        MOZ_ASSERT(getSlotRef(FLOAT64X2_TYPE_DESCR).isObject());
+        return getSlotRef(FLOAT64X2_TYPE_DESCR).toObject();
+    }
+
     void setInt32x4TypeDescr(JSObject &obj) {
         MOZ_ASSERT(getSlotRef(INT32X4_TYPE_DESCR).isUndefined());
         setSlot(INT32X4_TYPE_DESCR, ObjectValue(obj));
     }
 
     JSObject &int32x4TypeDescr() {
         MOZ_ASSERT(getSlotRef(INT32X4_TYPE_DESCR).isObject());
         return getSlotRef(INT32X4_TYPE_DESCR).toObject();
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -964,16 +964,17 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("TypedObjectIsAttached",          js::TypedObjectIsAttached, 1, 0),
     JS_FN("TypedObjectTypeDescr",           js::TypedObjectTypeDescr, 1, 0),
     JS_FN("ObjectIsOpaqueTypedObject",      js::ObjectIsOpaqueTypedObject, 1, 0),
     JS_FN("TypeDescrIsArrayType",           js::TypeDescrIsArrayType, 1, 0),
     JS_FN("TypeDescrIsSimpleType",          js::TypeDescrIsSimpleType, 1, 0),
     JS_FN("ClampToUint8",                   js::ClampToUint8, 1, 0),
     JS_FN("GetTypedObjectModule",           js::GetTypedObjectModule, 0, 0),
     JS_FN("GetFloat32x4TypeDescr",          js::GetFloat32x4TypeDescr, 0, 0),
+    JS_FN("GetFloat64x2TypeDescr",          js::GetFloat64x2TypeDescr, 0, 0),
     JS_FN("GetInt32x4TypeDescr",            js::GetInt32x4TypeDescr, 0, 0),
 
 #define LOAD_AND_STORE_SCALAR_FN_DECLS(_constant, _type, _name)         \
     JS_FN("Store_" #_name, js::StoreScalar##_type::Func, 3, 0),         \
     JS_FN("Load_" #_name,  js::LoadScalar##_type::Func, 3, 0),
     JS_FOR_EACH_UNIQUE_SCALAR_TYPE_REPR_CTYPE(LOAD_AND_STORE_SCALAR_FN_DECLS)
 #undef LOAD_AND_STORE_SCALAR_FN_DECLS
 
--- a/layout/base/SelectionCarets.cpp
+++ b/layout/base/SelectionCarets.cpp
@@ -579,28 +579,40 @@ SelectionCarets::SelectWord()
   if (!selectable) {
     SELECTIONCARETS_LOG(" frame %p is not selectable", ptFrame);
     return NS_ERROR_FAILURE;
   }
 
   nsPoint ptInFrame = mDownPoint;
   nsLayoutUtils::TransformPoint(rootFrame, ptFrame, ptInFrame);
 
-  // If target frame is editable, we should move focus to targe frame. If
-  // target frame isn't editable and our focus content is editable, we should
+  nsIFrame* currFrame = ptFrame;
+  nsIContent* newFocusContent = nullptr;
+  while (currFrame) {
+    int32_t tabIndexUnused = 0;
+    if (currFrame->IsFocusable(&tabIndexUnused, true)) {
+      newFocusContent = currFrame->GetContent();
+      nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(newFocusContent));
+      if (domElement)
+        break;
+    }
+    currFrame = currFrame->GetParent();
+  }
+
+
+  // If target frame is focusable, we should move focus to it. If target frame
+  // isn't focusable, and our previous focused content is editable, we should
   // clear focus.
   nsFocusManager* fm = nsFocusManager::GetFocusManager();
   nsIContent* editingHost = ptFrame->GetContent()->GetEditingHost();
-  if (editingHost) {
-    nsCOMPtr<nsIDOMElement> elt = do_QueryInterface(editingHost->GetParent());
-    if (elt) {
-      fm->SetFocus(elt, 0);
-    }
+  if (newFocusContent && currFrame) {
+    nsCOMPtr<nsIDOMElement> domElement(do_QueryInterface(newFocusContent));
+    fm->SetFocus(domElement,0);
 
-    if (!nsContentUtils::HasNonEmptyTextContent(
+    if (editingHost && !nsContentUtils::HasNonEmptyTextContent(
           editingHost, nsContentUtils::eRecurseIntoChildren)) {
       SELECTIONCARETS_LOG("Select a editable content %p with empty text",
                           editingHost);
       // Long tap on the content with empty text, no action for
       // selectioncarets but need to dispatch the touchcarettap event
       // to support the short cut mode
       DispatchCustomEvent(NS_LITERAL_STRING("touchcarettap"));
       return NS_OK;
--- a/layout/base/tests/marionette/test_selectioncarets.py
+++ b/layout/base/tests/marionette/test_selectioncarets.py
@@ -34,37 +34,53 @@ class SelectionCaretsTest(MarionetteTest
         self.marionette.navigate(test_html)
 
         self._input = self.marionette.find_element(*self._input_selector)
         self._textarea = self.marionette.find_element(*self._textarea_selector)
         self._textarea_rtl = self.marionette.find_element(*self._textarea_rtl_selector)
         self._contenteditable = self.marionette.find_element(*self._contenteditable_selector)
         self._content = self.marionette.find_element(*self._content_selector)
 
-    def _long_press_to_select_first_word(self, el, sel):
-        # Move caret inside the first word.
+    def _first_word_location(self, el):
+        '''Get the location (x, y) of the first word in el.
+
+        Note: this function has a side effect which changes focus to the
+        target element el.
+
+        '''
+        sel = SelectionManager(el)
+
+        # Move caret behind the first character to get the location of the first
+        # word.
         el.tap()
         sel.move_caret_to_front()
         sel.move_caret_by_offset(1)
-        x, y = sel.caret_location()
+
+        return sel.caret_location()
+
+    def _long_press_to_select(self, el, x, y):
+        '''Long press the location (x, y) to select a word.
 
-        # Long press the caret position. Selection carets should appear, and the
-        # first word will be selected. On Windows, those spaces after the word
-        # will also be selected.
-        long_press_without_contextmenu(self.marionette, el, self._long_press_time, x, y)
+        SelectionCarets should appear. On Windows, those spaces after the
+        word will also be selected.
+
+        '''
+        long_press_without_contextmenu(self.marionette, el, self._long_press_time,
+                                       x, y)
 
     def _test_long_press_to_select_a_word(self, el, assertFunc):
         sel = SelectionManager(el)
         original_content = sel.content
         words = original_content.split()
         self.assertTrue(len(words) >= 2, 'Expect at least two words in the content.')
         target_content = words[0]
 
         # Goal: Select the first word.
-        self._long_press_to_select_first_word(el, sel)
+        x, y = self._first_word_location(el)
+        self._long_press_to_select(el, x, y)
 
         # Ignore extra spaces selected after the word.
         assertFunc(target_content, sel.selected_content.rstrip())
 
     def _test_move_selection_carets(self, el, assertFunc):
         sel = SelectionManager(el)
         original_content = sel.content
         words = original_content.split()
@@ -74,60 +90,98 @@ class SelectionCaretsTest(MarionetteTest
         target_content = original_content[len(words[0]):]
 
         # Get the location of the selection carets at the end of the content for
         # later use.
         el.tap()
         sel.select_all()
         (_, _), (end_caret_x, end_caret_y) = sel.selection_carets_location()
 
-        self._long_press_to_select_first_word(el, sel)
+        x, y = self._first_word_location(el)
+        self._long_press_to_select(el, x, y)
 
         # Move the right caret to the end of the content.
         (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location()
         self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform()
 
         # Move the left caret to the previous position of the right caret.
         self.actions.flick(el, caret1_x, caret2_y, caret2_x, caret2_y).perform()
 
         # Ignore extra spaces at the beginning of the content in comparison.
         assertFunc(target_content.lstrip(), sel.selected_content.lstrip())
 
-    def _test_minimum_select_one_character(self, el, assertFunc):
+    def _test_minimum_select_one_character(self, el, assertFunc,
+                                           x=None, y=None):
         sel = SelectionManager(el)
         original_content = sel.content
         words = original_content.split()
         self.assertTrue(len(words) >= 1, 'Expect at least one word in the content.')
 
         # Goal: Select the first character.
         target_content = original_content[0]
 
-        self._long_press_to_select_first_word(el, sel)
+        if x and y:
+            # If we got x and y from the arguments, use it as a hint of the
+            # location of the first word
+            pass
+        else:
+            x, y = self._first_word_location(el)
+        self._long_press_to_select(el, x, y)
 
         # Move the right caret to the position of the left caret.
         (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.selection_carets_location()
         self.actions.flick(el, caret2_x, caret2_y, caret1_x, caret1_y,).perform()
 
         assertFunc(target_content, sel.selected_content)
 
+    def _test_focus_obtained_by_long_press(self, el1, el2):
+        '''Test the focus could be changed from el1 to el2 by long press.
+
+        If the focus is changed to e2 successfully, SelectionCarets should
+        appear and could be dragged.
+
+        '''
+        # Goal: Tap to focus el1, and then select the first character on
+        # el2.
+
+        # We want to collect the location of the first word in el2 here
+        # since self._first_word_location() has the side effect which would
+        # change the focus.
+        x, y = self._first_word_location(el2)
+        el1.tap()
+        self._test_minimum_select_one_character(el2, self.assertEqual,
+                                                x=x, y=y)
+
     ########################################################################
     # <input> test cases with selection carets enabled
     ########################################################################
     def test_input_long_press_to_select_a_word(self):
         self.openTestHtml(enabled=True)
         self._test_long_press_to_select_a_word(self._input, self.assertEqual)
 
     def test_input_move_selection_carets(self):
         self.openTestHtml(enabled=True)
         self._test_move_selection_carets(self._input, self.assertEqual)
 
     def test_input_minimum_select_one_caracter(self):
         self.openTestHtml(enabled=True)
         self._test_minimum_select_one_character(self._input, self.assertEqual)
 
+    def test_input_focus_obtained_by_long_press_from_textarea(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._textarea, self._input)
+
+    def test_input_focus_obtained_by_long_press_from_contenteditable(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._contenteditable, self._input)
+
+    def test_input_focus_obtained_by_long_press_from_content_non_editable(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._content, self._input)
+
     ########################################################################
     # <input> test cases with selection carets disabled
     ########################################################################
     def test_input_long_press_to_select_a_word_disabled(self):
         self.openTestHtml(enabled=False)
         self._test_long_press_to_select_a_word(self._input, self.assertNotEqual)
 
     def test_input_move_selection_carets_disabled(self):
@@ -144,16 +198,28 @@ class SelectionCaretsTest(MarionetteTest
     def test_textarea_move_selection_carets(self):
         self.openTestHtml(enabled=True)
         self._test_move_selection_carets(self._textarea, self.assertEqual)
 
     def test_textarea_minimum_select_one_caracter(self):
         self.openTestHtml(enabled=True)
         self._test_minimum_select_one_character(self._textarea, self.assertEqual)
 
+    def test_textarea_focus_obtained_by_long_press_from_input(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._input, self._textarea)
+
+    def test_textarea_focus_obtained_by_long_press_from_contenteditable(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._contenteditable, self._textarea)
+
+    def test_textarea_focus_obtained_by_long_press_from_content_non_editable(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._content, self._textarea)
+
     ########################################################################
     # <textarea> test cases with selection carets disabled
     ########################################################################
     def test_textarea_long_press_to_select_a_word_disabled(self):
         self.openTestHtml(enabled=False)
         self._test_long_press_to_select_a_word(self._textarea, self.assertNotEqual)
 
     def test_textarea_move_selection_carets_disable(self):
@@ -196,16 +262,28 @@ class SelectionCaretsTest(MarionetteTest
     def test_contenteditable_move_selection_carets(self):
         self.openTestHtml(enabled=True)
         self._test_move_selection_carets(self._contenteditable, self.assertEqual)
 
     def test_contenteditable_minimum_select_one_character(self):
         self.openTestHtml(enabled=True)
         self._test_minimum_select_one_character(self._contenteditable, self.assertEqual)
 
+    def test_contenteditable_focus_obtained_by_long_press_from_input(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._input, self._contenteditable)
+
+    def test_contenteditable_focus_obtained_by_long_press_from_textarea(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._textarea, self._contenteditable)
+
+    def test_contenteditable_focus_obtained_by_long_press_from_content_non_editable(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._content, self._contenteditable)
+
     ########################################################################
     # <div> contenteditable test cases with selection carets disabled
     ########################################################################
     def test_contenteditable_long_press_to_select_a_word_disabled(self):
         self.openTestHtml(enabled=False)
         self._test_long_press_to_select_a_word(self._contenteditable, self.assertNotEqual)
 
     def test_contenteditable_move_selection_carets_disabled(self):
@@ -213,8 +291,21 @@ class SelectionCaretsTest(MarionetteTest
         self._test_move_selection_carets(self._contenteditable, self.assertNotEqual)
 
     ########################################################################
     # <div> non-editable test cases with selection carets enabled
     ########################################################################
     def test_content_non_editable_minimum_select_one_character_by_selection(self):
         self.openTestHtml(enabled=True)
         self._test_minimum_select_one_character(self._content, self.assertEqual)
+
+    def test_content_non_editable_focus_obtained_by_long_press_from_input(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._input, self._content)
+
+    def test_content_non_editable_focus_obtained_by_long_press_from_textarea(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._textarea, self._content)
+
+    def test_content_non_editable_focus_obtained_by_long_press_from_contenteditable(self):
+        self.openTestHtml(enabled=True)
+        self._test_focus_obtained_by_long_press(self._contenteditable, self._content)
+
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -2920,18 +2920,34 @@ nsCSSRuleProcessor::MediumFeaturesChange
   }
 
   return false;
 }
 
 UniquePtr<nsMediaQueryResultCacheKey>
 nsCSSRuleProcessor::CloneMQCacheKey()
 {
+  MOZ_ASSERT(!(mRuleCascades && mPreviousCacheKey));
+
   RuleCascadeData* c = mRuleCascades;
-  if (!c || !c->mCacheKey.HasFeatureConditions()) {
+  if (!c) {
+    // We might have an mPreviousCacheKey.  It already comes from a call
+    // to CloneMQCacheKey, so don't bother checking
+    // HasFeatureConditions().
+    if (mPreviousCacheKey) {
+      NS_ASSERTION(mPreviousCacheKey->HasFeatureConditions(),
+                   "we shouldn't have a previous cache key unless it has "
+                   "feature conditions");
+      return MakeUnique<nsMediaQueryResultCacheKey>(*mPreviousCacheKey);
+    }
+
+    return UniquePtr<nsMediaQueryResultCacheKey>();
+  }
+
+  if (!c->mCacheKey.HasFeatureConditions()) {
     return UniquePtr<nsMediaQueryResultCacheKey>();
   }
 
   return MakeUnique<nsMediaQueryResultCacheKey>(c->mCacheKey);
 }
 
 /* virtual */ size_t
 nsCSSRuleProcessor::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
--- a/layout/style/test/test_bug1089417.html
+++ b/layout/style/test/test_bug1089417.html
@@ -19,16 +19,23 @@ https://bugzilla.mozilla.org/show_bug.cg
     var fwin = f.contentWindow;
     var fdoc = f.contentDocument;
 
     f.height = "400";
     fdoc.getElementById("s").disabled = false;
     is(fwin.getComputedStyle(fdoc.documentElement).backgroundColor,
        "rgb(0, 128, 0)",
        "media query change should have restyled");
+
+    f.height = "200";
+    fdoc.getElementById("s").disabled = true;
+    fdoc.getElementById("s").disabled = false;
+    is(fwin.getComputedStyle(fdoc.documentElement).backgroundColor,
+       "rgb(255, 0, 0)",
+       "media query change should have restyled");
     SimpleTest.finish();
   }
 
   </script>
 </head>
 <body onload="run()">
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1089417">Mozilla Bug 1089417</a>
 <div id="display">
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -3747,23 +3747,30 @@ pref("browser.zoom.reflowZoom.reflowTime
  * but has no effect if browser.zoom.reflowOnZoom is disabled.
  *
  * Note that this should be turned off only in cases where debugging of the
  * reflow-on-zoom feature is necessary, and enabling the feature during
  * a page load inhbits this debugging.
  */
 pref("browser.zoom.reflowZoom.reflowTextOnPageLoad", true);
 
+//
 // Image-related prefs
+//
+
 // The maximum size, in bytes, of the decoded images we cache
 pref("image.cache.size", 5242880);
+
 // A weight, from 0-1000, to place on time when comparing to size.
 // Size is given a weight of 1000 - timeweight.
 pref("image.cache.timeweight", 500);
 
+// Whether we attempt to downscale images during decoding.
+pref("image.downscale-during-decode.enabled", false);
+
 // The default Accept header sent for images loaded over HTTP(S)
 pref("image.http.accept", "image/png,image/*;q=0.8,*/*;q=0.5");
 
 pref("image.high_quality_downscaling.enabled", true);
 
 // The minimum percent downscaling we'll use high-quality downscaling on,
 // interpreted as a floating-point number / 1000.
 pref("image.high_quality_downscaling.min_factor", 1000);
@@ -3787,17 +3794,17 @@ pref("image.mem.decodeondraw", true);
 // Allows image locking of decoded image data in content processes.
 pref("image.mem.allow_locking_in_content_processes", true);
 
 // Chunk size for calls to the image decoders
 pref("image.mem.decode_bytes_at_a_time", 16384);
 
 // Minimum timeout for expiring unused images from the surface cache, in
 // milliseconds. This controls how long we store cached temporary surfaces.
-pref("image.mem.surfacecache.min_expiration_ms", 60000); // 60ms
+pref("image.mem.surfacecache.min_expiration_ms", 60000); // 60s
 
 // Maximum size for the surface cache, in kilobytes.
 pref("image.mem.surfacecache.max_size_kb", 1048576); // 1GB
 
 // The surface cache's size, within the constraints of the maximum size set
 // above, is determined as a fraction of main memory size. The size factor is
 // interpreted as a reciprocal, so a size factor of 4 means to use no more than
 // 1/4 of main memory.  The default should be a good balance for most systems.
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -1361,16 +1361,25 @@ HttpBaseChannel::IsNoCacheResponse(bool 
     return NS_ERROR_NOT_AVAILABLE;
   *value = mResponseHead->NoCache();
   if (!*value)
     *value = mResponseHead->ExpiresInPast();
   return NS_OK;
 }
 
 NS_IMETHODIMP
+HttpBaseChannel::IsPrivateResponse(bool *value)
+{
+  if (!mResponseHead)
+    return NS_ERROR_NOT_AVAILABLE;
+  *value = mResponseHead->Private();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 HttpBaseChannel::GetResponseStatus(uint32_t *aValue)
 {
   if (!mResponseHead)
     return NS_ERROR_NOT_AVAILABLE;
   *aValue = mResponseHead->Status();
   return NS_OK;
 }
 
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -148,16 +148,17 @@ public:
   NS_IMETHOD GetAllowPipelining(bool *value) MOZ_OVERRIDE;
   NS_IMETHOD SetAllowPipelining(bool value) MOZ_OVERRIDE;
   NS_IMETHOD GetAllowSTS(bool *value) MOZ_OVERRIDE;
   NS_IMETHOD SetAllowSTS(bool value) MOZ_OVERRIDE;
   NS_IMETHOD GetRedirectionLimit(uint32_t *value) MOZ_OVERRIDE;
   NS_IMETHOD SetRedirectionLimit(uint32_t value) MOZ_OVERRIDE;
   NS_IMETHOD IsNoStoreResponse(bool *value) MOZ_OVERRIDE;
   NS_IMETHOD IsNoCacheResponse(bool *value) MOZ_OVERRIDE;
+  NS_IMETHOD IsPrivateResponse(bool *value) MOZ_OVERRIDE;
   NS_IMETHOD GetResponseStatus(uint32_t *aValue) MOZ_OVERRIDE;
   NS_IMETHOD GetResponseStatusText(nsACString& aValue) MOZ_OVERRIDE;
   NS_IMETHOD GetRequestSucceeded(bool *aValue) MOZ_OVERRIDE;
   NS_IMETHOD RedirectTo(nsIURI *newURI) MOZ_OVERRIDE;
 
   // nsIHttpChannelInternal
   NS_IMETHOD GetDocumentURI(nsIURI **aDocumentURI) MOZ_OVERRIDE;
   NS_IMETHOD SetDocumentURI(nsIURI *aDocumentURI) MOZ_OVERRIDE;
--- a/netwerk/protocol/http/NullHttpChannel.cpp
+++ b/netwerk/protocol/http/NullHttpChannel.cpp
@@ -189,16 +189,22 @@ NullHttpChannel::IsNoStoreResponse(bool 
 
 NS_IMETHODIMP
 NullHttpChannel::IsNoCacheResponse(bool *_retval)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
+NullHttpChannel::IsPrivateResponse(bool *_retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 NullHttpChannel::RedirectTo(nsIURI *aNewURI)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 //-----------------------------------------------------------------------------
 // NullHttpChannel::nsIChannel
 //-----------------------------------------------------------------------------
--- a/netwerk/protocol/http/PHttpChannelParams.h
+++ b/netwerk/protocol/http/PHttpChannelParams.h
@@ -140,30 +140,32 @@ struct ParamTraits<mozilla::net::nsHttpR
   {
     WriteParam(aMsg, aParam.mHeaders);
     WriteParam(aMsg, aParam.mVersion);
     WriteParam(aMsg, aParam.mStatus);
     WriteParam(aMsg, aParam.mStatusText);
     WriteParam(aMsg, aParam.mContentLength);
     WriteParam(aMsg, aParam.mContentType);
     WriteParam(aMsg, aParam.mContentCharset);
+    WriteParam(aMsg, aParam.mCacheControlPrivate);
     WriteParam(aMsg, aParam.mCacheControlNoStore);
     WriteParam(aMsg, aParam.mCacheControlNoCache);
     WriteParam(aMsg, aParam.mPragmaNoCache);
   }
 
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
     if (!ReadParam(aMsg, aIter, &aResult->mHeaders)             ||
         !ReadParam(aMsg, aIter, &aResult->mVersion)             ||
         !ReadParam(aMsg, aIter, &aResult->mStatus)              ||
         !ReadParam(aMsg, aIter, &aResult->mStatusText)          ||
         !ReadParam(aMsg, aIter, &aResult->mContentLength)       ||
         !ReadParam(aMsg, aIter, &aResult->mContentType)         ||
         !ReadParam(aMsg, aIter, &aResult->mContentCharset)      ||
+        !ReadParam(aMsg, aIter, &aResult->mCacheControlPrivate) ||
         !ReadParam(aMsg, aIter, &aResult->mCacheControlNoStore) ||
         !ReadParam(aMsg, aIter, &aResult->mCacheControlNoCache) ||
         !ReadParam(aMsg, aIter, &aResult->mPragmaNoCache))
       return false;
 
     return true;
   }
 };
--- a/netwerk/protocol/http/nsHttpResponseHead.cpp
+++ b/netwerk/protocol/http/nsHttpResponseHead.cpp
@@ -619,16 +619,17 @@ nsHttpResponseHead::Reset()
 {
     LOG(("nsHttpResponseHead::Reset\n"));
 
     ClearHeaders();
 
     mVersion = NS_HTTP_VERSION_1_1;
     mStatus = 200;
     mContentLength = UINT64_MAX;
+    mCacheControlPrivate = false;
     mCacheControlNoStore = false;
     mCacheControlNoCache = false;
     mPragmaNoCache = false;
     mStatusText.Truncate();
     mContentType.Truncate();
     mContentCharset.Truncate();
 }
 
@@ -787,21 +788,26 @@ nsHttpResponseHead::ParseVersion(const c
         mVersion = NS_HTTP_VERSION_1_0;
 }
 
 void
 nsHttpResponseHead::ParseCacheControl(const char *val)
 {
     if (!(val && *val)) {
         // clear flags
+        mCacheControlPrivate = false;
         mCacheControlNoCache = false;
         mCacheControlNoStore = false;
         return;
     }
 
+    // search header value for occurrence of "private"
+    if (nsHttp::FindToken(val, "private", HTTP_HEADER_VALUE_SEPS))
+        mCacheControlPrivate = true;
+
     // search header value for occurrence(s) of "no-cache" but ignore
     // occurrence(s) of "no-cache=blah"
     if (nsHttp::FindToken(val, "no-cache", HTTP_HEADER_VALUE_SEPS))
         mCacheControlNoCache = true;
 
     // search header value for occurrence of "no-store"
     if (nsHttp::FindToken(val, "no-store", HTTP_HEADER_VALUE_SEPS))
         mCacheControlNoStore = true;
--- a/netwerk/protocol/http/nsHttpResponseHead.h
+++ b/netwerk/protocol/http/nsHttpResponseHead.h
@@ -18,30 +18,32 @@ namespace mozilla { namespace net {
 //-----------------------------------------------------------------------------
 
 class nsHttpResponseHead
 {
 public:
     nsHttpResponseHead() : mVersion(NS_HTTP_VERSION_1_1)
                          , mStatus(200)
                          , mContentLength(UINT64_MAX)
+                         , mCacheControlPrivate(false)
                          , mCacheControlNoStore(false)
                          , mCacheControlNoCache(false)
                          , mPragmaNoCache(false) {}
 
     const nsHttpHeaderArray & Headers()   const { return mHeaders; }
     nsHttpHeaderArray    &Headers()             { return mHeaders; }
     nsHttpVersion         Version()       const { return mVersion; }
 // X11's Xlib.h #defines 'Status' to 'int' on some systems!
 #undef Status
     uint16_t              Status()        const { return mStatus; }
     const nsAFlatCString &StatusText()    const { return mStatusText; }
     int64_t               ContentLength() const { return mContentLength; }
     const nsAFlatCString &ContentType()   const { return mContentType; }
     const nsAFlatCString &ContentCharset() const { return mContentCharset; }
+    bool                  Private() const { return mCacheControlPrivate; }
     bool                  NoStore() const { return mCacheControlNoStore; }
     bool                  NoCache() const { return (mCacheControlNoCache || mPragmaNoCache); }
     /**
      * Full length of the entity. For byte-range requests, this may be larger
      * than ContentLength(), which will only represent the requested part of the
      * entity.
      */
     int64_t               TotalEntitySize() const;
@@ -123,16 +125,17 @@ private:
     // All members must be copy-constructable and assignable
     nsHttpHeaderArray mHeaders;
     nsHttpVersion     mVersion;
     uint16_t          mStatus;
     nsCString         mStatusText;
     int64_t           mContentLength;
     nsCString         mContentType;
     nsCString         mContentCharset;
+    bool              mCacheControlPrivate;
     bool              mCacheControlNoStore;
     bool              mCacheControlNoCache;
     bool              mPragmaNoCache;
 
     friend struct IPC::ParamTraits<nsHttpResponseHead>;
 };
 }} // namespace mozilla::net
 
--- a/netwerk/protocol/http/nsIHttpChannel.idl
+++ b/netwerk/protocol/http/nsIHttpChannel.idl
@@ -9,17 +9,17 @@ interface nsIHttpHeaderVisitor;
 
 /**
  * nsIHttpChannel
  *
  * This interface allows for the modification of HTTP request parameters and
  * the inspection of the resulting HTTP response status and headers when they
  * become available.
  */
-[scriptable, uuid(82083578-fb78-4f9a-953c-cecbae500697)]
+[scriptable, uuid(a8bed710-653c-4ea4-9747-a629cc482cf8)]
 interface nsIHttpChannel : nsIChannel
 {
     /**************************************************************************
      * REQUEST CONFIGURATION
      *
      * Modifying request parameters after asyncOpen has been called is an error.
      */
 
@@ -295,16 +295,25 @@ interface nsIHttpChannel : nsIChannel
      * in the past relative to the value of the "Date" header.
      *
      * @throws NS_ERROR_NOT_AVAILABLE if called before the response
      *         has been received (before onStartRequest).
      */
     boolean isNoCacheResponse();
 
     /**
+     * Returns true if the server sent a "Cache-Control: private" response
+     * header.
+     *
+     * @throws NS_ERROR_NOT_AVAILABLE if called before the response
+     *         has been received (before onStartRequest).
+     */
+    boolean isPrivateResponse();
+
+    /**
      * Instructs the channel to immediately redirect to a new destination.
      * Can only be called on channels not yet opened.
      *
      * This method provides no explicit conflict resolution. The last
      * caller to call it wins.
      *
      * @throws NS_ERROR_ALREADY_OPENED if called after the channel
      *         has been opened.
--- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
+++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
@@ -764,14 +764,21 @@ nsViewSourceChannel::IsNoStoreResponse(b
 NS_IMETHODIMP
 nsViewSourceChannel::IsNoCacheResponse(bool *_retval)
 {
     return !mHttpChannel ? NS_ERROR_NULL_POINTER :
         mHttpChannel->IsNoCacheResponse(_retval);
 }
 
 NS_IMETHODIMP
+nsViewSourceChannel::IsPrivateResponse(bool *_retval)
+{
+    return !mHttpChannel ? NS_ERROR_NULL_POINTER :
+        mHttpChannel->IsPrivateResponse(_retval);
+}
+
+NS_IMETHODIMP
 nsViewSourceChannel::RedirectTo(nsIURI *uri)
 {
     return !mHttpChannel ? NS_ERROR_NULL_POINTER :
         mHttpChannel->RedirectTo(uri);
 }
 
--- a/netwerk/test/TestHttp.cpp
+++ b/netwerk/test/TestHttp.cpp
@@ -166,17 +166,17 @@ int main(int argc, char **argv)
         RETURN_IF_FAILED(rv, "NS_NewURI");
 
         rv = NS_NewChannel(getter_AddRefs(chan),
                            uri,
                            nsContentUtils::GetSystemPrincipal(),
                            nsILoadInfo::SEC_NORMAL,
                            nsIContentPolicy::TYPE_OTHER);
 
-        RETURN_IF_FAILED(rv, "NS_OpenURI");
+        RETURN_IF_FAILED(rv, "NS_NewChannel");
 
         rv = chan->AsyncOpen(listener, nullptr);
         RETURN_IF_FAILED(rv, "AsyncOpen");
 
         while (gKeepRunning)
             gEventQ->ProcessPendingEvents();
 
         printf(">>> done\n");
deleted file mode 100644
--- a/netwerk/test/TestPageLoad.cpp
+++ /dev/null
@@ -1,398 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "TestCommon.h"
-#include "nsNetUtil.h"
-#include "nsIServiceManager.h"
-#include "nsIInterfaceRequestor.h"
-#include "nsIInterfaceRequestorUtils.h"
-#include "nsIProgressEventSink.h"
-#include "nsIComponentManager.h"
-#include "prprf.h"
-#include "nsXPCOM.h"
-#include "nsISupportsPrimitives.h"
-#include "plstr.h"
-#include "nsCOMArray.h"
-#include "nsIComponentRegistrar.h"
-#include <algorithm>
-#include "nsIScriptSecurityManager.h"
-
-namespace TestPageLoad {
-
-int getStrLine(const char *src, char *str, int ind, int max);
-nsresult auxLoad(char *uriBuf);
-//----------------------------------------------------------------------
-
-
-#define RETURN_IF_FAILED(rv, ret, step) \
-    PR_BEGIN_MACRO \
-    if (NS_FAILED(rv)) { \
-        printf(">>> %s failed: rv=%x\n", step, static_cast<uint32_t>(rv)); \
-        return ret;\
-    } \
-    PR_END_MACRO
-
-static nsCString globalStream;
-//static char urlBuf[256];
-static nsCOMPtr<nsIURI> baseURI;
-static nsCOMArray<nsIURI> uriList;
-
-//Temp, should remove:
-static int numStart=0;
-static int numFound=0;
-
-static int32_t gKeepRunning = 0;
-
-
-//--------writer fun----------------------
-
-static NS_METHOD streamParse (nsIInputStream* in,
-                              void* closure,
-                              const char* fromRawSegment,
-                              uint32_t toOffset,
-                              uint32_t count,
-                              uint32_t *writeCount) {
-
-  char parseBuf[2048], loc[2048], lineBuf[2048];
-  char *loc_t, *loc_t2;
-  int i = 0;
-  const char *tmp;
-
-  if(!globalStream.IsEmpty()) {
-    globalStream.Append(fromRawSegment);
-    tmp = globalStream.get();
-    //printf("\n>>NOW:\n^^^^^\n%s\n^^^^^^^^^^^^^^", tmp);
-  } else {
-    tmp = fromRawSegment;
-  }
-
-  while(i < (int)count) {
-    i = getStrLine(tmp, lineBuf, i, count);
-    if(i < 0) {
-      *writeCount = count;
-      return NS_OK;
-    }
-    parseBuf[0]='\0';
-    if((loc_t=PL_strcasestr(lineBuf, "img"))!= nullptr 
-       || (loc_t=PL_strcasestr(lineBuf, "script"))!=nullptr) {
-      loc_t2=PL_strcasestr(loc_t, "src");
-      if(loc_t2!=nullptr) {
-        loc_t2+=3;
-        strcpy(loc, loc_t2);
-        sscanf(loc, "=\"%[^\"]", parseBuf);
-        if(parseBuf[0]=='\0')
-          sscanf(loc, "=%s", parseBuf);         
-        if(parseBuf[0]!='\0'){
-          numFound++;
-          auxLoad(parseBuf);
-        }     
-      }
-    }
-
-    /***NEED BETTER CHECK FOR STYLESHEETS
-    if((loc_t=PL_strcasestr(lineBuf, "link"))!= nullptr) { 
-       loc_t2=PL_strcasestr(loc_t, "href");
-      if(loc_t2!=nullptr) {
-        loc_t2+=4;
-        strcpy(loc, loc_t2);
-        //printf("%s\n", loc);
-        sscanf(loc, "=\"%[^\"]", parseBuf);
-        if(parseBuf[0]!='\0'){
-          //printf("%s\n", parseBuf);
-          numFound++;
-          auxLoad(parseBuf);
-        }     
-      }
-    }
-    */
-    if((loc_t=PL_strcasestr(lineBuf, "background"))!=nullptr) {
-      loc_t+=10;
-      strcpy(loc, loc_t);
-      sscanf(loc, "=\"%[^\"]", parseBuf);
-      if(parseBuf[0]!='\0') {
-        numFound++;
-        auxLoad(parseBuf);
-      }
-    }
-    i++;
-
-  }
-  *writeCount = count;
-  return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// nsIStreamListener implementation
-//-----------------------------------------------------------------------------
-
-class MyListener : public nsIStreamListener
-{
-    virtual ~MyListener() {}
-
-public:
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIREQUESTOBSERVER
-    NS_DECL_NSISTREAMLISTENER
-
-    MyListener() { }
-};
-
-NS_IMPL_ISUPPORTS(MyListener,
-                  nsIRequestObserver,
-                  nsIStreamListener)
-
-NS_IMETHODIMP
-MyListener::OnStartRequest(nsIRequest *req, nsISupports *ctxt)
-{
-  //printf(">>> OnStartRequest\n");
-    numStart++;
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-MyListener::OnStopRequest(nsIRequest *req, nsISupports *ctxt, nsresult status)
-{
-    //printf(">>> OnStopRequest status=%x\n", status);
-    if (--gKeepRunning == 0)
-      QuitPumpingEvents();
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-MyListener::OnDataAvailable(nsIRequest *req, nsISupports *ctxt,
-                            nsIInputStream *stream,
-                            uint64_t offset, uint32_t count)
-{
-    //printf(">>> OnDataAvailable [count=%u]\n", count);
-    nsresult rv = NS_ERROR_FAILURE;
-    uint32_t bytesRead=0;
-    char buf[1024];
-
-    if(ctxt == nullptr) {
-      bytesRead=0;
-      rv = stream->ReadSegments(streamParse, nullptr, count, &bytesRead);
-    } else {
-      while (count) {
-        uint32_t amount = std::min<uint32_t>(count, sizeof(buf));
-        rv = stream->Read(buf, amount, &bytesRead);  
-        count -= bytesRead;
-      }
-    }
-
-    if (NS_FAILED(rv)) {
-      printf(">>> stream->Read failed with rv=%x\n",
-             static_cast<uint32_t>(rv));
-      return rv;
-    }
-
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// NotificationCallbacks implementation
-//-----------------------------------------------------------------------------
-
-class MyNotifications : public nsIInterfaceRequestor
-                      , public nsIProgressEventSink
-{
-    virtual ~MyNotifications() {}
-
-public:
-    NS_DECL_THREADSAFE_ISUPPORTS
-    NS_DECL_NSIINTERFACEREQUESTOR
-    NS_DECL_NSIPROGRESSEVENTSINK
-
-    MyNotifications() { }
-};
-
-NS_IMPL_ISUPPORTS(MyNotifications,
-                  nsIInterfaceRequestor,
-                  nsIProgressEventSink)
-
-NS_IMETHODIMP
-MyNotifications::GetInterface(const nsIID &iid, void **result)
-{
-    return QueryInterface(iid, result);
-}
-
-NS_IMETHODIMP
-MyNotifications::OnStatus(nsIRequest *req, nsISupports *ctx,
-                          nsresult status, const char16_t *statusText)
-{
-    //printf("status: %x\n", status);
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-MyNotifications::OnProgress(nsIRequest *req, nsISupports *ctx,
-                            uint64_t progress, uint64_t progressMax)
-{
-    // char buf[100];
-    // PR_snprintf(buf, sizeof(buf), "%llu/%llu\n", progress, progressMax);
-    // printf("%s", buf);
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-// main, etc..
-//-----------------------------------------------------------------------------
-
-//---------getStrLine Helper function---------------
-//Finds a newline in src starting at ind. Puts the
-//line in str (must be big enough). Returns the index
-//of the newline, or -1 if at end of string. If reaches 
-//end of string ('\0'), then will copy contents to 
-//globalStream. 
-int getStrLine(const char *src, char *str, int ind, int max) {
-  char c = src[ind];
-  int i=0;
-  globalStream.Assign('\0');
-  while(c!='\n' && c!='\0' && i<max) {
-    str[i] = src[ind];
-    i++; ind++;
-    c = src[ind];
-  }
-  str[i]='\0';
-  if(i==max || c=='\0') {
-    globalStream.Assign(str);
-    //printf("\nCarryover (%d|%d):\n------------\n%s\n-------\n",i,max,str);
-    return -1;
-  }
-  return ind;
-}
-
-//----------AUX LOAD-----------
-nsresult auxLoad(char *uriBuf)
-{
-    nsresult rv;
-
-    nsCOMPtr<nsISupportsPRBool> myBool = do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
-
-    nsCOMPtr<nsIURI> uri;
-    nsCOMPtr<nsIChannel> chan;
-    nsCOMPtr<nsIStreamListener> listener = new MyListener();
-    nsCOMPtr<nsIInterfaceRequestor> callbacks = new MyNotifications();
-
-    printf("Getting: %s", uriBuf);
-
-    //If relative link
-    if(strncmp(uriBuf, "http:", 5)) {
-      //Relative link
-      rv = NS_NewURI(getter_AddRefs(uri), uriBuf, baseURI);
-      if (NS_FAILED(rv)) return(rv);
-    } else {
-      //Absolute link, no base needed
-      rv = NS_NewURI(getter_AddRefs(uri), uriBuf);
-      if (NS_FAILED(rv)) return(rv);
-    }
-
-    //Compare to see if exists
-    bool equal;
-    for(int32_t i = 0; i < uriList.Count(); i++) {
-      uri->Equals(uriList[i], &equal);
-      if(equal) {
-        printf("(duplicate, canceling) %s\n",uriBuf); 
-        return NS_OK;
-      }
-    }
-    printf("\n");
-    uriList.AppendObject(uri);
-
-    nsCOMPtr<nsIScriptSecurityManager> secman =
-      do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
-    RETURN_IF_FAILED(rv, rv, "Couldn't get script security manager!");
-       nsCOMPtr<nsIPrincipal> systemPrincipal;
-    rv = secman->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
-    RETURN_IF_FAILED(rv, rv, "Couldn't get system principal!");
-
-    rv = NS_NewChannel(getter_AddRefs(chan),
-                       uri,
-                       systemPrincipal,
-                       nsILoadInfo::SEC_NORMAL,
-                       nsIContentPolicy::TYPE_OTHER,
-                       nullptr,   // loadGroup
-                       callbacks);
-
-    RETURN_IF_FAILED(rv, rv, "NS_NewChannel");
-
-    gKeepRunning++;
-    rv = chan->AsyncOpen(listener, myBool);
-    RETURN_IF_FAILED(rv, rv, "AsyncOpen");
-
-    return NS_OK;
-
-}
-
-//---------Buffer writer fun---------
-
-} // namespace
-
-using namespace TestPageLoad;
-
-//---------MAIN-----------
-
-int main(int argc, char **argv)
-{ 
-    if (test_common_init(&argc, &argv) != 0)
-        return -1;
-
-    nsresult rv;
-
-    if (argc == 1) {
-        printf("usage: TestPageLoad <url>\n");
-        return -1;
-    }
-    {
-        nsCOMPtr<nsIServiceManager> servMan;
-        NS_InitXPCOM2(getter_AddRefs(servMan), nullptr, nullptr);
-
-        PRTime start, finish;
-
-        printf("Loading necko ... \n");
-        nsCOMPtr<nsIChannel> chan;
-        nsCOMPtr<nsIStreamListener> listener = new MyListener();
-        nsCOMPtr<nsIInterfaceRequestor> callbacks = new MyNotifications();
-
-        rv = NS_NewURI(getter_AddRefs(baseURI), argv[1]);
-        RETURN_IF_FAILED(rv, -1, "NS_NewURI");
-
-        nsCOMPtr<nsIScriptSecurityManager> secman =
-          do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
-        RETURN_IF_FAILED(rv, -1, "Couldn't get script security manager!");
-           nsCOMPtr<nsIPrincipal> systemPrincipal;
-        rv = secman->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
-        RETURN_IF_FAILED(rv, -1, "Couldn't get system principal!");
-
-        rv = NS_NewChannel(getter_AddRefs(chan),
-                           baseURI,
-                           systemPrincipal,
-                           nsILoadInfo::SEC_NORMAL,
-                           nsIContentPolicy::TYPE_OTHER,
-                           nullptr,   // loadGroup
-                           callbacks);
-
-        RETURN_IF_FAILED(rv, -1, "NS_OpenURI");
-        gKeepRunning++;
-
-        //TIMER STARTED-----------------------
-        printf("Starting clock ... \n");
-        start = PR_Now();
-        rv = chan->AsyncOpen(listener, nullptr);
-        RETURN_IF_FAILED(rv, -1, "AsyncOpen");
-
-        PumpEvents();
-
-        finish = PR_Now();
-        uint32_t totalTime32 = uint32_t(finish - start);
-
-        printf("\n\n--------------------\nAll done:\nnum found:%d\nnum start:%d\n", numFound, numStart);
-
-        printf("\n\n>>PageLoadTime>>%u>>\n\n", totalTime32);
-    } // this scopes the nsCOMPtrs
-    // no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
-    rv = NS_ShutdownXPCOM(nullptr);
-    NS_ASSERTION(NS_SUCCEEDED(rv), "NS_ShutdownXPCOM failed");
-    return 0;
-}
deleted file mode 100644
--- a/netwerk/test/TestPerf.cpp
+++ /dev/null
@@ -1,273 +0,0 @@
-#include "TestCommon.h"
-#include <stdio.h>
-#include "nsCRT.h" /* should be "plstr.h"? */
-#include "nsNetUtil.h"
-#include "nsIServiceManager.h"
-#include "nsIComponentRegistrar.h"
-#include "nsISupportsArray.h"
-#include "nsContentUtils.h"
-#include <algorithm>
-
-namespace TestPerf {
-
-static nsIIOService *gIOService = nullptr;
-
-//-----------------------------------------------------------------------------
-
-static bool
-load_sync_1(nsISupports *element, void *data)
-{
-    nsCOMPtr<nsIInputStream> stream;
-    nsCOMPtr<nsIURI> uri( do_QueryInterface(element) );
-    nsAutoCString spec;
-    nsresult rv;
-
-    rv = NS_OpenURI(getter_AddRefs(stream),
-                    uri,
-                    nsContentUtils::GetSystemPrincipal(),
-                    nsILoadInfo::SEC_NORMAL,
-                    nsIContentPolicy::TYPE_OTHER,
-                    nullptr, // aLoadGroup
-                    nullptr, // aCallbacks
-                    LOAD_NORMAL,
-                    gIOService);
-
-    if (NS_FAILED(rv)) {
-        uri->GetAsciiSpec(spec);
-        fprintf(stderr, "*** failed opening %s [rv=%x]\n", spec.get(), rv);
-        return true;
-    }
-
-    char buf[4096];
-    uint32_t bytesRead;
-
-    while (1) {
-        rv = stream->Read(buf, sizeof(buf), &bytesRead);
-        if (NS_FAILED(rv) || bytesRead == 0) {
-            if (NS_FAILED(rv)) {
-                uri->GetAsciiSpec(spec);
-                fprintf(stderr, "*** failed reading %s [rv=%x]\n", spec.get(), rv);
-            }
-            break;
-        }
-    }
-
-    return true;
-}
-
-static nsresult
-load_sync(nsISupportsArray *urls)
-{
-    urls->EnumerateForwards(load_sync_1, nullptr);
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-
-static int gRequestCount = 0;
-
-class MyListener : public nsIStreamListener
-{
-public:
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIREQUESTOBSERVER
-    NS_DECL_NSISTREAMLISTENER
-
-    MyListener() { }
-    virtual ~MyListener() {}
-};
-
-NS_IMPL_ISUPPORTS(MyListener, nsIStreamListener, nsIRequestObserver)
-
-NS_IMETHODIMP
-MyListener::OnStartRequest(nsIRequest *req, nsISupports *ctx)
-{
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-MyListener::OnDataAvailable(nsIRequest *req, nsISupports *ctx,
-                            nsIInputStream *stream,
-                            uint64_t offset, uint32_t count)
-{
-    nsresult rv;
-    char buf[4096];
-    uint32_t n, bytesRead;
-    while (count) {
-        n = std::min<uint32_t>(count, sizeof(buf));
-        rv = stream->Read(buf, n, &bytesRead);
-        if (NS_FAILED(rv))
-            break;
-        count -= bytesRead;
-        if (bytesRead == 0)
-            break;
-    }
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-MyListener::OnStopRequest(nsIRequest *req, nsISupports *ctx, nsresult status)
-{
-    if (NS_FAILED(status)) {
-        nsAutoCString spec;
-        req->GetName(spec);
-        fprintf(stderr, "*** failed loading %s [reason=%x]\n", spec.get(), status);
-    }
-    if (--gRequestCount == 0) {
-        // post shutdown event
-        QuitPumpingEvents();
-    }
-    return NS_OK;
-}
-
-static bool
-load_async_1(nsISupports *element, void *data)
-{
-    nsCOMPtr<nsIURI> uri( do_QueryInterface(element) );
-    if (!uri)
-        return true;
-
-    MyListener *listener = new MyListener();
-    if (!listener)
-        return true;
-    NS_ADDREF(listener);
-
-    nsresult rv = NS_OpenURI(listener,
-                             nullptr,   // aContext
-                             uri,
-                             nsContentUtils::GetSystemPrincipal(),
-                             nsILoadInfo::SEC_NORMAL,
-                             nsIContentPolicy::TYPE_OTHER,
-                             nullptr,   // aLoadGroup
-                             nullptr,   // aCallbacks
-                             gIOService);
-
-    NS_RELEASE(listener);
-    if (NS_SUCCEEDED(rv))
-        gRequestCount++;
-    else 
-        printf(">> NS_OpenURI failed [rv=%x]\n", rv);
-    return true;
-}
-
-static nsresult
-load_async(nsISupportsArray *urls)
-{
-    urls->EnumerateForwards(load_async_1, nullptr);
-
-    PumpEvents();
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-
-static nsresult
-read_file(const char *fname, nsISupportsArray *urls)
-{
-    FILE *fp = fopen(fname, "r");
-    if (!fp) {
-        printf("failed opening file: %s\n", fname);
-        return NS_ERROR_FAILURE;
-    }
-
-    nsCOMPtr<nsIURI> uri;
-    nsresult rv;
-    char buf[512];
-    while (fgets(buf, sizeof(buf), fp)) {
-        // remove trailing newline
-        buf[strlen(buf) - 1] = 0;
-        rv = NS_NewURI(getter_AddRefs(uri), buf, nullptr, gIOService); 
-        if (NS_FAILED(rv))
-            printf("*** ignoring malformed uri: %s\n", buf);
-        else {
-            //nsXPIDLCString spec;
-            //uri->GetSpec(getter_Copies(spec));
-            //printf("read url: %s\n", spec.get());
-            urls->AppendElement(uri);
-        }
-    }
-
-    fclose(fp);
-    return NS_OK;
-}
-
-//-----------------------------------------------------------------------------
-
-static void
-print_usage()
-{
-    printf("usage: TestPerf [-sync|-async] <file-of-urls>\n");
-}
-
-} // namespace
-
-using namespace TestPerf;
-
-int
-main(int argc, char **argv)
-{
-    if (test_common_init(&argc, &argv) != 0)
-        return -1;
-
-    nsresult rv;
-    bool sync;
-
-    if (argc < 3) {
-        print_usage();
-        return -1;
-    }
-
-    if (PL_strcasecmp(argv[1], "-sync") == 0)
-        sync = true;
-    else if (PL_strcasecmp(argv[1], "-async") == 0)
-        sync = false;
-    else {
-        print_usage();
-        return -1;
-    }
-
-    nsCOMPtr<nsIServiceManager> servMan;
-    NS_InitXPCOM2(getter_AddRefs(servMan), nullptr, nullptr);
-    nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(servMan);
-    NS_ASSERTION(registrar, "Null nsIComponentRegistrar");
-    registrar->AutoRegister(nullptr);
-
-    // cache the io service
-    {
-        nsCOMPtr<nsIIOService> ioserv( do_GetIOService() );
-        NS_ADDREF(gIOService = ioserv);
-    }
-
-    nsCOMPtr<nsISupportsArray> urls;
-    rv = NS_NewISupportsArray(getter_AddRefs(urls));
-    if (NS_FAILED(rv)) return -1;
-
-    rv = read_file(argv[2], urls);
-    if (NS_FAILED(rv)) {
-        printf("failed reading file-of-urls\n");
-        return -1;
-    }
-
-    uint32_t urlCount;
-    urls->Count(&urlCount);
-
-    PRIntervalTime start = PR_IntervalNow();
-
-    if (sync)
-        rv = load_sync(urls);
-    else
-        rv = load_async(urls);
-
-    if (NS_FAILED(rv)) {
-        printf("load failed\n");
-        return -1;
-    }
-
-    PRIntervalTime end = PR_IntervalNow();
-    fprintf(stderr, "read: %u urls; total time: %u milliseconds\n",
-            urlCount,
-            PR_IntervalToMilliseconds(end - start));
-
-    NS_RELEASE(gIOService);
-    return 0;
-}
--- a/netwerk/test/TestProtocols.cpp
+++ b/netwerk/test/TestProtocols.cpp
@@ -644,17 +644,17 @@ nsresult StartLoadingURL(const char* aUr
                            nsIContentPolicy::TYPE_OTHER,
                            nullptr,  // loadGroup
                            callbacks,
                            nsIRequest::LOAD_NORMAL,
                            pService);
 
         NS_RELEASE(callbacks);
         if (NS_FAILED(rv)) {
-            LOG(("ERROR: NS_OpenURI failed for %s [rv=%x]\n", aUrlString, rv));
+            LOG(("ERROR: NS_NewChannel failed for %s [rv=%x]\n", aUrlString, rv));
             return rv;
         }
 
         nsCOMPtr<nsITimedChannel> timed(do_QueryInterface(pChannel));
         if (timed)
             timed->SetTimingEnabled(true);
 
         nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(pChannel);
deleted file mode 100644
--- a/netwerk/test/TestSyncHTTP.cpp
+++ /dev/null
@@ -1,111 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include <nsCOMPtr.h>
-#include <nsString.h>
-#include <nsIURI.h>
-#include <nsIChannel.h>
-#include <nsIHTTPChannel.h>
-#include <nsIInputStream.h>
-#include "nsContentUtils.h"
-#include <nsNetUtil.h>
-
-/*
- * Test synchronous HTTP.
- */
-
-#define RETURN_IF_FAILED(rv, what) \
-    PR_BEGIN_MACRO \
-    if (NS_FAILED(rv)) { \
-        printf(what ": failed - %08x\n", rv); \
-        return -1; \
-    } \
-    PR_END_MACRO
-
-struct TestContext {
-    nsCOMPtr<nsIURI> uri;
-    nsCOMPtr<nsIChannel> channel;
-    nsCOMPtr<nsIInputStream> inputStream;
-    PRTime t1, t2;
-    uint32_t bytesRead, totalRead;
-
-    TestContext()
-        : t1(0), t2(0), bytesRead(0), totalRead(0)
-        { printf("TestContext [this=%p]\n", (void*)this); }
-   ~TestContext()
-        { printf("~TestContext [this=%p]\n", (void*)this); }
-};
-
-int
-main(int argc, char **argv)
-{
-    nsresult rv;
-    TestContext *c;
-    int i, nc=0, npending=0;
-    char buf[256];
-
-    if (argc < 2) {
-        printf("Usage: TestSyncHTTP <url-list>\n");
-        return -1;
-    }
-
-    c = new TestContext[argc-1];
-
-    for (i=0; i<(argc-1); ++i, ++nc) {
-        rv = NS_NewURI(getter_AddRefs(c[i].uri), argv[i+1]);
-        RETURN_IF_FAILED(rv, "NS_NewURI");
-
-        rv = NS_OpenURI(getter_AddRefs(c[i].channel,
-                        c[i].uri,
-                        nsContentUtils::GetSystemPrincipal(),
-                        nsILoadInfo::SEC_NORMAL,
-                        nsIContentPolicy::TYPE_OTHER);
-
-        RETURN_IF_FAILED(rv, "NS_OpenURI");
-
-        nsCOMPtr<nsIHTTPChannel> httpChannel = do_QueryInterface(c[i].channel);
-        if (httpChannel)
-            httpChannel->SetOpenHasEventQueue(false);
-
-        // initialize these fields for reading
-        c[i].bytesRead = 1;
-        c[i].totalRead = 0;
-    }
-
-    for (i=0; i<nc; ++i) {
-        c[i].t1 = PR_Now();
-
-        rv = c[i].channel->Open(getter_AddRefs(c[i].inputStream));
-        RETURN_IF_FAILED(rv, "nsIChannel::OpenInputStream");
-    }
-
-    npending = nc;
-    while (npending) {
-        for (i=0; i<nc; ++i) {
-            //
-            // read the response content...
-            //
-            if (c[i].bytesRead > 0) {
-                rv = c[i].inputStream->Read(buf, sizeof buf, &c[i].bytesRead);
-                RETURN_IF_FAILED(rv, "nsIInputStream::Read");
-                c[i].totalRead += c[i].bytesRead;
-
-                if (c[i].bytesRead == 0) {
-                    c[i].t2 = PR_Now();
-                    printf("finished GET of: %s\n", argv[i+1]);
-                    printf("total read: %u bytes\n", c[i].totalRead);
-                    printf("total read time: %0.3f\n",
-                            ((double) (c[i].t2 - c[i].t1))/1000000.0);
-                    npending--;
-                }
-            }
-        }
-    }
-
-    delete[] c;
-
-    NS_ShutdownXPCOM(nullptr);
-    return 0;
-}
deleted file mode 100644
--- a/netwerk/test/TestThreadedIO.cpp
+++ /dev/null
@@ -1,290 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#include <stdio.h>
-#include "nsCOMPtr.h"
-#include "nsIEventQueueService.h"
-#include "nsIServiceManager.h"
-#include "nsIStreamListener.h"
-#include "nsIURI.h"
-#include "nsNetUtil.h"
-#include "nsContentUtils.h"
-#include <algorithm>
-//#include "prthread.h"
-
-// This test attempts to load a URL on a separate thread.  It is currently
-// designed simply to expose the problems inherent in such an ambitous task
-// (i.e., it don't work).
-
-// Utility functions...
-
-// Create event queue for current thread.
-static nsCOMPtr<nsIEventQueue>
-createEventQueue() {
-    nsCOMPtr<nsIEventQueue> result;
-    // Get event queue service.
-    nsresult rv = NS_OK;
-    nsCOMPtr<nsIEventQueueService> eqs = 
-             do_GetService(NS_EVENTQUEUESERVICE_CONTRACTID, &rv);
-    if ( NS_SUCCEEDED( rv ) ) {
-            eqs->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(result));
-    } else {
-        printf( "%s %d: NS_WITH_SERVICE(nsIEventQueueService) failed, rv=0x%08X\n",
-                (char*)__FILE__, (int)__LINE__, (int)rv );
-    }
-    return result;
-}
-
-// Create channel for requested URL.
-static nsCOMPtr<nsIChannel>
-createChannel( const char *url ) {
-    nsCOMPtr<nsIInputStream> result;
-
-    nsCOMPtr<nsIURI> uri;
-    printf( "Calling NS_NewURI for %s...\n", url );
-    nsresult rv = NS_NewURI( getter_AddRefs( uri ), url );
-
-    if ( NS_SUCCEEDED( rv ) ) {
-        printf( "...NS_NewURI completed OK\n" );
-
-        // Allocate a new input channel on this thread.
-        printf( "Calling NS_OpenURI...\n" );
-
-        nsresult rv = NS_OpenURI(getter_AddRefs(result),
-                                 uri,
-                                 nsContentUtils::GetSystemPrincipal(),
-                                 nsILoadInfo::SEC_NORMAL,
-                                 nsIContentPolicy::TYPE_OTHER);
-
-        if ( NS_SUCCEEDED( rv ) ) {
-            printf( "...NS_OpenURI completed OK\n" );
-        } else {
-            printf( "%s %d: NS_OpenURI failed, rv=0x%08X\n",
-                    (char*)__FILE__, (int)__LINE__, (int)rv );
-        }
-    } else {
-        printf( "%s %d: NS_NewURI failed, rv=0x%08X\n",
-                (char*)__FILE__, (int)__LINE__, (int)rv );
-    }
-    return result;
-}
-
-// Test listener.  It basically dumps incoming data to console.
-class TestListener : public nsIStreamListener {
-public:
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSISTREAMLISTENER
-    NS_DECL_NSISTREAMOBSERVER
-
-    TestListener();
-    ~TestListener();
-    static void IOThread( void *p );
-
-private:
-    bool mDone;
-    int    mThreadNo;
-    FILE  *mFile;
-    static int threadCount;
-}; // class TestListener
-
-int TestListener::threadCount = 0;
-
-TestListener::TestListener()
-    : mDone( false ), mThreadNo( ++threadCount ) {
-    printf( "TestListener ctor called on thread %d\n", mThreadNo );
-}
-
-TestListener::~TestListener() {
-    printf( "TestListener dtor called on thread %d\n", mThreadNo );
-}
-
-NS_IMPL_ISUPPORTS( TestListener, nsIStreamListener, nsIRequestObserver )
-
-NS_IMETHODIMP
-TestListener::OnStartRequest( nsIChannel *aChannel, nsISupports *aContext ) {
-    nsresult rv = NS_OK;
-
-    printf( "TestListener::OnStartRequest called on thread %d\n", mThreadNo );
-
-    // Open output file.
-    char fileName[32];
-    sprintf( fileName, "%s%d", "thread", mThreadNo );
-    mFile = fopen( fileName, "wb" );
-    setbuf( mFile, 0 );
-
-    return rv;
-}
-
-NS_IMETHODIMP
-TestListener::OnStopRequest( nsIChannel *aChannel,
-                             nsISupports *aContext,
-                             nsresult aStatus,
-                             const char16_t *aMsg ) {
-    nsresult rv = NS_OK;
-
-    printf( "TestListener::OnStopRequest called on thread %d\n", mThreadNo );
-
-    fclose( mFile );
-    mDone = true;
-
-    return rv;
-}
-
-NS_IMETHODIMP
-TestListener::OnDataAvailable( nsIChannel *aChannel,
-                               nsISupports *aContext,
-                               nsIInputStream *aStream,
-                               uint64_t offset,
-                               uint32_t aLength ) {
-    nsresult rv = NS_OK;
-
-    printf( "TestListener::OnDataAvailable called on thread %d\n", mThreadNo );
-
-    // Write the data to the console.
-    // Read a buffer full till aLength bytes have been processed.
-    char buffer[ 8192 ];
-    unsigned long bytesRemaining = aLength;
-    while ( bytesRemaining ) {
-        unsigned int bytesRead;
-        // Read a buffer full or the number remaining (whichever is smaller).
-        rv = aStream->Read( buffer,
-                            std::min( sizeof( buffer ), bytesRemaining ),
-                            &bytesRead );
-        if ( NS_SUCCEEDED( rv ) ) {
-            // Write the bytes just read to the output file.
-            fwrite( buffer, 1, bytesRead, mFile );
-            bytesRemaining -= bytesRead;
-        } else {
-            printf( "%s %d: Read error, rv=0x%08X\n",
-                    (char*)__FILE__, (int)__LINE__, (int)rv );
-            break;
-        }
-    }
-    printf( "\n" );
-
-    return rv;
-}
-
-// IOThread: this function creates a new TestListener object (on the new
-// thread), opens a channel, and does AsyncRead to it.
-void
-TestListener::IOThread( void *p ) {
-    printf( "I/O thread (0x%08X) started...\n", (int)(void*)PR_GetCurrentThread() );
-
-    // Argument is pointer to the nsIEventQueue for the main thread.
-    nsIEventQueue *mainThreadQ = static_cast<nsIEventQueue*>(p);
-
-    // Create channel for random web page.
-    nsCOMPtr<nsIChannel> channel = createChannel( (const char*)p );
-
-    if ( channel ) {
-        // Create event queue.
-        nsCOMPtr<nsIEventQueue> ioEventQ = createEventQueue();
-
-        if ( ioEventQ ) {
-            // Create test listener.
-            TestListener *testListener = new TestListener();
-            testListener->AddRef();
-
-            // Read the channel.
-            printf( "Doing AsyncRead...\n" );
-            nsresult rv = channel->AsyncRead( testListener, 0 );
-
-            if ( NS_SUCCEEDED( rv ) ) {
-                printf( "...AsyncRead completed OK\n" );
-
-                // Process events till testListener says stop.
-                printf( "Start event loop on io thread %d...\n", testListener->mThreadNo );
-                while ( !testListener->mDone ) {
-                    PLEvent *event;
-                    ioEventQ->GetEvent( &event );
-                    ioEventQ->HandleEvent( event );
-                }
-                printf( "...io thread %d event loop exiting\n", testListener->mThreadNo );
-            } else {
-                printf( "%s %d: AsyncRead failed on thread %d, rv=0x%08X\n",
-                        (char*)__FILE__, (int)__LINE__, testListener->mThreadNo, (int)rv );
-            }
-
-            // Release the test listener.
-            testListener->Release();
-        }
-    }
-
-    printf( "...I/O thread terminating\n" );
-}
-
-static const int maxThreads = 5;
-
-int
-main( int argc, char* argv[] ) {
-    setbuf( stdout, 0 );
-    if ( argc < 2 || argc > maxThreads + 1 ) {
-        printf( "usage: testThreadedIO url1 <url2>...\n"
-                "where <url#> is a location to be loaded on a separate thread\n"
-                "limit is %d urls/threads", maxThreads );
-        return -1;
-    }
-
-    nsresult rv= (nsresult)-1;
-
-    printf( "Test starting...\n" );
-
-    // Initialize XPCOM.
-    printf( "Initializing XPCOM...\n" );
-    rv = NS_InitXPCOM2(nullptr, nullptr, nullptr);
-    if ( NS_FAILED( rv ) ) {
-        printf( "%s %d: NS_InitXPCOM failed, rv=0x%08X\n",
-                (char*)__FILE__, (int)__LINE__, (int)rv );
-        return rv;
-    }
-    printf( "...XPCOM initialized OK\n" );
-    // Create the Event Queue for this thread...
-    printf( "Creating event queue for main thread (0x%08X)...\n",
-            (int)(void*)PR_GetCurrentThread() );
-    {
-        nsCOMPtr<nsIEventQueue> mainThreadQ = createEventQueue();
-
-        if ( mainThreadQ ) {
-            printf( "...main thread's event queue created OK\n" );
-
-            // Spawn threads to do I/O.
-            int goodThreads = 0;
-            PRThread *thread[ maxThreads ];
-            for ( int threadNo = 1; threadNo < argc; threadNo++ ) {
-                printf( "Creating I/O thread %d to load %s...\n", threadNo, argv[threadNo] );
-                PRThread *ioThread = PR_CreateThread( PR_USER_THREAD,
-                                                      TestListener::IOThread,
-                                                      argv[threadNo],
-                                                      PR_PRIORITY_NORMAL,
-                                                      PR_GLOBAL_THREAD,
-                                                      PR_JOINABLE_THREAD,
-                                                      0 );
-                if ( ioThread ) {
-                    thread[ goodThreads++ ] = ioThread;
-                    printf( "...I/O thread %d (0x%08X) created OK\n",
-                            threadNo, (int)(void*)ioThread );
-                } else {
-                    printf( "%s %d: PR_CreateThread for thread %d failed\n",
-                            (char*)__FILE__, (int)__LINE__, threadNo );
-                }
-            }
-
-            // Wait for all the threads to terminate.
-            for ( int joinThread = 0; joinThread < goodThreads; joinThread++ ) {
-                printf( "Waiting for thread %d to terminate...\n", joinThread+1 );
-                PR_JoinThread( thread[ joinThread ] );
-            }
-        }
-    } // this scopes the nsCOMPtrs
-    // no nsCOMPtrs are allowed to be alive when you call NS_ShutdownXPCOM
-    // Shut down XPCOM.
-    printf( "Shutting down XPCOM...\n" );
-    NS_ShutdownXPCOM( 0 );
-    printf( "...XPCOM shutdown complete\n" );
-
-    // Exit.
-    printf( "...test complete, rv=0x%08X\n", (int)rv );
-    return rv;
-}
--- a/netwerk/test/httpserver/test/test_basic_functionality.js
+++ b/netwerk/test/httpserver/test/test_basic_functionality.js
@@ -54,16 +54,17 @@ const HEADER_COUNT = 1000;
 
 // common properties *always* appended by server
 // or invariants for every URL in paths
 function commonCheck(ch)
 {
   do_check_true(ch.contentLength > -1);
   do_check_eq(ch.getResponseHeader("connection"), "close");
   do_check_false(ch.isNoStoreResponse());
+  do_check_false(ch.isPrivateResponse());
 }
 
 function start_objHandler(ch, cx)
 {
   commonCheck(ch);
 
   do_check_eq(ch.responseStatus, 200);
   do_check_true(ch.requestSucceeded);
--- a/netwerk/test/moz.build
+++ b/netwerk/test/moz.build
@@ -19,31 +19,29 @@ XPCSHELL_TESTS_MANIFESTS += [
 GeckoSimplePrograms([
     'PropertiesTest',
     'ReadNTLM',
     'TestBlockingSocket',
     'TestCallbacks',
     'TestDNS',
     'TestIncrementalDownload',
     'TestOpen',
-    'TestPageLoad',
     'TestProtocols',
     'TestServ',
     'TestStandardURL',
     'TestStreamLoader',
     'TestUpload',
     'TestURLParser',
     'urltest',
 ])
 
 # XXX Make this work in libxul builds.
 #SIMPLE_PROGRAMS += [
 #    TestIDN',
 #    TestIOThreads',
-#    TestPerf',
 #    TestSocketTransport',
 #    TestStreamChannel',
 #    TestStreamPump',
 #    TestStreamTransport',
 #    TestUDPSocketProvider',
 #]
 
 CppUnitTests([
--- a/testing/marionette/client/marionette/selection.py
+++ b/testing/marionette/client/marionette/selection.py
@@ -100,17 +100,17 @@ class SelectionManager(object):
         considered.
 
         '''
         range_count = self.range_count();
         first_rect_list = self.selection_rect_list(0)
         last_rect_list = self.selection_rect_list(range_count - 1)
         last_list_length = last_rect_list['length']
         first_rect, last_rect = first_rect_list['0'], last_rect_list[str(last_list_length - 1)]
-        origin_x, origin_y = self.element.location['x'], self.element.location['y']
+        origin_x, origin_y = self.element.rect['x'], self.element.rect['y']
 
         if self.element.get_attribute('dir') == 'rtl':  # such as Arabic
             start_pos, end_pos = 'right', 'left'
         else:
             start_pos, end_pos = 'left', 'right'
 
         # Calculate y offset according to different needs.
         if location_type == 'center':
--- a/testing/marionette/client/marionette/tests/unit/unit-tests.ini
+++ b/testing/marionette/client/marionette/tests/unit/unit-tests.ini
@@ -12,16 +12,17 @@ b2g = true
 skip = false
 
 [test_marionette.py]
 [test_data_driven.py]
 [test_session.py]
 [test_capabilities.py]
 
 [test_accessibility.py]
+b2g = false
 
 [test_expectedfail.py]
 expected = fail
 [test_import_script.py]
 b2g = false
 [test_import_script_reuse_window.py]
 b2g = false
 [test_click.py]
--- a/toolkit/content/tests/chrome/findbar_window.xul
+++ b/toolkit/content/tests/chrome/findbar_window.xul
@@ -88,46 +88,50 @@
       // setTimeout to the test runs after painting suppression ends
       setTimeout(onPageShow, 0);
     }
 
     let onPageShow = Task.async(function* () {
       testNormalFind();
       gFindBar.close();
       ok(gFindBar.hidden, "Failed to close findbar after testNormalFind");
+      yield openFindbar();
       testNormalFindWithComposition();
       gFindBar.close();
       ok(gFindBar.hidden, "findbar should be hidden after testNormalFindWithComposition");
+      yield openFindbar();
       testAutoCaseSensitivityUI();
       testQuickFindText();
       gFindBar.close();
       ok(gFindBar.hidden, "Failed to close findbar after testQuickFindText");
       testFindWithHighlight();
       gFindBar.close();
       ok(gFindBar.hidden, "Failed to close findbar after testFindWithHighlight");
-      testFindbarSelection();
+      yield Task.spawn(testFindbarSelection);
+      ok(gFindBar.hidden, "Failed to close findbar after testFindbarSelection");
       testDrop();
       testQuickFindLink();
       if (gHasFindClipboard) {
         yield testStatusText();
       }
       yield testFindCountUI();
       gFindBar.close();
       ok(gFindBar.hidden, "Failed to close findbar after testFindCountUI");
+      yield openFindbar();
       yield testFindAfterCaseChanged();
       gFindBar.close();
+      yield openFindbar();
       yield testFailedStringReset();
       gFindBar.close();
       yield testQuickFindClose();
       finish();
     });
 
-    function testFindbarSelection() {
+    function* testFindbarSelection() {
       function checkFindbarState(aTestName, aExpSelection) {
-        document.getElementById("cmd_find").doCommand();
         ok(!gFindBar.hidden, "testFindbarSelection: failed to open findbar: " + aTestName);
         ok(document.commandDispatcher.focusedElement == gFindBar._findField.inputField,
            "testFindbarSelection: find field is not focused: " + aTestName);
         if (!gHasFindClipboard) {
           ok(gFindBar._findField.value == aExpSelection,
              "Incorrect selection in testFindbarSelection: "  + aTestName +
              ". Selection: " + gFindBar._findField.value);
         }
@@ -140,26 +144,29 @@
       // test normal selected text
       var cH2 = gBrowser.contentDocument.getElementById("h2");
       var cSelection = gBrowser.contentDocument.defaultView.getSelection();
       var cRange = gBrowser.contentDocument.createRange();
       cRange.setStart(cH2, 0);
       cRange.setEnd(cH2, 1);
       cSelection.removeAllRanges();
       cSelection.addRange(cRange);
+      yield openFindbar();
       checkFindbarState("plain text", SEARCH_TEXT);
 
       // test nsIDOMNSEditableElement with selection
       var textInput = gBrowser.contentDocument.getElementById("text");
       textInput.focus();
       textInput.select();
+      yield openFindbar();
       checkFindbarState("text input", SAMPLE_TEXT);
 
       // test non-editable nsIDOMNSEditableElement (button)
       gBrowser.contentDocument.getElementById("button").focus();
+      yield openFindbar();
       checkFindbarState("button", "");
     }
 
     function testDrop()
     {
       gFindBar.open();
       // use an dummy image to start the drag so it doesn't get interrupted by a selection
       var img = gBrowser.contentDocument.getElementById("img");
@@ -221,19 +228,22 @@
         matchCaseCheckbox.click();
         enterStringIntoFindField("t");
         ok(gBrowser.contentWindow.getSelection() != searchStr,
            "testNormalFind: Case-sensitivy is broken '" + searchStr + "'");
         matchCaseCheckbox.click();
       }
     }
 
-    function testNormalFindWithComposition() {
+    function openFindbar() {
       document.getElementById("cmd_find").doCommand();
+      return gFindBar._startFindDeferred.promise;
+    }
 
+    function testNormalFindWithComposition() {
       ok(!gFindBar.hidden, "testNormalFindWithComposition: findbar should be open");
       ok(document.commandDispatcher.focusedElement == gFindBar._findField.inputField,
          "testNormalFindWithComposition: find field should be focused");
 
       var matchCaseCheckbox = gFindBar.getElement("find-case-sensitive");
       var clicked = false;
       if (!matchCaseCheckbox.hidden & matchCaseCheckbox.checked) {
         matchCaseCheckbox.click();
@@ -256,29 +266,28 @@
           "caret": { "start": searchStr.length, "length": 0 }
         });
 
       ok(gBrowser.contentWindow.getSelection().toString().toLowerCase() != searchStr,
          "testNormalFindWithComposition: text shouldn't be found during composition");
 
       synthesizeComposition({ type: "compositioncommitasis" });
 
-      ok(gBrowser.contentWindow.getSelection().toString().toLowerCase() == searchStr,
+      is(gBrowser.contentWindow.getSelection().toString().toLowerCase(), searchStr,
          "testNormalFindWithComposition: text should be found after committing composition");
       testClipboardSearchString(gBrowser.contentWindow.getSelection().toString());
 
       if (clicked) {
         matchCaseCheckbox.click();
       }
     }
 
     function testAutoCaseSensitivityUI() {
       var matchCaseCheckbox = gFindBar.getElement("find-case-sensitive");
       var matchCaseLabel = gFindBar.getElement("match-case-status");
-      document.getElementById("cmd_find").doCommand();
       ok(!matchCaseCheckbox.hidden, "match case box is hidden in manual mode");
       ok(matchCaseLabel.hidden, "match case label is visible in manual mode");
 
       var prefsvc = Cc["@mozilla.org/preferences-service;1"].
                     getService(Components.interfaces.nsIPrefBranch);
       prefsvc.setIntPref("accessibility.typeaheadfind.casesensitive", 2);
 
       ok(matchCaseCheckbox.hidden,
@@ -326,32 +335,43 @@
       testClipboardSearchString(searchStr);
     }
 
     // See bug 963925 for more details on this test.
     function testFindWithHighlight() {
       //clearFocus();
       gFindBar._findField.value = "";
 
+      // For this test, we want to closely control the selection. The easiest
+      // way to do so is to replace the implementation of
+      // Finder.getInitialSelection with a no-op and call the findbar's callback
+      // (onCurrentSelection(..., true)) ourselves with our hand-picked
+      // selection.
+      let oldGetInitialSelection = gFindBar.browser.finder.getInitialSelection;
+      let searchStr;
+      gFindBar.browser.finder.getInitialSelection = function(){};
+
       let findCommand = document.getElementById("cmd_find");
       findCommand.doCommand();
 
-      let searchStr = "e";
+      gFindBar.onCurrentSelection("", true);
+
+      searchStr = "e";
       enterStringIntoFindField(searchStr);
 
       let a = gFindBar._findField.value;
       let b = gFindBar._browser.finder._fastFind.searchString;
       let c = gFindBar._browser.finder.searchString;
       ok(a == b && b == c, "testFindWithHighlight 1: " + a + ", " + b + ", " + c + ".");
 
-      let oldGetInitialSelection = gFindBar._getInitialSelection;
       searchStr = "t";
-      gFindBar._getInitialSelection = () => searchStr;
       findCommand.doCommand();
-      gFindBar._getInitialSelection = oldGetInitialSelection;
+
+      gFindBar.onCurrentSelection(searchStr, true);
+      gFindBar.browser.finder.getInitialSelection = oldGetInitialSelection;
 
       a = gFindBar._findField.value;
       b = gFindBar._browser.finder._fastFind.searchString;
       c = gFindBar._browser.finder.searchString;
       ok(a == searchStr && b == c, "testFindWithHighlight 2: " + searchStr +
          ", " + a + ", " + b + ", " + c + ".");
 
       let highlightButton = gFindBar.getElement("highlight");
@@ -467,17 +487,16 @@
       gFindBar.browser.finder.addResultListener(resultListener);
       test.next();
       return deferred.promise;
     }
 
     // See bug 1051187.
     function testFindAfterCaseChanged() {
       let deferred = Promise.defer();
-      document.getElementById("cmd_find").doCommand();
 
       // Search to set focus on "Text Test" so that searching for "t" selects first
       // (upper case!) "T".
       enterStringIntoFindField(SEARCH_TEXT);
       gFindBar.clear();
 
       let prefsvc = Cc["@mozilla.org/preferences-service;1"]
                       .getService(Ci.nsIPrefBranch);
@@ -494,17 +513,16 @@
       return deferred.promise;
     }
 
     // Make sure that _findFailedString is cleared:
     // 1. Do a search that fails with case sensitivity but matches with no case sensitivity.
     // 2. Uncheck case sensitivity button to match the string.
     function testFailedStringReset(aCallback) {
       let deferred = Promise.defer();
-      document.getElementById("cmd_find").doCommand();
 
       var prefsvc = Cc["@mozilla.org/preferences-service;1"].
                     getService(Components.interfaces.nsIPrefBranch);
       prefsvc.setIntPref("accessibility.typeaheadfind.casesensitive", 1);
 
       enterStringIntoFindField(SEARCH_TEXT.toUpperCase());
       is(gBrowser.contentWindow.getSelection(), "", "Not found.");
 
--- a/toolkit/content/widgets/findbar.xml
+++ b/toolkit/content/widgets/findbar.xml
@@ -210,20 +210,25 @@
       <field name="FIND_TYPEAHEAD">1</field>
       <field name="FIND_LINKS">2</field>
 
       <field name="_findMode">0</field>
 
       <field name="_flashFindBar">0</field>
       <field name="_initialFlashFindBarCount">6</field>
 
+      <!--
+        - For tests that need to know when the find bar is finished
+        - initializing, we store a promise to notify on.
+        -->
+      <field name="_startFindDeferred">null</field>
+
       <property name="prefillWithSelection"
                 onget="return this.getAttribute('prefillwithselection') != 'false'"
                 onset="this.setAttribute('prefillwithselection', val); return val;"/>
-      <field name="_selectionMaxLen">150</field>
 
       <method name="getElement">
         <parameter name="aAnonymousID"/>
         <body><![CDATA[
           return document.getAnonymousElementByAttribute(this,
                                                          "anonid",
                                                          aAnonymousID)
         ]]></body>
@@ -1021,53 +1026,16 @@
         <parameter name="aResult"/>
         <parameter name="aFindPrevious"/>
         <body><![CDATA[
           this._updateStatusUI(aResult, aFindPrevious);
           this._enableFindButtons(aResult !== this.nsITypeAheadFind.FIND_NOTFOUND);
         ]]></body>
       </method>
 
-      <method name="_getInitialSelection">
-        <body><![CDATA[
-          let focusedElement = document.commandDispatcher.focusedElement;
-          let selText;
-
-          if (focusedElement instanceof Components.interfaces.nsIDOMNSEditableElement &&
-              focusedElement.editor &&
-              focusedElement.ownerDocument.defaultView.top == this._browser.contentWindow)
-          {
-            // The user may have a selection in an input or textarea
-            selText = focusedElement.editor.selectionController
-              .getSelection(Components.interfaces.nsISelectionController.SELECTION_NORMAL)
-              .toString();
-          }
-          else {
-            // Look for any selected text on the actual page
-            let focusedWindow = document.commandDispatcher.focusedWindow;
-            if (focusedWindow.top == this._browser.contentWindow)
-              selText = focusedWindow.getSelection().toString();
-          }
-
-          if (!selText)
-            return "";
-
-          // Process our text to get rid of unwanted characters
-          if (selText.length > this._selectionMaxLen) {
-            let pattern = new RegExp("^(?:\\s*.){0," + this._selectionMaxLen + "}");
-            pattern.test(selText);
-            selText = RegExp.lastMatch;
-          }
-          return selText.replace(/^\s+/, "")
-                        .replace(/\s+$/, "")
-                        .replace(/\s+/g, " ")
-                        .substr(0, this._selectionMaxLen);
-        ]]></body>
-      </method>
-
       <method name="_dispatchFindEvent">
         <parameter name="aType"/>
         <parameter name="aFindPrevious"/>
         <body><![CDATA[
           let event = document.createEvent("CustomEvent");
           event.initCustomEvent("find" + aType, true, true, {
             query: this._findField.value,
             caseSensitive: !!this._typeAheadCaseSensitive,
@@ -1097,66 +1065,65 @@
           this.open(aMode);
 
           if (this._flashFindBar) {
             this._flashFindBarTimeout = setInterval(() => this._flash(), 500);
             prefsvc.setIntPref("accessibility.typeaheadfind.flashBar",
                                --this._flashFindBar);
           }
 
+          let {PromiseUtils} =
+            Components.utils.import("resource://gre/modules/PromiseUtils.jsm", {});
+          this._startFindDeferred = PromiseUtils.defer();
+          let startFindPromise = this._startFindDeferred.promise;
+
           if (this.prefillWithSelection)
             userWantsPrefill =
               prefsvc.getBoolPref("accessibility.typeaheadfind.prefillwithselection");
 
-          let initialString = null;
-          if (this.prefillWithSelection && userWantsPrefill)
-            initialString = this._getInitialSelection();
-#ifdef XP_MACOSX
-          if (!initialString) {
-            let clipboardSearchString = this.browser.finder.clipboardSearchString;
-            if (clipboardSearchString)
-              initialString = clipboardSearchString;
+          if (this.prefillWithSelection && userWantsPrefill) {
+            // NB: We have to focus this._findField here so tests that send
+            // key events can open and close the find bar synchronously.
+            this._findField.focus();
+            this.browser.finder.getInitialSelection();
+            return startFindPromise;
           }
-#endif
 
-          if (initialString)
-            this._findField.value = initialString;
-
-          this._enableFindButtons(!!this._findField.value);
-
-          this._findField.select();
-          this._findField.focus();
+          // If userWantsPrefill is false but prefillWithSelection is true,
+          // then we might need to check the selection clipboard. Call
+          // onCurrentSelection to do so.
+          // Note: this.onCurrentSelection clears this._startFindDeferred.
+          this.onCurrentSelection("", true);
+          return startFindPromise;
         ]]></body>
       </method>
 
       <!--
         - Convenient alias to startFind(gFindBar.FIND_NORMAL);
         -
         - You should generally map the window's find command to this method.
         -   e.g. <command name="cmd_find" oncommand="gFindBar.onFindCommand();"/>
         -->
       <method name="onFindCommand">
         <body><![CDATA[
-          this.startFind(this.FIND_NORMAL);
+          return this.startFind(this.FIND_NORMAL);
         ]]></body>
       </method>
 
       <!--
         - Stub for find-next and find-previous commands
         - @param aFindPrevious
         -        true for find-previous, false otherwise.
         -->
       <method name="onFindAgainCommand">
         <parameter name="aFindPrevious"/>
         <body><![CDATA[
           let findString = this._browser.finder.searchString || this._findField.value;
-          if (!findString) {
-            this.startFind();
-            return;
-          }
+          if (!findString)
+            return this.startFind();
 
           // We dispatch the findAgain event here instead of in _findAgain since
           // if there is a find event handler that prevents the default then
           // finder.searchString will never get updated which in turn means
           // there would never be findAgain events because of the logic below.
           if (!this._dispatchFindEvent("again", aFindPrevious))
             return;
 
@@ -1260,16 +1227,43 @@
             this._foundMatches.hidden = false;
           } else {
             this._foundMatches.hidden = true;
             this._foundMatches.value = "";
           }
         ]]></body>
       </method>
 
+      <method name="onCurrentSelection">
+        <parameter name="aSelectionString" />
+        <parameter name="aIsInitialSelection" />
+        <body><![CDATA[
+#ifdef XP_MACOSX
+          if (aIsInitialSelection && !aSelectionString) {
+            let clipboardSearchString = this.browser.finder.clipboardSearchString;
+            if (clipboardSearchString)
+              aSelectionString = clipboardSearchString;
+          }
+#endif
+
+          if (aSelectionString)
+            this._findField.value = aSelectionString;
+
+          if (aIsInitialSelection) {
+            this._enableFindButtons(!!this._findField.value);
+            this._findField.select();
+            this._findField.focus();
+            if (this._startFindDeferred) {
+              this._startFindDeferred.resolve();
+              this._startFindDeferred = null;
+            }
+          }
+        ]]></body>
+      </method>
+
       <!--
         - This handler may cancel a request to focus content by returning |false|
         - explicitly.
         -->
       <method name="shouldFocusContent">
         <body><![CDATA[
           const fm = Components.classes["@mozilla.org/focus-manager;1"]
                                .getService(Components.interfaces.nsIFocusManager);
--- a/toolkit/modules/Finder.jsm
+++ b/toolkit/modules/Finder.jsm
@@ -1,13 +1,14 @@
+// vim: set ts=2 sw=2 sts=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/.
 
-this.EXPORTED_SYMBOLS = ["Finder"];
+this.EXPORTED_SYMBOLS = ["Finder","GetClipboardSearchString"];
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Geometry.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
@@ -19,16 +20,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
 XPCOMUtils.defineLazyServiceGetter(this, "Clipboard",
                                          "@mozilla.org/widget/clipboard;1",
                                          "nsIClipboard");
 XPCOMUtils.defineLazyServiceGetter(this, "ClipboardHelper",
                                          "@mozilla.org/widget/clipboardhelper;1",
                                          "nsIClipboardHelper");
 
 const kHighlightIterationSizeMax = 100;
+const kSelectionMaxLen = 150;
 
 function Finder(docShell) {
   this._fastFind = Cc["@mozilla.org/typeaheadfind;1"].createInstance(Ci.nsITypeAheadFind);
   this._fastFind.init(docShell);
 
   this._docShell = docShell;
   this._listeners = [];
   this._previousLink = null;
@@ -85,41 +87,20 @@ Finder.prototype = {
 
   get searchString() {
     if (!this._searchString && this._fastFind.searchString)
       this._searchString = this._fastFind.searchString;
     return this._searchString;
   },
 
   get clipboardSearchString() {
-    let searchString = "";
-    if (!Clipboard.supportsFindClipboard())
-      return searchString;
-
-    try {
-      let trans = Cc["@mozilla.org/widget/transferable;1"]
-                    .createInstance(Ci.nsITransferable);
-      trans.init(this._getWindow()
-                     .QueryInterface(Ci.nsIInterfaceRequestor)
-                     .getInterface(Ci.nsIWebNavigation)
-                     .QueryInterface(Ci.nsILoadContext));
-      trans.addDataFlavor("text/unicode");
-
-      Clipboard.getData(trans, Ci.nsIClipboard.kFindClipboard);
-
-      let data = {};
-      let dataLen = {};
-      trans.getTransferData("text/unicode", data, dataLen);
-      if (data.value) {
-        data = data.value.QueryInterface(Ci.nsISupportsString);
-        searchString = data.toString();
-      }
-    } catch (ex) {}
-
-    return searchString;
+    return GetClipboardSearchString(this._getWindow()
+                                        .QueryInterface(Ci.nsIInterfaceRequestor)
+                                        .getInterface(Ci.nsIWebNavigation)
+                                        .QueryInterface(Ci.nsILoadContext));
   },
 
   set clipboardSearchString(aSearchString) {
     if (!aSearchString || !Clipboard.supportsFindClipboard())
       return;
 
     ClipboardHelper.copyStringToClipboard(aSearchString,
                                           Ci.nsIClipboard.kFindClipboard,
@@ -160,22 +141,18 @@ Finder.prototype = {
     this._notify(searchString, this._lastFindResult, aFindBackwards, aDrawOutline);
   },
 
   /**
    * Forcibly set the search string of the find clipboard to the currently
    * selected text in the window, on supported platforms (i.e. OSX).
    */
   setSearchStringToSelection: function() {
-    // Find the selected text.
-    let selection = this._getWindow().getSelection();
-    // Don't go for empty selections.
-    if (!selection.rangeCount)
-      return null;
-    let searchString = (selection.toString() || "").trim();
+    let searchString = this.getActiveSelectionText();
+
     // Empty strings are rather useless to search for.
     if (!searchString.length)
       return null;
 
     this.clipboardSearchString = searchString;
     return searchString;
   },
 
@@ -196,16 +173,54 @@ Finder.prototype = {
     this._notifyHighlightFinished(aHighlight);
     if (aHighlight) {
       let result = found ? Ci.nsITypeAheadFind.FIND_FOUND
                          : Ci.nsITypeAheadFind.FIND_NOTFOUND;
       this._notify(aWord, result, false, false, false);
     }
   }),
 
+  getInitialSelection: function() {
+    this._getWindow().setTimeout(() => {
+      let initialSelection = this.getActiveSelectionText();
+      for (let l of this._listeners) {
+        try {
+          l.onCurrentSelection(initialSelection, true);
+        } catch (ex) {}
+      }
+    }, 0);
+  },
+
+  getActiveSelectionText: function() {
+    let focusedWindow = {};
+    let focusedElement =
+      Services.focus.getFocusedElementForWindow(this._getWindow(), true,
+                                                focusedWindow);
+    focusedWindow = focusedWindow.value;
+
+    let selText;
+
+    if (focusedElement instanceof Ci.nsIDOMNSEditableElement &&
+        focusedElement.editor) {
+      // The user may have a selection in an input or textarea.
+      selText = focusedElement.editor.selectionController
+        .getSelection(Ci.nsISelectionController.SELECTION_NORMAL)
+        .toString();
+    } else {
+      // Look for any selected text on the actual page.
+      selText = focusedWindow.getSelection().toString();
+    }
+
+    if (!selText)
+      return "";
+
+    // Process our text to get rid of unwanted characters.
+    return selText.trim().replace(/\s+/g, " ").substr(0, kSelectionMaxLen);
+  },
+
   enableSelection: function() {
     this._fastFind.setSelectionModeAndRepaint(Ci.nsISelectionController.SELECTION_ON);
     this._restoreOriginalOutline();
   },
 
   removeSelection: function() {
     this._fastFind.collapseSelection();
     this.enableSelection();
@@ -642,18 +657,24 @@ Finder.prototype = {
         this._editors[x].addEditActionListener(this);
         this._editors[x].addDocumentStateListener(this._stateListeners[x]);
       }
     }
   },
 
   _getSelectionController: function(aWindow) {
     // display: none iframes don't have a selection controller, see bug 493658
-    if (!aWindow.innerWidth || !aWindow.innerHeight)
+    try {
+      if (!aWindow.innerWidth || !aWindow.innerHeight)
+        return null;
+    } catch (e) {
+      // If getting innerWidth or innerHeight throws, we can't get a selection
+      // controller.
       return null;
+    }
 
     // Yuck. See bug 138068.
     let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                           .getInterface(Ci.nsIWebNavigation)
                           .QueryInterface(Ci.nsIDocShell);
 
     let controller = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsISelectionDisplay)
@@ -997,8 +1018,33 @@ Finder.prototype = {
       notifyDocumentCreated: function() {},
       notifyDocumentStateChanged: function(aDirty) {}
     };
   },
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
                                          Ci.nsISupportsWeakReference])
 };
+
+function GetClipboardSearchString(aLoadContext) {
+  let searchString = "";
+  if (!Clipboard.supportsFindClipboard())
+    return searchString;
+
+  try {
+    let trans = Cc["@mozilla.org/widget/transferable;1"]
+                  .createInstance(Ci.nsITransferable);
+    trans.init(aLoadContext);
+    trans.addDataFlavor("text/unicode");
+
+    Clipboard.getData(trans, Ci.nsIClipboard.kFindClipboard);
+
+    let data = {};
+    let dataLen = {};
+    trans.getTransferData("text/unicode", data, dataLen);
+    if (data.value) {
+      data = data.value.QueryInterface(Ci.nsISupportsString);
+      searchString = data.toString();
+    }
+  } catch (ex) {}
+
+  return searchString;
+}
--- a/toolkit/modules/RemoteFinder.jsm
+++ b/toolkit/modules/RemoteFinder.jsm
@@ -1,66 +1,93 @@
 // -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// vim: set ts=2 sw=2 sts=2 et tw=80: */
 // This Source Code Form is subject to the terms of the Mozilla Public
 // License, v. 2.0. If a copy of the MPL was not distributed with this
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 this.EXPORTED_SYMBOLS = ["RemoteFinder", "RemoteFinderListener"];
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
+XPCOMUtils.defineLazyGetter(this, "GetClipboardSearchString",
+  () => Cu.import("resource://gre/modules/Finder.jsm", {}).GetClipboardSearchString
+);
+
 function RemoteFinder(browser) {
   this._browser = browser;
-  this._listeners = [];
+  this._listeners = new Set();
   this._searchString = null;
 
-  this._browser.messageManager.addMessageListener("Finder:Result", this);
-  this._browser.messageManager.addMessageListener("Finder:MatchesResult", this);
-  this._browser.messageManager.sendAsyncMessage("Finder:Initialize");
+  let mm = this._browser.messageManager;
+  mm.addMessageListener("Finder:Result", this);
+  mm.addMessageListener("Finder:MatchesResult", this);
+  mm.addMessageListener("Finder:CurrentSelectionResult",this);
+  mm.sendAsyncMessage("Finder:Initialize");
 }
 
 RemoteFinder.prototype = {
   addResultListener: function (aListener) {
-    if (this._listeners.indexOf(aListener) === -1)
-      this._listeners.push(aListener);
+    this._listeners.add(aListener);
   },
 
   removeResultListener: function (aListener) {
-    this._listeners = this._listeners.filter(l => l != aListener);
+    this._listeners.delete(aListener);
   },
 
   receiveMessage: function (aMessage) {
     // Only Finder:Result messages have the searchString field.
-    if (aMessage.name == "Finder:Result") {
-      this._searchString = aMessage.data.searchString;
+    let callback;
+    let params;
+    switch (aMessage.name) {
+      case "Finder:Result":
+        this._searchString = aMessage.data.searchString;
+        callback = "onFindResult";
+        params = [ aMessage.data ];
+        break;
+      case "Finder:MatchesResult":
+        callback = "onMatchesCountResult";
+        params = [ aMessage.data ];
+        break;
+      case "Finder:CurrentSelectionResult":
+        callback = "onCurrentSelection";
+        params = [ aMessage.data.selection, aMessage.data.initial ];
+        break;
     }
 
-    // The parent can receive either one of the two types of message.
     for (let l of this._listeners) {
-      if (aMessage.name == "Finder:Result") {
-        l.onFindResult(aMessage.data);
-      } else if (aMessage.name == "Finder:MatchesResult") {
-        l.onMatchesCountResult(aMessage.data);
-      }
+      l[callback].apply(l, params);
     }
   },
 
   get searchString() {
     return this._searchString;
   },
 
+  get clipboardSearchString() {
+    return GetClipboardSearchString(this._browser.loadContext);
+  },
+
+  setSearchStringToSelection() {
+    this._browser.messageManager.sendAsyncMessage("Finder:SetSearchStringToSelection", {});
+  },
+
   set caseSensitive(aSensitive) {
     this._browser.messageManager.sendAsyncMessage("Finder:CaseSensitive",
                                                   { caseSensitive: aSensitive });
   },
 
+  getInitialSelection: function() {
+    this._browser.messageManager.sendAsyncMessage("Finder:GetInitialSelection", {});
+  },
+
   fastFind: function (aSearchString, aLinksOnly) {
     this._browser.messageManager.sendAsyncMessage("Finder:FastFind",
                                                   { searchString: aSearchString,
                                                     linksOnly: aLinksOnly });
   },
 
   findAgain: function (aFindBackwards, aLinksOnly) {
     this._browser.messageManager.sendAsyncMessage("Finder:FindAgain",
@@ -122,16 +149,18 @@ function RemoteFinderListener(global) {
   }
 }
 
 RemoteFinderListener.prototype = {
   MESSAGES: [
     "Finder:CaseSensitive",
     "Finder:FastFind",
     "Finder:FindAgain",
+    "Finder:SetSearchStringToSelection",
+    "Finder:GetInitialSelection",
     "Finder:Highlight",
     "Finder:EnableSelection",
     "Finder:RemoveSelection",
     "Finder:FocusContent",
     "Finder:KeyPress",
     "Finder:MatchesCount"
   ],
 
@@ -148,16 +177,32 @@ RemoteFinderListener.prototype = {
   receiveMessage: function (aMessage) {
     let data = aMessage.data;
 
     switch (aMessage.name) {
       case "Finder:CaseSensitive":
         this._finder.caseSensitive = data.caseSensitive;
         break;
 
+      case "Finder:SetSearchStringToSelection": {
+        let selection = this._finder.setSearchStringToSelection();
+        this._global.sendAsyncMessage("Finder:CurrentSelectionResult",
+                                      { selection: selection,
+                                        initial: false });
+        break;
+      }
+
+      case "Finder:GetInitialSelection": {
+        let selection = this._finder.getActiveSelectionText();
+        this._global.sendAsyncMessage("Finder:CurrentSelectionResult",
+                                      { selection: selection,
+                                        initial: true });
+        break;
+      }
+
       case "Finder:FastFind":
         this._finder.fastFind(data.searchString, data.linksOnly);
         break;
 
       case "Finder:FindAgain":
         this._finder.findAgain(data.findBackwards, data.linksOnly);
         break;
 
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -281,17 +281,17 @@ function getLocale() {
  *         will not be passed to the listener
  * @param  aListener
  *         A listener implementing nextObject and noMoreObjects methods. The
  *         former will be called with the AsyncObjectCaller as the first
  *         parameter and the object as the second. noMoreObjects will be passed
  *         just the AsyncObjectCaller
  */
 function AsyncObjectCaller(aObjects, aMethod, aListener) {
-  this.objects = aObjects.slice(0);
+  this.objects = [...aObjects];
   this.method = aMethod;
   this.listener = aListener;
 
   this.callNext();
 }
 
 AsyncObjectCaller.prototype = {
   objects: null,
@@ -513,17 +513,18 @@ var gShutdownInProgress = false;
  * This is the real manager, kept here rather than in AddonManager to keep its
  * contents hidden from API users.
  */
 var AddonManagerInternal = {
   managerListeners: [],
   installListeners: [],
   addonListeners: [],
   typeListeners: [],
-  providers: [],
+  pendingProviders: new Set(),
+  providers: new Set(),
   providerShutdowns: new Map(),
   types: {},
   startupChanges: {},
   // Store telemetry details per addon provider
   telemetryDetails: {},
 
   // A read-only wrapper around the types dictionary
   typesProxy: Proxy.create({
@@ -661,33 +662,47 @@ var AddonManagerInternal = {
   /**
    * Start up a provider, and register its shutdown hook if it has one
    */
   _startProvider(aProvider, aAppChanged, aOldAppVersion, aOldPlatformVersion) {
     if (!gStarted)
       throw Components.Exception("AddonManager is not initialized",
                                  Cr.NS_ERROR_NOT_INITIALIZED);
 
+    logger.debug(`Starting provider: ${providerName(aProvider)}`);
     callProvider(aProvider, "startup", null, aAppChanged, aOldAppVersion, aOldPlatformVersion);
     if ('shutdown' in aProvider) {
       let name = providerName(aProvider);
       let AMProviderShutdown = () => {
+        // If the provider has been unregistered, it will have been removed from
+        // this.providers. If it hasn't been unregistered, then this is a normal
+        // shutdown - and we move it to this.pendingProviders incase we're
+        // running in a test that will start AddonManager again.
+        if (this.providers.has(aProvider)) {
+          this.providers.delete(aProvider);
+          this.pendingProviders.add(aProvider);
+        }
+
         return new Promise((resolve, reject) => {
             logger.debug("Calling shutdown blocker for " + name);
             resolve(aProvider.shutdown());
           })
           .catch(err => {
             logger.warn("Failure during shutdown of " + name, err);
             AddonManagerPrivate.recordException("AMI", "Async shutdown of " + name, err);
           });
       };
       logger.debug("Registering shutdown blocker for " + name);
       this.providerShutdowns.set(aProvider, AMProviderShutdown);
       AddonManager.shutdown.addBlocker(name, AMProviderShutdown);
     }
+
+    this.pendingProviders.delete(aProvider);
+    this.providers.add(aProvider);
+    logger.debug(`Provider finished startup: ${providerName(aProvider)}`);
   },
 
   /**
    * Initializes the AddonManager, loading any known providers and initializing
    * them.
    */
   startup: function AMI_startup() {
     try {
@@ -800,16 +815,17 @@ var AddonManagerInternal = {
                    getService(Ci.nsICategoryManager);
       let entries = catman.enumerateCategory(CATEGORY_PROVIDER_MODULE);
       while (entries.hasMoreElements()) {
         let entry = entries.getNext().QueryInterface(Ci.nsISupportsCString).data;
         let url = catman.getCategoryEntry(CATEGORY_PROVIDER_MODULE, entry);
 
         try {
           Components.utils.import(url, {});
+          logger.debug(`Loaded provider scope for ${url}`);
         }
         catch (e) {
           AddonManagerPrivate.recordException("AMI", "provider " + url + " load failed", e);
           logger.error("Exception loading provider " + entry + " from category \"" +
                 url + "\"", e);
         }
       }
 
@@ -817,33 +833,36 @@ var AddonManagerInternal = {
       gShutdownBarrier = new AsyncShutdown.Barrier("AddonManager: Waiting for providers to shut down.");
       AsyncShutdown.profileBeforeChange.addBlocker("AddonManager: shutting down.",
                                                    this.shutdownManager.bind(this),
                                                    {fetchState: this.shutdownState.bind(this)});
 
       // Once we start calling providers we must allow all normal methods to work.
       gStarted = true;
 
-      for (let provider of this.providers) {
+      for (let provider of this.pendingProviders) {
         this._startProvider(provider, appChanged, oldAppVersion, oldPlatformVersion);
       }
 
       // If this is a new profile just pretend that there were no changes
       if (appChanged === undefined) {
         for (let type in this.startupChanges)
           delete this.startupChanges[type];
       }
 
       gStartupComplete = true;
       this.recordTimestamp("AMI_startup_end");
     }
     catch (e) {
       logger.error("startup failed", e);
       AddonManagerPrivate.recordException("AMI", "startup failed", e);
     }
+
+    logger.debug("Completed startup sequence");
+    this.callManagerListeners("onStartup");
   },
 
   /**
    * Registers a new AddonProvider.
    *
    * @param  aProvider
    *         The provider to register
    * @param  aTypes
@@ -853,17 +872,17 @@ var AddonManagerInternal = {
     if (!aProvider || typeof aProvider != "object")
       throw Components.Exception("aProvider must be specified",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (aTypes && !Array.isArray(aTypes))
       throw Components.Exception("aTypes must be an array or null",
                                  Cr.NS_ERROR_INVALID_ARG);
 
-    this.providers.push(aProvider);
+    this.pendingProviders.add(aProvider);
 
     if (aTypes) {
       aTypes.forEach(function(aType) {
         if (!(aType.id in this.types)) {
           if (!VALID_TYPES_REGEXP.test(aType.id)) {
             logger.warn("Ignoring invalid type " + aType.id);
             return;
           }
@@ -901,23 +920,21 @@ var AddonManagerInternal = {
    *         For providers that have async shutdown methods returning Promises,
    *         the caller should wait for that Promise to resolve.
    */
   unregisterProvider: function AMI_unregisterProvider(aProvider) {
     if (!aProvider || typeof aProvider != "object")
       throw Components.Exception("aProvider must be specified",
                                  Cr.NS_ERROR_INVALID_ARG);
 
-    let pos = 0;
-    while (pos < this.providers.length) {
-      if (this.providers[pos] == aProvider)
-        this.providers.splice(pos, 1);
-      else
-        pos++;
-    }
+    this.providers.delete(aProvider);
+    // The test harness will unregister XPIProvider *after* shutdown, which is
+    // after the provider will have been moved from providers to
+    // pendingProviders.
+    this.pendingProviders.delete(aProvider);
 
     for (let type in this.types) {
       this.types[type].providers = this.types[type].providers.filter(function filterProvider(p) p != aProvider);
       if (this.types[type].providers.length == 0) {
         let oldType = this.types[type].type;
         delete this.types[type];
 
         let typeListeners = this.typeListeners.slice(0);
@@ -940,32 +957,65 @@ var AddonManagerInternal = {
         gShutdownBarrier.client.removeBlocker(shutter);
         return shutter();
       }
     }
     return undefined;
   },
 
   /**
+   * Mark a provider as safe to access via AddonManager APIs, before its
+   * startup has completed.
+   *
+   * Normally a provider isn't marked as safe until after its (synchronous)
+   * startup() method has returned. Until a provider has been marked safe,
+   * it won't be used by any of the AddonManager APIs. markProviderSafe()
+   * allows a provider to mark itself as safe during its startup; this can be
+   * useful if the provider wants to perform tasks that block startup, which
+   * happen after its required initialization tasks and therefore when the
+   * provider is in a safe state.
+   *
+   * @param aProvider Provider object to mark safe
+   */
+  markProviderSafe: function AMI_markProviderSafe(aProvider) {
+    if (!gStarted) {
+      throw Components.Exception("AddonManager is not initialized",
+                                 Cr.NS_ERROR_NOT_INITIALIZED);
+    }
+
+    if (!aProvider || typeof aProvider != "object") {
+      throw Components.Exception("aProvider must be specified",
+                                 Cr.NS_ERROR_INVALID_ARG);
+    }
+
+    if (!this.pendingProviders.has(aProvider)) {
+      return;
+    }
+
+    this.pendingProviders.delete(aProvider);
+    this.providers.add(aProvider);
+  },
+
+  /**
    * Calls a method on all registered providers if it exists and consumes any
    * thrown exception. Return values are ignored. Any parameters after the
    * method parameter are passed to the provider's method.
    * WARNING: Do not use for asynchronous calls; callProviders() does not
    * invoke callbacks if provider methods throw synchronous exceptions.
    *
    * @param  aMethod
    *         The method name to call
    * @see    callProvider
    */
   callProviders: function AMI_callProviders(aMethod, ...aArgs) {
     if (!aMethod || typeof aMethod != "string")
       throw Components.Exception("aMethod must be a non-empty string",
                                  Cr.NS_ERROR_INVALID_ARG);
 
-    let providers = this.providers.slice(0);
+    let providers = [...this.providers];
     for (let provider of providers) {
       try {
         if (aMethod in provider)
           provider[aMethod].apply(provider, aArgs);
       }
       catch (e) {
         reportProviderError(aProvider, aMethod, e);
       }
@@ -993,16 +1043,18 @@ var AddonManagerInternal = {
   /**
    * Shuts down the addon manager and all registered providers, this must clean
    * up everything in order for automated tests to fake restarts.
    * @return Promise{null} that resolves when all providers and dependent modules
    *                       have finished shutting down
    */
   shutdownManager: Task.async(function* () {
     logger.debug("shutdown");
+    this.callManagerListeners("onShutdown");
+
     gRepoShutdownState = "pending";
     gShutdownInProgress = true;
     // Clean up listeners
     Services.prefs.removeObserver(PREF_EM_CHECK_COMPATIBILITY, this);
     Services.prefs.removeObserver(PREF_EM_STRICT_COMPATIBILITY, this);
     Services.prefs.removeObserver(PREF_EM_CHECK_UPDATE_SECURITY, this);
     Services.prefs.removeObserver(PREF_EM_UPDATE_ENABLED, this);
     Services.prefs.removeObserver(PREF_EM_AUTOUPDATE_DEFAULT, this);
@@ -1555,17 +1607,33 @@ var AddonManagerInternal = {
     if (aID && typeof aID != "string")
       throw Components.Exception("aID must be a string or null",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (!aType || typeof aType != "string")
       throw Components.Exception("aType must be a non-empty string",
                                  Cr.NS_ERROR_INVALID_ARG);
 
-    this.callProviders("addonChanged", aID, aType, aPendingRestart);
+    // Temporary hack until bug 520124 lands.
+    // We can get here during synchronous startup, at which point it's
+    // considered unsafe (and therefore disallowed by AddonManager.jsm) to
+    // access providers that haven't been initialized yet. Since this is when
+    // XPIProvider is starting up, XPIProvider can't access itself via APIs
+    // going through AddonManager.jsm. Furthermore, LightweightThemeManager may
+    // not be initialized until after XPIProvider is, and therefore would also
+    // be unaccessible during XPIProvider startup. Thankfully, these are the
+    // only two uses of this API, and we know it's safe to use this API with
+    // both providers; so we have this hack to allow bypassing the normal
+    // safetey guard.
+    // The notifyAddonChanged/addonChanged API will be unneeded and therefore
+    // removed by bug 520124, so this is a temporary quick'n'dirty hack.
+    let providers = [...this.providers, ...this.pendingProviders];
+    for (let provider of providers) {
+      callProvider(provider, "addonChanged", null, aID, aType, aPendingRestart);
+    }
   },
 
   /**
    * Notifies all providers they need to update the appDisabled property for
    * their add-ons in response to an application change such as a blocklist
    * update.
    */
   updateAddonAppDisabledStates: function AMI_updateAddonAppDisabledStates() {
@@ -1666,17 +1734,17 @@ var AddonManagerInternal = {
     if (aVersion && typeof aVersion != "string")
       throw Components.Exception("aVersion must be a string or null",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (aBrowser && (!(aBrowser instanceof Ci.nsIDOMElement)))
       throw Components.Exception("aBrowser must be a nsIDOMElement or null",
                                  Cr.NS_ERROR_INVALID_ARG);
 
-    let providers = this.providers.slice(0);
+    let providers = [...this.providers];
     for (let provider of providers) {
       if (callProvider(provider, "supportsMimetype", false, aMimetype)) {
         callProviderAsync(provider, "getInstallForURL",
                           aUrl, aHash, aName, aIcons, aVersion, aBrowser,
                           function  getInstallForURL_safeCall(aInstall) {
           safeCall(aCallback, aInstall);
         });
         return;
@@ -1798,18 +1866,19 @@ var AddonManagerInternal = {
    * @return string containing the Addon ID or null
    * @see    amIAddonManager.mapURIToAddonID
    */
   mapURIToAddonID: function AMI_mapURIToAddonID(aURI) {
     if (!(aURI instanceof Ci.nsIURI)) {
       throw Components.Exception("aURI is not a nsIURI",
                                  Cr.NS_ERROR_INVALID_ARG);
     }
+
     // Try all providers
-    let providers = this.providers.slice(0);
+    let providers = [...this.providers];
     for (let provider of providers) {
       var id = callProvider(provider, "mapURIToAddonID", null, aURI);
       if (id !== null) {
         return id;
       }
     }
 
     return null;
@@ -1826,17 +1895,17 @@ var AddonManagerInternal = {
     if (!gStarted)
       throw Components.Exception("AddonManager is not initialized",
                                  Cr.NS_ERROR_NOT_INITIALIZED);
 
     if (!aMimetype || typeof aMimetype != "string")
       throw Components.Exception("aMimetype must be a non-empty string",
                                  Cr.NS_ERROR_INVALID_ARG);
 
-    let providers = this.providers.slice(0);
+    let providers = [...this.providers];
     for (let provider of providers) {
       if (callProvider(provider, "supportsMimetype", false, aMimetype) &&
           callProvider(provider, "isInstallEnabled"))
         return true;
     }
     return false;
   },
 
@@ -1858,17 +1927,17 @@ var AddonManagerInternal = {
     if (!aMimetype || typeof aMimetype != "string")
       throw Components.Exception("aMimetype must be a non-empty string",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (aURI && !(aURI instanceof Ci.nsIURI))
       throw Components.Exception("aURI must be a nsIURI or null",
                                  Cr.NS_ERROR_INVALID_ARG);
 
-    let providers = this.providers.slice(0);
+    let providers = [...this.providers];
     for (let provider of providers) {
       if (callProvider(provider, "supportsMimetype", false, aMimetype) &&
           callProvider(provider, "isInstallAllowed", null, aURI))
         return true;
     }
     return false;
   },
 
@@ -2407,16 +2476,20 @@ this.AddonManagerPrivate = {
   registerProvider: function AMP_registerProvider(aProvider, aTypes) {
     AddonManagerInternal.registerProvider(aProvider, aTypes);
   },
 
   unregisterProvider: function AMP_unregisterProvider(aProvider) {
     AddonManagerInternal.unregisterProvider(aProvider);
   },
 
+  markProviderSafe: function AMP_markProviderSafe(aProvider) {
+    AddonManagerInternal.markProviderSafe(aProvider);
+  },
+
   backgroundUpdateCheck: function AMP_backgroundUpdateCheck() {
     return AddonManagerInternal.backgroundUpdateCheck();
   },
 
   backgroundUpdateTimerHandler() {
     // Don't call through to the real update check if no checks are enabled.
     let checkHotfix = AddonManagerInternal.hotfixID &&
                       Services.prefs.getBoolPref(PREF_APP_UPDATE_ENABLED) &&
@@ -2713,16 +2786,20 @@ this.AddonManager = {
   STATE_ASK_TO_ACTIVATE: "askToActivate",
 
 #ifdef MOZ_EM_DEBUG
   get __AddonManagerInternal__() {
     return AddonManagerInternal;
   },
 #endif
 
+  get isReady() {
+    return gStartupComplete && !gShutdownInProgress;
+  },
+
   getInstallForURL: function AM_getInstallForURL(aUrl, aCallback, aMimetype,
                                                  aHash, aName, aIcons,
                                                  aVersion, aBrowser) {
     AddonManagerInternal.getInstallForURL(aUrl, aCallback, aMimetype, aHash,
                                           aName, aIcons, aVersion, aBrowser);
   },
 
   getInstallForFile: function AM_getInstallForFile(aFile, aCallback, aMimetype) {
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -2089,16 +2089,18 @@ this.XPIProvider = {
       }
 
       let flushCaches = this.checkForChanges(aAppChanged, aOldAppVersion,
                                              aOldPlatformVersion);
 
       // Changes to installed extensions may have changed which theme is selected
       this.applyThemeChange();
 
+      AddonManagerPrivate.markProviderSafe(this);
+
       if (aAppChanged === undefined) {
         // For new profiles we will never need to show the add-on selection UI
         Services.prefs.setBoolPref(PREF_SHOWN_SELECTION_UI, true);
       }
       else if (aAppChanged && !this.allAppGlobal &&
                Preferences.get(PREF_EM_SHOW_MISMATCH_UI, true)) {
         if (!Preferences.get(PREF_SHOWN_SELECTION_UI, false)) {
           // Flip a flag to indicate that we interrupted startup with an interactive prompt
--- a/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/head_addons.js
@@ -906,93 +906,103 @@ function getExpectedInstall(aAddon) {
     do_throw("Wasn't expecting events for " + id);
   if (gExpectedInstalls[id].length == 0)
     do_throw("Too many events for " + id);
   return gExpectedInstalls[id].shift();
 }
 
 const AddonListener = {
   onPropertyChanged: function(aAddon, aProperties) {
+    do_print(`Got onPropertyChanged event for ${aAddon.id}`);
     let [event, properties] = getExpectedEvent(aAddon.id);
     do_check_eq("onPropertyChanged", event);
     do_check_eq(aProperties.length, properties.length);
     properties.forEach(function(aProperty) {
       // Only test that the expected properties are listed, having additional
       // properties listed is not necessary a problem
       if (aProperties.indexOf(aProperty) == -1)
         do_throw("Did not see property change for " + aProperty);
     });
     return check_test_completed(arguments);
   },
 
   onEnabling: function(aAddon, aRequiresRestart) {
+    do_print(`Got onEnabling event for ${aAddon.id}`);
     let [event, expectedRestart] = getExpectedEvent(aAddon.id);
     do_check_eq("onEnabling", event);
     do_check_eq(aRequiresRestart, expectedRestart);
     if (expectedRestart)
       do_check_true(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_ENABLE));
     do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
     return check_test_completed(arguments);
   },
 
   onEnabled: function(aAddon) {
+    do_print(`Got onEnabled event for ${aAddon.id}`);
     let [event, expectedRestart] = getExpectedEvent(aAddon.id);
     do_check_eq("onEnabled", event);
     do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
     return check_test_completed(arguments);
   },
 
   onDisabling: function(aAddon, aRequiresRestart) {
+    do_print(`Got onDisabling event for ${aAddon.id}`);
     let [event, expectedRestart] = getExpectedEvent(aAddon.id);
     do_check_eq("onDisabling", event);
     do_check_eq(aRequiresRestart, expectedRestart);
     if (expectedRestart)
       do_check_true(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_DISABLE));
     do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
     return check_test_completed(arguments);
   },
 
   onDisabled: function(aAddon) {
+    do_print(`Got onDisabled event for ${aAddon.id}`);
     let [event, expectedRestart] = getExpectedEvent(aAddon.id);
     do_check_eq("onDisabled", event);
     do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
     return check_test_completed(arguments);
   },
 
   onInstalling: function(aAddon, aRequiresRestart) {
+    do_print(`Got onInstalling event for ${aAddon.id}`);
     let [event, expectedRestart] = getExpectedEvent(aAddon.id);
     do_check_eq("onInstalling", event);
     do_check_eq(aRequiresRestart, expectedRestart);
     if (expectedRestart)
       do_check_true(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_INSTALL));
     return check_test_completed(arguments);
   },
 
   onInstalled: function(aAddon) {
+    do_print(`Got onInstalled event for ${aAddon.id}`);
     let [event, expectedRestart] = getExpectedEvent(aAddon.id);
     do_check_eq("onInstalled", event);
     return check_test_completed(arguments);
   },
 
   onUninstalling: function(aAddon, aRequiresRestart) {
+    do_print(`Got onUninstalling event for ${aAddon.id}`);
     let [event, expectedRestart] = getExpectedEvent(aAddon.id);
     do_check_eq("onUninstalling", event);
     do_check_eq(aRequiresRestart, expectedRestart);
     if (expectedRestart)
       do_check_true(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_UNINSTALL));
     return check_test_completed(arguments);
   },
 
   onUninstalled: function(aAddon) {
+    do_print(`Got onUninstalled event for ${aAddon.id}`);
     let [event, expectedRestart] = getExpectedEvent(aAddon.id);
     do_check_eq("onUninstalled", event);
     return check_test_completed(arguments);
   },
 
   onOperationCancelled: function(aAddon) {
+    do_print(`Got onOperationCancelled event for ${aAddon.id}`);
     let [event, expectedRestart] = getExpectedEvent(aAddon.id);
     do_check_eq("onOperationCancelled", event);
     return check_test_completed(arguments);
   }
 };
 
 const InstallListener = {
   onNewInstall: function(install) {
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_isReady.js
@@ -0,0 +1,49 @@
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+function run_test() {
+  run_next_test();
+}
+
+add_task(function* () {
+  equal(AddonManager.isReady, false, "isReady should be false before startup");
+
+  let gotStartupEvent = false;
+  let gotShutdownEvent = false;
+  let listener = {
+    onStartup() {
+      gotStartupEvent = true;
+    },
+    onShutdown() {
+      gotShutdownEvent = true;
+    },
+  };
+  AddonManager.addManagerListener(listener);
+
+  do_print("Starting manager...");
+  startupManager();
+  equal(AddonManager.isReady, true, "isReady should be true after startup");
+  equal(gotStartupEvent, true, "Should have seen onStartup event after startup");
+  equal(gotShutdownEvent, false, "Should not have seen onShutdown event before shutdown");
+
+  gotStartupEvent = false;
+  gotShutdownEvent = false;
+
+  do_print("Shutting down manager...");
+  let shutdownPromise = promiseShutdownManager();
+  equal(AddonManager.isReady, false, "isReady should be false when shutdown commences");
+  yield shutdownPromise;
+
+  equal(AddonManager.isReady, false, "isReady should be false after shutdown");
+  equal(gotStartupEvent, false, "Should not have seen onStartup event after shutdown");
+  equal(gotShutdownEvent, true, "Should have seen onShutdown event after shutdown");
+
+  AddonManager.addManagerListener(listener);
+  gotStartupEvent = false;
+  gotShutdownEvent = false;
+
+  do_print("Starting manager again...");
+  startupManager();
+  equal(AddonManager.isReady, true, "isReady should be true after repeat startup");
+  equal(gotStartupEvent, true, "Should have seen onStartup event after repeat startup");
+  equal(gotShutdownEvent, false, "Should not have seen onShutdown event before shutdown, following repeat startup");
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_provider_markSafe.js
@@ -0,0 +1,47 @@
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+let startupOrder = [];
+
+function mockAddonProvider(name) {
+  let mockProvider = {
+    markSafe: false,
+    apiAccessed: false,
+
+    startup() {
+      if (this.markSafe)
+        AddonManagerPrivate.markProviderSafe(this);
+
+      let uri = Services.io.newURI("beard://long", null, null);
+      AddonManager.isInstallEnabled("made-up-mimetype");
+    },
+    supportsMimetype(mimetype) {
+      this.apiAccessed = true;
+      return false;
+    },
+
+    get name() name,
+  };
+
+  return mockProvider;
+};
+
+function run_test() {
+  run_next_test();
+}
+
+add_task(function* testMarkSafe() {
+  do_print("Starting with provider normally");
+  let provider = mockAddonProvider("Mock1");
+  AddonManagerPrivate.registerProvider(provider);
+  startupManager();
+  ok(!provider.apiAccessed, "Provider API should not have been accessed");
+  AddonManagerPrivate.unregisterProvider(provider);
+  yield promiseShutdownManager();
+
+  do_print("Starting with provider that marks itself safe");
+  provider.apiAccessed = false;
+  provider.markSafe = true;
+  AddonManagerPrivate.registerProvider(provider);
+  startupManager();
+  ok(provider.apiAccessed, "Provider API should have been accessed");
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_provider_unsafe_access_shutdown.js
@@ -0,0 +1,61 @@
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+let shutdownOrder = [];
+
+function mockAddonProvider(name) {
+  let mockProvider = {
+    hasShutdown: false,
+    unsafeAccess: false,
+
+    shutdownCallback: null,
+
+    startup() { },
+    shutdown() {
+      this.hasShutdown = true;
+      shutdownOrder.push(this.name);
+      if (this.shutdownCallback)
+        return this.shutdownCallback();
+    },
+    getAddonByID(id, callback) {
+      if (this.hasShutdown) {
+        unsafeAccess = true;
+      }
+      callback(null);
+    },
+
+    get name() name,
+  };
+
+  return mockProvider;
+};
+
+function run_test() {
+  run_next_test();
+}
+
+add_task(function* unsafeProviderShutdown() {
+  let firstProvider = mockAddonProvider("Mock1");
+  AddonManagerPrivate.registerProvider(firstProvider);
+  let secondProvider = mockAddonProvider("Mock2");
+  AddonManagerPrivate.registerProvider(secondProvider);
+
+  startupManager();
+
+  let shutdownPromise = null;
+  yield new Promise(resolve => {
+    secondProvider.shutdownCallback = function() {
+      return new Promise(shutdownResolve => {
+        AddonManager.getAddonByID("does-not-exist", () => {
+          shutdownResolve();
+          resolve();
+        });
+      });
+    };
+
+    shutdownPromise = promiseShutdownManager();
+  });
+  yield shutdownPromise;
+
+  equal(shutdownOrder.join(","), ["Mock1", "Mock2"].join(","), "Mock providers should have shutdown in expected order");
+  ok(!firstProvider.unsafeAccess, "First registered mock provider should not have been accessed unsafely");
+});
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_provider_unsafe_access_startup.js
@@ -0,0 +1,53 @@
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+let startupOrder = [];
+
+function mockAddonProvider(name) {
+  let mockProvider = {
+    hasStarted: false,
+    unsafeAccess: false,
+
+    startupCallback: null,
+
+    startup() {
+      this.hasStarted = true;
+      startupOrder.push(this.name);
+      if (this.startupCallback)
+        this.startupCallback();
+    },
+    getAddonByID(id, callback) {
+      if (!this.hasStarted) {
+        unsafeAccess = true;
+      }
+      callback(null);
+    },
+
+    get name() name,
+  };
+
+  return mockProvider;
+};
+
+function run_test() {
+  run_next_test();
+}
+
+add_task(function* unsafeProviderStartup() {
+  let secondProvider = null;
+
+  yield new Promise(resolve => {
+    let firstProvider = mockAddonProvider("Mock1");
+    firstProvider.startupCallback = function() {
+      AddonManager.getAddonByID("does-not-exist", resolve);
+    };
+    AddonManagerPrivate.registerProvider(firstProvider);
+
+    secondProvider = mockAddonProvider("Mock2");
+    AddonManagerPrivate.registerProvider(secondProvider);
+
+    startupManager();
+  });
+
+  equal(startupOrder.join(","), ["Mock1", "Mock2"].join(","), "Mock providers should have hasStarted in expected order");
+  ok(!secondProvider.unsafeAccess, "Second registered mock provider should not have been accessed unsafely");
+});
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -6,18 +6,22 @@ firefox-appdir = browser
 dupe-manifest =
 support-files =
   data/**
   xpcshell-shared.ini
 
 [test_addon_path_service.js]
 [test_asyncBlocklistLoad.js]
 [test_DeferredSave.js]
+[test_isReady.js]
 [test_metadata_update.js]
 [test_openh264.js]
 run-if = appname == "firefox"
 [test_pluginInfoURL.js]
+[test_provider_markSafe.js]
 [test_provider_shutdown.js]
+[test_provider_unsafe_access_shutdown.js]
+[test_provider_unsafe_access_startup.js]
 [test_shutdown.js]
 [test_XPIcancel.js]
 [test_XPIStates.js]
 
 [include:xpcshell-shared.ini]
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -150,16 +150,17 @@
 #endif
 #endif
 
 #include "base/process_util.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
+#include "mozilla/DebugOnly.h"
 #include "mozilla/HoldDropJSObjects.h"
 /* This must occur *after* base/process_util.h to avoid typedefs conflicts. */
 #include "mozilla/LinkedList.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/SegmentedVector.h"
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsCycleCollectionNoteRootCallback.h"
@@ -1252,31 +1253,31 @@ class nsCycleCollector : public nsIMemor
 
   CycleCollectedJSRuntime* mJSRuntime;
 
   ccPhase mIncrementalPhase;
   CCGraph mGraph;
   nsAutoPtr<CCGraphBuilder> mBuilder;
   nsCOMPtr<nsICycleCollectorListener> mListener;
 
-  nsIThread* mThread;
+  DebugOnly<void*> mThread;
 
   nsCycleCollectorParams mParams;
 
   uint32_t mWhiteNodeCount;
 
   CC_BeforeUnlinkCallback mBeforeUnlinkCB;
   CC_ForgetSkippableCallback mForgetSkippableCB;
 
   nsPurpleBuffer mPurpleBuf;
 
   uint32_t mUnmergedNeeded;
   uint32_t mMergedInARow;
 
-  JSPurpleBuffer* mJSPurpleBuffer;
+  nsRefPtr<JSPurpleBuffer> mJSPurpleBuffer;
 
 private:
   virtual ~nsCycleCollector();
 
 public:
   nsCycleCollector();
 
   void RegisterJSRuntime(CycleCollectedJSRuntime* aJSRuntime);
@@ -2036,17 +2037,17 @@ private:
   CCGraph& mGraph;
   CycleCollectorResults& mResults;
   NodePool::Builder mNodeBuilder;
   EdgePool::Builder mEdgeBuilder;
   PtrInfo* mCurrPi;
   nsCycleCollectionParticipant* mJSParticipant;
   nsCycleCollectionParticipant* mJSZoneParticipant;
   nsCString mNextEdgeName;
-  nsICycleCollectorListener* mListener;
+  nsCOMPtr<nsICycleCollectorListener> mListener;
   bool mMergeZones;
   bool mRanOutOfMemory;
   nsAutoPtr<NodePool::Enumerator> mCurrNode;
 
 public:
   CCGraphBuilder(CCGraph& aGraph,
                  CycleCollectorResults& aResults,
                  CycleCollectedJSRuntime* aJSRuntime,
@@ -2573,39 +2574,37 @@ class JSPurpleBuffer
 {
   ~JSPurpleBuffer()
   {
     MOZ_ASSERT(mValues.IsEmpty());
     MOZ_ASSERT(mObjects.IsEmpty());
   }
 
 public:
-  explicit JSPurpleBuffer(JSPurpleBuffer*& aReferenceToThis)
+  explicit JSPurpleBuffer(nsRefPtr<JSPurpleBuffer>& aReferenceToThis)
     : mReferenceToThis(aReferenceToThis)
     , mValues(kSegmentSize)
     , mObjects(kSegmentSize)
   {
     mReferenceToThis = this;
-    NS_ADDREF_THIS();
     mozilla::HoldJSObjects(this);
   }
 
   void Destroy()
   {
     mReferenceToThis = nullptr;
     mValues.Clear();
     mObjects.Clear();
     mozilla::DropJSObjects(this);
-    NS_RELEASE_THIS();
   }
 
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(JSPurpleBuffer)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(JSPurpleBuffer)
 
-  JSPurpleBuffer*& mReferenceToThis;
+  nsRefPtr<JSPurpleBuffer>& mReferenceToThis;
 
   // These are raw pointers instead of Heap<T> because we only need Heap<T> for
   // pointers which may point into the nursery. The purple buffer never contains
   // pointers to the nursery because nursery gcthings can never be gray and only
   // gray things can be inserted into the purple buffer.
   static const size_t kSegmentSize = 512;
   SegmentedVector<JS::Value, kSegmentSize, InfallibleAllocPolicy> mValues;
   SegmentedVector<JSObject*, kSegmentSize, InfallibleAllocPolicy> mObjects;
@@ -2737,17 +2736,17 @@ public:
   }
 
   virtual void Trace(JS::Heap<JSFunction*>* aFunction, const char* aName,
                      void* aClosure) const
   {
   }
 
 private:
-  nsCycleCollector* mCollector;
+  nsRefPtr<nsCycleCollector> mCollector;
   ObjectsVector mObjects;
 };
 
 class RemoveSkippableVisitor : public SnowWhiteKiller
 {
 public:
   RemoveSkippableVisitor(nsCycleCollector* aCollector,
                          uint32_t aMaxCount, bool aRemoveChildlessNodes,
@@ -3001,17 +3000,17 @@ public:
     if (pi->mColor == black) {
       return;
     }
     FloodBlackNode(mCount, mFailed, pi);
   }
 
 private:
   CCGraph& mGraph;
-  nsICycleCollectorListener* mListener;
+  nsCOMPtr<nsICycleCollectorListener> mListener;
   uint32_t& mCount;
   bool& mFailed;
 };
 
 // Objects that have been stored somewhere since the start of incremental graph building must
 // be treated as live for this cycle collection, because we may not have accurate information
 // about who holds references to them.
 void
@@ -3395,18 +3394,17 @@ nsCycleCollector::nsCycleCollector() :
   mScanInProgress(false),
   mJSRuntime(nullptr),
   mIncrementalPhase(IdlePhase),
   mThread(NS_GetCurrentThread()),
   mWhiteNodeCount(0),
   mBeforeUnlinkCB(nullptr),
   mForgetSkippableCB(nullptr),
   mUnmergedNeeded(0),
-  mMergedInARow(0),
-  mJSPurpleBuffer(nullptr)
+  mMergedInARow(0)
 {
 }
 
 nsCycleCollector::~nsCycleCollector()
 {
   UnregisterWeakMemoryReporter(this);
 }
 
deleted file mode 100644
--- a/xpcom/tests/CvtURL.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-#include <stdio.h>
-#include "nscore.h"
-#include "nsIConverterInputStream.h"
-#include "nsIURL.h"
-#include "nsNetUtil.h"
-#include "nsCRT.h"
-#include "nsString.h"
-#include "prprf.